Home > OS >  Foreach generic as specific type
Foreach generic as specific type

Time:08-14

Cannot resolve and find solution. I want to create easy-extend structure and save data to ScriptableObjects. For example I create 2 AudioSettings (assets) based on SettingsSO: DefaultSettings, CurrentSettings, then I'm going to set up Default on UnityEditor and store user's settings in Current, if Current == null then Current = CreateInstance<Default>. So any setting group has 2 refs. In future I want add another setting groups in SettingManager and be able to add all of them in some list or dictionary and then foreach do something common. For example load default settings.

SettingsManager.cs

public class SettingsManager
{
    [SerializeField] private SettingGroup<VideoSettings> _videoSettings;
    [SerializeField] private SettingGroup<AudioSettings> _audioSettings;

    private Dictionary<Type, SettingGroup> _settingDictionary;

    public void Init()
    {
        _settingDictionary = new Dictionary<Type, SettingGroup>
        {
            { _audioSettings.GetType(), _audioSettings },
            { _videoSettings.GetType(), _videoSettings }
        };
    }

    private void LoadDefaultSetting()
    {
        foreach (var pair in _settingDictionary)
        {
            //var eachSettingGroup = (pair.Key)pair.Value;              //Error Cannot resolve symbol 'pair'
            //var eachSettingGroup = pair.Value as pair.Key;            //Error Cannot resolve symbol 'pair'
            //var eachSettingGroup = (typeof(pair.Key))pair.Value;    //Error Cannot resolve symbol 'pair'
            var eachSettingGroup = I need settings.Default HelpMeGuys!! (;
            eachSettingGroup.Init();
        }
    }
}

SettingGroup.cs

public class SettingGroup<T> : SettingGroup where T : SettingsSO
    {
        public T Default => _default;
        public T Current => _current;
        public Type SettingsType => typeof(T);


        [SerializeField] private T _default;
        [SerializeField] private T _current;
        private T _lastSaved;

        public void Init()
        {
            if (_current == null)
            {
                Debug.LogWarning("Current settings reference not found. Default settings using instead without saving.");
                _current = ScriptableObject.CreateInstance<T>();
                _default.CopyDataTo(_current);
            }

            _lastSaved = ScriptableObject.CreateInstance<T>();
            _current.CopyDataTo(_lastSaved);
        }
    }

AudioSettings.cs

public class AudioSettings : SettingsSO
{
   public bool MusicEnabled;
   public bool SoundEnabled;
   public float MusicVolume;
   public float SoundVolume;
}

SettingsSO.cs

public abstract class SettingsSO : ScriptableObject
{
    public string Description;
}

CodePudding user response:

It's a little unclear what capabilities you need for your SettingGroup<T> and if there's anything important in SettingGroup, which we can't see. But you can solve the immediate problem by introducing an interface with a covariant generic type:

// Notice the 'out' keyword    vvv
public interface ISettingGroup<out T> where T : SettingsSO
{
    public T Default { get; }
    public T Current { get; }
    public Type SettingsType { get; }
    
    public void Init();
}

Then implement that interface in your SettingGroup:

public class SettingGroup<T> : ISettingGroup<T> where T : SettingsSO
{ /* ... */ }

In your SettingsManager we can now change the type of the dictionary's values:

public class SettingsManager
{
    [SerializeField] private SettingGroup<VideoSettings> _videoSettings;
    [SerializeField] private SettingGroup<AudioSettings> _audioSettings;

    // Now uses the interface vvvvvvvvvvvvvvvvvvvvvvv
    private Dictionary<Type,  ISettingGroup<SettingsSO>> _settingDictionary;

    public void Init()
    {
        _settingDictionary = new Dictionary<Type, SettingGroup>
        {
            { _audioSettings.GetType(), _audioSettings },
            { _videoSettings.GetType(), _videoSettings }
        };
    }

    private void LoadDefaultSetting()
    {
        foreach (var pair in _settingDictionary)
        {
            // No need for any type casting now...
            var settingGroup = pair.Value;
            var settingsDefault = settingGroup.Default;
            settingGroup.Init();
        }
    }
}

CodePudding user response:

I found solution, I forget that those setting groups will be use same methods and I can replace these methods to interface. And I don't need to know which of type this is setting group.

SettingGroup.cs

    public interface SettingGroup
    {
        void Init();
    }

    [Serializable]
    public class SettingGroup<T> : SettingGroup where T : SettingsSO
    {
        [SerializeField] private T _default;
        [SerializeField] private T _saved;
        [SerializeField] private T _current;

        public T CurrentSettings  => _current;

        public void Init()
        {
            if (_saved == null)
            {
                Debug.LogWarning(
                    "Current settings reference not found. Default settings using instead without saving.");
                _saved = ScriptableObject.CreateInstance<T>();
                _saved.CopyDataFrom(_default);
            }

            _current = ScriptableObject.CreateInstance<T>();
            _current.CopyDataFrom(_saved);
        }
    }
}

SettingsManager.cs

    [Serializable]
    public class SettingsManager
    {
        [SerializeField] private SettingGroup<VideoSettings> _videoSettings;
        [SerializeField] private SettingGroup<AudioSettings> _audioSettings;

        public VideoSettings Video => _videoSettings.CurrentSettings;
        public AudioSettings Audio => _audioSettings.CurrentSettings;

        private List<SettingGroup> _settingList;

        public void Init()
        {
            //TODO don't forget to add all above SettingsGroups to list 
            _settingList = new List<SettingGroup>
            {
                _audioSettings, 
                _videoSettings
            };

            foreach (var settingGroup in _settingList)
            {
                settingGroup.Init();
            }
        }
   }
  • Related