If two classes (Bar1 and Bar2) inherit from the same base class (Foo)

public abstract class Foo
    {
        public abstract void Spawn(Settings settings);
    }

    public class Bar1 : Foo
    {
        float a;
        int b;

        public override void Spawn(Bar1Settings settings)
        {
            if (settings == null)
            {
                settings = new Bar1Settings();
            }

            a = settings.a;
            b = settings.b;
        }
    }

    public class Bar2 : Foo
    {
        bool a;
        int b;
         
        public override void Spawn(Bar2Settings settings)
        {
            if (settings == null)
            {
                settings = new Bar2Settings();
            }

            a = settings.a;
            b = settings.b;
        }
    }

and all settings specific to each class inherit from a base class Settings

public abstract class Settings { }

    public class Bar1Settings : Settings
    {
        public float a = 1.0f;
        public int b = 5;
    }

    public class Bar2Settings : Settings
    {
        public bool a = true;
        public int b = 76;
    }

How can I load the correct settings for each class when they are stored in a list of settings?

List<Settings> settingsList = new List<Settings>();

            for (int i = 0; i < settingsList.Count; ++i)
            {
                // Spawn correct Bar class from settings
            }

Is this going to be possible, or should I store each type of derived settings class in a separate settings list and then spawn from that?

The abstract Settings class is still required for overriding the abstract Foo.Spawn(Settings settings) correctly though, is it not? As it currently stands, it doesn't work though as the abstract will only accept the base Settings class not the derived classes?!

Recommended Answers

All 15 Replies

public abstract class Foo
{
    public abstract void Spawn(Settings settings);
}

public class Bar1 : Foo
{
    float a;
    int b;

    public override void Spawn(Settings _settings)
    {
        if (_settings == null)
        {
            _settings = new Bar1Settings();
        }
        Bar1Settings settings = _settings as Bar1Settings ;
        a = settings.a;
        b = settings.b;
    }
}

public class Bar2 : Foo
{
    bool a;
    int b;

    public override void Spawn(Settings _settings)
    {
        if (_settings == null)
        {
            _settings = new Bar2Settings();
        }
        Bar2Settings settings = _settings as Bar2Settings;
        a = settings.a;
        b = settings.b;
    }
}

adatapost's post is only half the answer. You will also need to test the Settings type to be able to create the correct the bar class.

List<Settings> settingsList = new List<Settings>();

foo newBar; // local holding variable

for (int i = 0; i < settingsList.Count; ++i)
{
    // Spawn correct Bar class from settings
    if (settingsList[i] is Bar1Settings)
    {
        newBar = new Bar1();
    }
    else if (settingsList[i] is Bar2Settings)
    {
        newBar = new Bar2();
    }
    if (newBar != null)
    {
        newBar.Spawn(settingsList[i]);
        // do something with newBar e.g. put in to a collection 
    }
}

Also, a slight adjustment to adatapost's Spawn code to stop other Settings types from causing errors.

public override void Spawn(Settings _settings)
{
    Bar1Settings settings = _settings as Bar1Settings;
    if (settings == null)
    {
        settings = new Bar1Settings();
    }
    a = settings.a;
    b = settings.b;
}
commented: Well written and concise. +1

Thank you both. That has made things very clear for me.

For saving I have come up with the following solution. It was easier to grasp than loading and works with everything I've thrown at it.

public class Bar1 : Foo
    {
        float a;
        int b;

        public override Settings SaveToSettings()
        {
            Bar1Settings settings = new Bar1Settings();
            settings.a = a;
            settings.b = b;
            return settings;
        }

        public override void Spawn(Settings _settings)
        {
            Bar1Settings settings = _settings as Bar1Settings;
            
            if (settings == null)
            {
                settings = new Bar1Settings();
            }

            a = settings.a;
            b = settings.b;
        }
    }

    public class Bar2 : Foo
    {
        bool a;
        int b;

        public override Settings SaveToSettings()
        {
            Bar2Settings settings = new Bar2Settings();
            settings.a = a;
            settings.b = b;
            return settings;
        }

        public override void Spawn(Settings _settings)
        {
            Bar2Settings settings = _settings as Bar2Settings;
            
            if (settings == null)
            {
                settings = new Bar2Settings();
            }

            a = settings.a;
            b = settings.b;
        }
    }

I had a idea how you might do things slightly differently.
You could change your Settings object to 'create' the Bar (foo) objects as an alternative method to the Spawn.
This then will remove the if (settingsList[i] is Bar1Settings) i.e. you can just do newBar = Settings.CreateBar(); And setting now becomes

public abstract class Settings 
{
    public abstract Foo CreateBar();
}

That's a good idea but shouldn't the Settings class be just a retainer for settings? I would find it a little confusing to create objects from it.

Also, if I wanted to create different classes of objects that have their settings derived from the Settings class but they themselves do not derive from Foo, then that method wouldn't work?

Just a few thoughts but please correct me if I'm wrong.

Also, if I wanted to create different classes of objects that have their settings derived from the Settings class but they themselves do not derive from Foo, then that method wouldn't work?

I agree, although returning an object would get arround this.

I would find it a little confusing to create objects from it.

It is usually best to go with whatever you find comfortable and what is most appropriate from your understanding of the application, both now and in the future.

Could you please give an example of what you intended with your idea?

In my case all objects are Spawned because they are already loaded in a resource pool with default settings. This way they must have their parameters overwritten by the new Settings when Spawned.

How could this work with your automated Settings class idea? It would be useful to avoid using the "as" and "is" keyword each time Settings are loaded.

It comes from persisting objects to storage.
The Component and Control classes of a form are not serializable, but I sometimes use a 'Settings' object to store the parameters I need and create them when restoring the data from file.
There are ways to do this using type converters but that can be overkill sometimes.

Thanks Nick, I've been playing around with your ideas/suggestions and have a few questions based on my experience.

I agree, although returning an object would get arround this.

Let's say the base class returns an object like you suggested.

public abstract class Settings 
{
    public abstract object CreateBar();
}
public override Foo CreateBar()
        {

        }

Overriding classes must return objects and not Foo otherwise I get the following error.

return type must be 'object' to match overridden member

If this is the case then how do I know what class is returned from CreateBar() because the following line will not work

Bar newBar = Settings.CreateBar();

due to the following error.

Cannot implicitly convert type 'object' to 'Bar'.

I assume that I can get around this error by casting but how can the computer know exactly what class to correctly cast to based on the returned class?

Or maybe I misunderstood you?

Since you expect a Bar then you can cast.
Or to prevent errors should the create return the wrong type (or null) use an 'as'.

Bar newBar = Settings.CreateBar() as Bar;

Then test newBar for none null before using it.
There are always pro's and con's with every method you adopt.
It's ultimatley up to your personal preferences how you code it.

How does this work for the derived Bar1, Bar2 classes? If I were to create a Bar class then I wouldn't have access to Bar1 or Bar2 class properties.

Is there a way to know which derived class the settings belonged to so that class is created?

Don't you derived a Settings class for each BarN class.
Bar1Settings knows it needs to create a Bar1 instance.

That's correct but it doesn't avoid the casting required in each derived class as the base type of Settings has to be overridden using Settings for each method. Therefore each class still has to cast back from Settings to Bar1Settings or Bar2Settings.

Is there no way of avoiding this?

I don't think there is. As I said before

There are always pro's and con's with every method you adopt.
It's ultimatley up to your personal preferences how you code it.

That's correct but it doesn't avoid the casting required in each derived class as the base type of Settings has to be overridden using Settings for each method. Therefore each class still has to cast back from Settings to Bar1Settings or Bar2Settings.

Is there no way of avoiding this?

I was thinking about this over the weekend and I wondered if Generics could help.
I came up with this. Not a single cast or as anywhere.
However, whenever you derive multiple objects from a base class it is almost inevitable that you will need to cast at some point.

public abstract class Settings
    {
        public Foo Spawn()
        {
            return this.FooSpawn();
        }
        protected abstract Foo FooSpawn();
    }

    public abstract class Settings<T> : Settings where T : Foo
    {
        protected override Foo FooSpawn()
        {
            return this.DoSpawn();
        }

        protected abstract T DoSpawn();
    }

    public abstract class Foo
    {
        public Settings Save()
        {
            return this.FooSave();
        }
        protected abstract Settings FooSave();
    }

    public abstract class Foo<T> : Foo where T : Settings
    {
        protected override Settings FooSave()
        {
            return this.DoSave();
        }

        protected abstract T DoSave();
    }

    public class Bar1Settings : Settings<Bar1>
    {
        public float a = 1.0f;
        public int b = 5;

        protected override Bar1 DoSpawn()
        {
            Bar1 value = new Bar1();
            value.a = a;
            value.b = b;
            return value;
        }
    }

    public class Bar2Settings : Settings<Bar2>
    {
        public bool a = true;
        public int b = 76;

        protected override Bar2 DoSpawn()
        {
            Bar2 value = new Bar2();
            value.a = a;
            value.b = b;
            return value;
        }
    }

    public class Bar1 : Foo<Bar1Settings>
    {
        public float a;
        public int b;

        protected override Bar1Settings DoSave()
        {
            Bar1Settings settings = new Bar1Settings();
            settings.a = a;
            settings.b = b;
            return settings;
        }
    }

    public class Bar2 : Foo<Bar2Settings>
    {
        public bool a;
        public int b;

        protected override Bar2Settings DoSave()
        {
            Bar2Settings settings = new Bar2Settings();
            settings.a = a;
            settings.b = b;
            return settings;
        }
    }

    public class TestFooSettings
    {
        List<Foo> fooList = new List<Foo>();
        List<Settings> settingList = new List<Settings>();
        List<Settings> settingList2 = new List<Settings>();

        public void RunTest()
        {
            // add default settings
            settingList.Add(new Bar1Settings());
            settingList.Add(new Bar2Settings());
            // add different settings
            Bar1Settings b1 = new Bar1Settings();
            b1.a = 25.4f;
            b1.b = 22;
            settingList.Add(b1);
            Bar2Settings b2 = new Bar2Settings();
            b2.a = false;
            b2.b = 44;
            settingList.Add(b2);

            // turn settings list in to foo list
            foreach (Settings val in settingList)
            {
                fooList.Add(val.Spawn());
            }

            // turn foo list back to new settings list
            foreach (Foo foo in fooList)
            {
                settingList2.Add(foo.Save());
            }
        }
    }
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.