A list of multiple data types?

93,519

Solution 1

The easiest way to do this is to declare an interface, and have both types implement it:

public interface IMachine { … }

public class MachineLine : IMachine
{
    public double X1;
    public double Y1;
    public double X2;
    public double Y2;
    public double Thickness;
}

public class MachineCircle : IMachine
{
    public double CenterX;
    public double CenterY;
    public double Radius;
}

Then you can do this:

List<IMachine> m = new List<IMachine>();

Solution 2

You've got some answers explaining how to do this with an empty interface:

interface IMachineSomething {}
…
var m = new List<IMachineSomething>();

This works fine if all you want to put objects into that list. But what if you wanted to actually fetch objects from the list? What could you actually do with them, and what would the above solution force you to do?

The following shows that you can do even better with a non-empty interface.

IMachineSomething is an empty interface. That is, if you actually want to do something with the objects in your list, all you're going to see of them is the empty contract defined by that interface. All the actual functionality resides with the concrete class types. So you'd first have to check their type and then perform a type-cast in order to access the public fields:

foreach (IMachineSomething sth in m)
{
    if (sth is MachineLine)
    {
        var machineLine = (MachineLine)sth;
        machineLine.DoThis();
    }
    else if (sth is MachineCircle)
    {
        var machineCircle = (MachineCircle)sth;
        machineCircle.DoThat();
    }
    else …
}

Since you're working with a object-oriented language, there is a much better solution: Polymorphism. That is, put common functionality (properties as well as methods) in the interface, so that you won't need to distinguish between the types when you go through your list:

interface IMachineSomething
{
    void DoSomething();
}

class MachineLine : IMachineSomething
{
    …
    public void DoSomething() { DoThis(); } // example for implicit implementation, or…
}

class MachineCircle : IMachineSomething
{
    …
    void IMachineSomething.DoSomething() { DoThat(); } // …explicit implementation
}

Which allows you to get rid of the if (sth is …) type checks and subsequent type-casts, and simplify your code to this:

foreach (IMachineSomething sth in m)
{
    sth.DoSomething();
}

The loop no longer needs to care how each element gets processed exactly. All it needs to know that it must "do something" with an element, and the element itself will know what that means.

See also:

Solution 3

You have 2 ways:

1- Use inheritance:

public class MachineShape{}

public class MachineLine :MachineShape
{
    public double X1;
    public double Y1;
    public double X2;
    public double Y2;
    public double Thickness;
}

public class MachineCircle : MachineShape
{
    public double CenterX;
    public double CenterY;
    public double Radius;
}

List<MachineShape> m = new List<MachineShape>();


2- Use interface:

public interface IMachineShape{}

public class MachineLine : IMachineShape
{
    public double X1;
    public double Y1;
    public double X2;
    public double Y2;
    public double Thickness;
}

public class MachineCircle : IMachineShape
{
    public double CenterX;
    public double CenterY;
    public double Radius;
}

List<IMachineShape> m = new List<IMachineShape>();

And I recommend inheritance in your case...

Solution 4

Just create your own interface

public interface IMySillyInterface {}

public class MachineLine : IMySillyInterface
{
    public double X1;
    public double Y1;
    public double X2;
    public double Y2;
    public double Thickness;
}

public class MachineCircle : IMySillyInterface
{
    public double CenterX;
    public double CenterY;
    public double Radius;
}

List<IMySillyInterface> list = new List<IMySillyInterface> 
                                {
                                    new MachineCircle(), 
                                    new MachineLine()
                                };

Solution 5

Now, with C# 7.0 is as easy as using a Tuple:

public List<(MachineLine line, MachineCircle circle)> machineList { get; set; }

To add data (Note the (( and )), one parenthesis for params and another one to indicate tuple):

MachineLine theLine = ...;
MachineCircle theCircle = ...;

machineList.Add((theLine, theCircle));

To get data:

MachineLine ml = emailSender.ElementAt(3).line;
MachineCircle mc = emailSender.ElementAt(3).circle;

As easy as one, two, three!

Share:
93,519
Adam S
Author by

Adam S

Updated on July 09, 2022

Comments

  • Adam S
    Adam S almost 2 years

    I have two classes as such:

    public class MachineLine
    {
        public double X1;
        public double Y1;
        public double X2;
        public double Y2;
        public double Thickness;
    }
    
    public class MachineCircle
    {
        public double CenterX;
        public double CenterY;
        public double Radius;
    }
    

    I want to make a List which can hold both of these, but I don't want my list to be able to hold any other data type. How can this be done?