A list of multiple data types?
I have two classes as such:
public class MachineLine
{
public double X1;
public double Y1;
public double X2;
开发者_StackOverflow中文版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?
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>();
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:
- Conditionals and polymorphism (presentation) discusses the same main idea as this answer does (replacing
if
type checks with polymorphism). - Martin Fowler: Role interface (article), which are interfaces designed towards a specific interaction.
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...
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()
};
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!
You could make the two classes implement the same interface, and make that interface the contained generic type of your List
.
精彩评论