【问题标题】:A list of multiple data types?多种数据类型的列表?
【发布时间】:2011-05-19 14:30:21
【问题描述】:

我有两个这样的课程:

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

我想创建一个可以同时保存这两种数据的列表,但我不希望我的列表能够保存任何其他数据类型。如何做到这一点?

【问题讨论】:

    标签: c# class list


    【解决方案1】:

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

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

    【讨论】:

    • 啊,这就是接口!感谢您提供示例!
    • 面对这些类型的问题,几乎总是一场比赛,看谁能先回答,哈哈
    • @BoltClock - 我听到了。虽然我认为最简单的答案总是会收到必须的 UP 也很有趣——而大多数人也“太懒”或者其他什么都无法解决更复杂的问题,呵呵
    • @bitxwise:我知道这种感觉,如果你看看我的一些 2 位数答案,其中一些甚至不到 30 个字。
    【解决方案2】:

    您已经得到了一些解释如何使用空界面执行此操作的答案:

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

    如果您想将所有对象放入该列表,则此方法可以正常工作。但是,如果您想真正从列表中获取对象 怎么办?您实际上可以用它们做什么,上述解决方案会迫使您做什么?

    以下显示使用非空接口可以做得更好。

    IMachineSomething 是一个空接口。也就是说,如果您真的想对列表中的对象做某事,那么您将看到的只是该接口定义的空合约。所有实际功能都存在于具体的class 类型中。因此,您首先必须检查它们的类型,然后执行类型转换才能访问公共字段:

    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 字符串,这些解决方案会起作用吗?还是他们会序列化父类或接口?
    【解决方案3】:

    你有两种方法:

    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- 使用界面:

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

    我建议在你的情况下继承......

    【讨论】:

    • 你为什么推荐子类化而不是使用接口?
    • 如果我需要将列表序列化为 json 字符串,这些解决方案会起作用吗?还是他们会序列化父类或接口?
    【解决方案4】:

    只需创建您自己的界面

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

    【讨论】:

      【解决方案5】:

      您可以使这两个类实现相同的接口,并使该接口成为您的List 的包含泛型类型。

      【讨论】:

        【解决方案6】:

        现在,使用 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;
        

        就像一、二、三一样简单!

        【讨论】:

        • 这是比接口/超类好,还是差还是看场景?如果它是最后一个选项,请给我一个例子,说明什么时候比另一个更好?
        猜你喜欢
        • 1970-01-01
        • 2021-12-27
        • 2020-02-16
        • 2021-06-16
        • 1970-01-01
        • 1970-01-01
        • 2012-10-03
        • 2015-11-25
        • 2022-01-01
        相关资源
        最近更新 更多