代码之家  ›  专栏  ›  技术社区  ›  Bruno

C多态性简单问题

  •  9
  • Bruno  · 技术社区  · 16 年前

    我得到了一个x类和一个y类,后者源自x:

    class x {}
    class y : x {}
    

    然后在某个地方我使用了一个x列表:

    List<X> lstX;
    ...
    

    然后,我想使用一个新的y列表,从我的其他列表中的数据中……沿着这些行:

    List<Y> lstY = lstX;
    

    我相信X列表中的项目会自动转换为Y,但事实并非如此。

    另外,如何从某个X初始化Y的新实例?我想这样做:

    var newX = new X();
    var newY = new Y(X);
    

    但似乎不是这样的。

    谢谢你的帮助! 很抱歉格式化,尽我所能

    7 回复  |  直到 14 年前
        1
  •  27
  •   Eric Lippert    16 年前

    这里有几个问题。

    首先:“我可以将老虎类型的对象分配给动物类型的变量。为什么我不能将老虎类型列表的对象分配给动物类型列表的变量?”

    因为这样会发生:

    List<Tiger> tigers = new List<Tiger>();
    List<Animal> animals = tigers; // this is illegal because if we allow it...
    animals.Add(new Giraffe()); // ... then you just put a giraffe into a list of tigers.
    

    在C 4中,这样做是合法的:

    IEnumerable<Tiger> tigers = new List<Tiger>();
    IEnumerable<Animal> animals = tigers;
    

    这是合法的,因为 IEnumerable<T> 没有“添加”方法,因此可以保证这是安全的。

    有关C 4这个新特性的详细信息,请参阅我关于协方差的系列文章。

    http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

    第二个问题:“我如何从某种动物身上初始化老虎的新实例?”

    你不能。有问题的动物可能是瓢虫。如何从瓢虫的实例初始化一个新的老虎?这没有任何意义,所以我们不允许你这么做。如果你想写你自己的方法,知道如何把任意的动物变成老虎,你可以自由地这样做。但我们不知道如何为你做到这一点。

        2
  •  3
  •   Marc Gravell    16 年前

    那是永远不会奏效的;毕竟 List<Y> lstY = lstX; 只需复制引用(除非您将自己的隐式静态转换运算符添加到自己的列表类型中),就可以了。 仍然 列表 X 可能包含一些东西 其他 比 Y 实例。

    即使在4.0中,协变/逆变也不会扩展到:列表(两者都是 in 和 out )或B:混凝土类型(如 List<T> )。

    有趣的是,它 将 (并且始终具有)用于引用类型数组,但仅限于方向 X[] arrX = arrY; . 它不转换任何内容;如果您尝试将错误的数据放入其中,它将抛出异常。

        3
  •  1
  •   Frederik Gheysels    16 年前

    不,因为您不能确定listx中所有类型为“x”的项也属于y类型。
    继承关系是相反的:类型Y的项可以强制转换为X,因为Y IS-A X。

    在C++中,也没有像C++一样可用的“复制构造函数”,所以我担心你必须实现这个逻辑,以便能够从某个X,你自己初始化一个新的Y实例。 另外,记住类是引用类型…

        4
  •  1
  •   Neil Barnwell    16 年前

    它不能自动将对象类型从x“加宽”到y,因为x不是y,而是y 是 X。

    你可以试着从X到Y,但这会失败的。 InvalidCastException 除非你的物体原来是一个Y,一直伪装成X。您需要初始化并填充一个新的 List<Y> 手动:

    IList<Y> yList = new List<Y>();
    foreach (var x in xList)
    {
        var y = new Y(x); // Copies/clones inherited properties from x to a new Y
        // TODO: Set other properties of y not present on x
        yList.Add(y);
    }
    
        5
  •  1
  •   Matheus de Lara Calache    14 年前

    试试这个:

    
    using System.Collections.Generic;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                List myList;
    
                myList = GetMyList(MyListTypeEnum.MyList1);
                myList = GetMyList(MyListTypeEnum.MyList2);
            }
    
            public static List GetMyList(MyListTypeEnum tipo)
            {
                List result;
                result = new List();
    
                switch (tipo)
                {
                    case MyListTypeEnum.MyList1:
                        List myList1 = GetMyList1();
                        foreach (var item in myList1)
                        {
                            result.Add((IMyList) item);
                        }
                        break;
                    case MyListTypeEnum.MyList2:
                        List myList2 = GetMyList2();
                        foreach (var item in myList2)
                        {
                            result.Add((IMyList) item);
                        }
                        break;
                }
    
                return result;
            }
    
            public static List GetMyList1()
            {
                List myList1 = new List();
                myList1.Add(new MyList1 { Code = 1 });
                myList1.Add(new MyList1 { Code = 2 });
                myList1.Add(new MyList1 { Code = 3 });
                return myList1;
            }
    
            public static List GetMyList2()
            {
                List myList2 = new List();
                myList2.Add(new MyList2 { Name = "1" });
                myList2.Add(new MyList2 { Name = "2" });
                myList2.Add(new MyList2 { Name = "3" });
                return myList2;
            }
        }
    
        public interface IMyList
        {
        }
    
        public class MyList1 : IMyList
        {
            public int Code { get; set; }
        }
    
        public class MyList2 : IMyList
        {
            public string Name { get; set; }
        }
    
        public enum MyListTypeEnum
        {
            MyList1,
            MyList2
        }
    }
    
    
        6
  •  0
  •   Sailing Judo    16 年前

    您的场景与通常的多态用例有点背道而驰。y是x,但x不是y。

    考虑到这一点,你可以 力 它按照您所说的方式工作,在构造函数中放置克隆代码,等等。

        7
  •  0
  •   GrayWizardx    16 年前

    对于第一个示例,您将遇到的问题是y从x派生,因此它“是x”,但“x不是y”,而且c目前不支持此方法中的类型转换。您可以尝试将扩展方法用于强制转换,例如lstx.convertall作为帮助器来完成此操作。

    对于第二个问题,您需要研究一个复制构造函数,例如:

    public Y(X baseObject) {
       //copy all the data you need here...
    }