Conceptual help Different classes in a single List<>?

Rhodan

Active member
Joined
Apr 7, 2015
Messages
41
Programming Experience
10+
I'm working on a project using a beaglebone/mono and I want to display inputs, outputs, alarms, etc all on a single datagridview. The problem is that the classes for these are quite different. I could just use separate classes and manually fill the DGV but I was thinking there should be a way to use an "info" class to contain all the other classes then use the "Info" class for filling controls.

About the only way i can think of to do this is to use a member of type "Object" to point to the actual classes so that the info class entries are all identical as far as the List<> and control are concerned. I'd then have to figure out which class "Object" is and extract the appropriate information when needed.

Is this a good approach or can someone suggest something more elegant. Don't need code necessarily, just a nudge in the right direction.

Thanks!

P.S. Using VS2013 or 2015
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
3,792
Location
Sydney, Australia
Programming Experience
10+
If you really want to go down that track then you could define a wrapper class that knew about all the other types and could extract their data and expose it through a set of common properties. You could then create a list of that type with each item wrapping an instance of one of the original classes. You could then simply bind your list to the grid. Your class might look something like this:
    internal class FirstItemType
    {
        public string SomeText { get; set; }
        public int SomeNumber { get; set; }
    }

    internal class SecondItemType
    {
        public string MyText { get; set; }
        public int MyNumber { get; set; }
    }

    internal class ItemWrapper
    {
        private FirstItemType firstInstance;
        private SecondItemType secondInstance;

        public ItemWrapper(FirstItemType firstInstance)
        {
            this.firstInstance = firstInstance;
        }

        public ItemWrapper(SecondItemType secondInstance)
        {
            this.secondInstance = secondInstance;
        }

        public string Text
        {
            get
            {
                string value = null;

                if (firstInstance != null)
                {
                    value = firstInstance.SomeText;
                }
                else if (secondInstance != null)
                {
                    value = secondInstance.MyText;
                }

                return value;
            }
            set
            {
                if (firstInstance != null)
                {
                    firstInstance.SomeText = value;
                }
                else if (secondInstance != null)
                {
                    secondInstance.MyText = value;
                }
            }
        }

        public int Number
        {
            get
            {
                int value = 0;

                if (firstInstance != null)
                {
                    value = firstInstance.SomeNumber;
                }
                else if (secondInstance != null)
                {
                    value = secondInstance.MyNumber;
                }

                return value;
            }
            set
            {
                if (firstInstance != null)
                {
                    firstInstance.SomeNumber = value;
                }
                else if (secondInstance != null)
                {
                    secondInstance.MyNumber = value;
                }
            }
        }
    }
It's somewhat laborious to write but, once you have, it makes using the disparate types quite easy.
 

Rhodan

Active member
Joined
Apr 7, 2015
Messages
41
Programming Experience
10+
That wasn't quite what I was trying to describe ( I think). The wrapper class instance would only have one wrapped class instance assigned to it at a time and I think you were aiming at two in the example(?).

Oh wait... Now I see it. The public methods are invoked by the type of class being assigned to it (I haven't played with multiple public methods this way yet).

I wonder if I could use a switch block based on the wrapped class type rather than lots of if-thens.

Thanks very much for the explanation. It will really help!
 

JohnH

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
1,183
Location
Norway
Programming Experience
10+
If you can modify the different types you can define an interface and implement that in the types, then use list<Interfacetype> as datasource. You can add any object that implements the interface to this list. DataGridView will use the interface properties to display data from the different objects.
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
3,792
Location
Sydney, Australia
Programming Experience
10+
I wonder if I could use a switch block based on the wrapped class type rather than lots of if-thens.

No, you couldn't. Unlike Select Case in VB.NET, `switch` in C# requires constant values in each `case`. That basically means value types or strings.
 

StoneCodeMonkey

New member
Joined
Jul 7, 2016
Messages
3
Programming Experience
10+
Abstract

Define an abstract class with abstract properties for publishing the data in a consistent manner. Then implement the abstract class in each of your existing classes.

C#:
    [COLOR=#569cd6][B]abstract[/B][/COLOR] [COLOR=#569cd6][B]class[/B][/COLOR] [COLOR=#4ec9b0]AbstractPublisher[/COLOR]
    {
        [COLOR=#569cd6][B]public[/B][/COLOR] [COLOR=#569cd6][B]abstract[/B][/COLOR] [COLOR=#569cd6][B]string[/B][/COLOR] [COLOR=white]MyData[/COLOR] { [COLOR=#569cd6][B]get[/B][/COLOR]; [COLOR=#569cd6][B]set[/B][/COLOR]; }    
    }
 
    [COLOR=#569cd6][B]class[/B][/COLOR] [COLOR=#4ec9b0]MyClass[/COLOR]:[COLOR=#4ec9b0]AbstractPublisher[/COLOR]
    {
 
        [COLOR=#569cd6][B]public[/B][/COLOR] [COLOR=#569cd6][B]override[/B][/COLOR] [COLOR=#569cd6][B]string[/B][/COLOR] [COLOR=white]MyData[/COLOR]
        {
            [COLOR=#569cd6][B]get[/B][/COLOR]
            {
                [COLOR=#569cd6][B]throw[/B][/COLOR] [COLOR=#569cd6][B]new[/B][/COLOR] [COLOR=#4ec9b0]NotImplementedException[/COLOR]();
            }
            [COLOR=#569cd6][B]set[/B][/COLOR]
            {
                [COLOR=#569cd6][B]throw[/B][/COLOR] [COLOR=#569cd6][B]new[/B][/COLOR] [COLOR=#4ec9b0]NotImplementedException[/COLOR]();
            }
        }
    }

Override the abstract properties and use them to publish the data.
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
3,792
Location
Sydney, Australia
Programming Experience
10+
Define an abstract class with abstract properties for publishing the data in a consistent manner. Then implement the abstract class in each of your existing classes.

C#:
    [COLOR=#569cd6][B]abstract[/B][/COLOR] [COLOR=#569cd6][B]class[/B][/COLOR] [COLOR=#4ec9b0]AbstractPublisher[/COLOR]
    {
        [COLOR=#569cd6][B]public[/B][/COLOR] [COLOR=#569cd6][B]abstract[/B][/COLOR] [COLOR=#569cd6][B]string[/B][/COLOR] [COLOR=white]MyData[/COLOR] { [COLOR=#569cd6][B]get[/B][/COLOR]; [COLOR=#569cd6][B]set[/B][/COLOR]; }    
    }
 
    [COLOR=#569cd6][B]class[/B][/COLOR] [COLOR=#4ec9b0]MyClass[/COLOR]:[COLOR=#4ec9b0]AbstractPublisher[/COLOR]
    {
 
        [COLOR=#569cd6][B]public[/B][/COLOR] [COLOR=#569cd6][B]override[/B][/COLOR] [COLOR=#569cd6][B]string[/B][/COLOR] [COLOR=white]MyData[/COLOR]
        {
            [COLOR=#569cd6][B]get[/B][/COLOR]
            {
                [COLOR=#569cd6][B]throw[/B][/COLOR] [COLOR=#569cd6][B]new[/B][/COLOR] [COLOR=#4ec9b0]NotImplementedException[/COLOR]();
            }
            [COLOR=#569cd6][B]set[/B][/COLOR]
            {
                [COLOR=#569cd6][B]throw[/B][/COLOR] [COLOR=#569cd6][B]new[/B][/COLOR] [COLOR=#4ec9b0]NotImplementedException[/COLOR]();
            }
        }
    }

Override the abstract properties and use them to publish the data.
That assumes that it makes sense for each derived type to have the same set of properties. The OP says that the classes are quite different so I don't think that that would make sense in this case.
 

StoneCodeMonkey

New member
Joined
Jul 7, 2016
Messages
3
Programming Experience
10+
Sure, but...

That assumes that it makes sense for each derived type to have the same set of properties. The OP says that the classes are quite different so I don't think that that would make sense in this case.

Sure, but the point is that the OP wants to use a common DGV for displaying data. So if the OP has a common abstract class, or even a base class to inherit from, this simplifies producing a collection of these objects. Once the objects are in a collection, they can be easily presented in the DGV.

Short of implementing a class that tracks each instance of each object type and transforms the data that needs to be presented to a format or common object for the DGV, deriving from a common base is the simplest way.

The DGV needs a collection, so why not bake in the required properties into the base class and then add them to a collection type appropriate to for the DGV?

Regards,

BTW, based on the title "Conceptual help Different classes in a single List<>?", the abstract or base class directly supports the idea of List<AbstractDataSource>. This allows the OP to have the single list they are looking for.

C#:
namespace Abstract
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass c = new MyClass();
            c.Iterate();
            Console.Read();
        }
    }


    abstract class AbstractDataPublisher
    {
        public abstract string MyData { get; }
    }


    class Class1 : AbstractDataPublisher
    {
        string data;
        public override string MyData
        {
            get
            {
                return data;
            }


        }
        public Class1()
        {
            data = "Class1.Tada!";
        }


    }
    class Class2 : AbstractDataPublisher
    {


        int data;
        public override string MyData
        {
            get
            {
                return string.Format("Class{0}.Tada!", data.ToString());
            }
        }


        public Class2()
        {
            data = 2;
        }
    }
    class Class3 : AbstractDataPublisher
    {
        double data;
        public override string MyData
        {
            get
            {
                return string.Format("Class{0}.Tada!", data.ToString());
            }
        }


        public Class3()
        {
            data = 3d;
        }
    }


    class MyClass
    {
        List<AbstractDataPublisher> myList;


        public MyClass()
        {
            myList = new List<AbstractDataPublisher>();


            myList.Add(new Class1());
            myList.Add(new Class2());
            myList.Add(new Class3());
        }


        public void Iterate()
        {
            foreach (var item in myList)
            {
                Console.WriteLine(item.MyData);
            }
        }


    }
}
 
Last edited:

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
3,792
Location
Sydney, Australia
Programming Experience
10+
Sure, but the point is that the OP wants to use a common DGV for displaying data. So if the OP has a common abstract class, or even a base class to inherit from, this simplifies producing a collection of these objects. Once the objects are in a collection, they can be easily presented in the DGV.

Short of implementing a class that tracks each instance of each object type and transforms the data that needs to be presented to a format or common object for the DGV, deriving from a common base is the simplest way.

The DGV needs a collection, so why not bake in the required properties into the base class and then add them to a collection type appropriate to for the DGV?

Regards,

BTW, based on the title "Conceptual help Different classes in a single List<>?", the abstract or base class directly supports the idea of List<AbstractDataSource>. This allows the OP to have the single list they are looking for.

C#:
namespace Abstract
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass c = new MyClass();
            c.Iterate();
            Console.Read();
        }
    }


    abstract class AbstractDataPublisher
    {
        public abstract string MyData { get; }
    }


    class Class1 : AbstractDataPublisher
    {
        string data;
        public override string MyData
        {
            get
            {
                return data;
            }


        }
        public Class1()
        {
            data = "Class1.Tada!";
        }


    }
    class Class2 : AbstractDataPublisher
    {


        int data;
        public override string MyData
        {
            get
            {
                return string.Format("Class{0}.Tada!", data.ToString());
            }
        }


        public Class2()
        {
            data = 2;
        }
    }
    class Class3 : AbstractDataPublisher
    {
        double data;
        public override string MyData
        {
            get
            {
                return string.Format("Class{0}.Tada!", data.ToString());
            }
        }


        public Class3()
        {
            data = 3d;
        }
    }


    class MyClass
    {
        List<AbstractDataPublisher> myList;


        public MyClass()
        {
            myList = new List<AbstractDataPublisher>();


            myList.Add(new Class1());
            myList.Add(new Class2());
            myList.Add(new Class3());
        }


        public void Iterate()
        {
            foreach (var item in myList)
            {
                Console.WriteLine(item.MyData);
            }
        }


    }
}

I agree with everything you just said but my previous statement still stands. What you're suggesting simply will not work if the derived classes are, as the OP states, significantly different. If they're not too different then your suggestion is great. If they are then it's no help at all.
 
Top Bottom