多个数据类型的列表?

60

我有以下两个类:

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;
}

我想创建一个可以容纳这两种类型的列表,但我不希望我的列表能够容纳其他任何数据类型。怎样实现?

6个回答

72

最简单的方法是声明一个接口,并要求这两种类型都实现它:

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;
}

那么你可以这样做:

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

7
啊,原来这就是接口!谢谢你提供一个例子! - Adam S
7
在这种类型的问题中,几乎总是争先恐后地回答,哈哈。 - bitxwise
1
@BoltClock - 我明白你的意思。虽然我认为最简单的答案总是得到最多的赞,但有趣的是大多数人也“太懒”或者其他原因不去回答更复杂的问题呢,哈哈。 - bitxwise
1
@bitxwise:我知道那种感觉,如果你看看我的一些两位数答案,它们中的一些甚至不到30个单词。 - BoltClock

46

您已经有了一些解释如何使用空接口来完成此操作:

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

这对于想要将对象放入列表中的情况非常有效。但是,如果您想要从列表中实际获取对象,该怎么办?你可以做什么以及上述解决方案会强制你做什么?
以下内容显示,使用一个非空接口可以做得更好。
IMachineSomething是一个空接口。也就是说,如果您真的想对列表中的对象执行某些操作,那么您所看到的只是该接口定义的空合同。所有实际功能都驻留在具体的类类型中。因此,您首先需要检查它们的类型,然后执行类型转换才能访问公共字段:
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 …
}

由于你正在使用面向对象的语言,有一个更好的解决方案:多态性。也就是说,在接口中放置公共功能(包括属性和方法),这样当你遍历列表时就不需要区分类型:

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
}

这使得你可以摆脱if (sth is …)类型检查和随后的类型转换,并将你的代码简化为以下形式:

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

循环不再需要关心如何处理每个元素。它只需要知道必须对一个元素进行"某些操作",而元素本身将知道这意味着什么。

另请参阅:


如果我需要将列表序列化为JSON字符串,这些解决方案是否有效?还是它们会序列化父类或接口? - Adam

10

你有两种方法:

1- 使用继承:

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- 使用接口(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>();

在您的情况下,我建议使用继承...


为什么您推荐使用子类化而不是接口? - stakx - no longer contributing
1
如果我需要将列表序列化为JSON字符串,这些解决方案是否有效?或者它们会序列化父类或接口吗? - Adam

8

只需创建自己的界面

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()
                                };

8
现在,使用C# 7.0就像使用元组一样简单:
public List<(MachineLine line, MachineCircle circle)> machineList { get; set; }

添加数据(注意((和)),一个括号表示参数,另一个括号表示元组):

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

machineList.Add((theLine, theCircle));

获取数据的方法:

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

一、二、三,就这么简单!

这比接口/超类更好,还是更差,还是取决于情况?如果是最后一种选项,您能否举一个例子说明何时使用其中一种比另一种更好? - Kil jeaden

6
你可以让这两个类实现相同的接口,然后将该接口作为你的List所包含的泛型类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接