本帖最后由 aaatttcccc 于 2012-03-30 23:14:41 编辑

解决方案 »

  1.   

    逆变和协变是针对泛型接口的上下层次隐式转换, 在.net4 以前是不允许的, 
    而普通类的上下转换一直都支持..net4 加入这个功能就是为了让泛型类也保持这样的便利性.
      

  2.   

    比如这个方法:
     void ProcessControls(IEnumerable<Control> ctrs){ }
    没有协变,你就还得另外创建个实现了IEnumerable<Control>的集合。再比如:
    Comparison<Control> ControlComparison =
                    (ctr1, ctr2) => ctr1.Left - ctr2.Left;            List<TextBox> TextBoxList = new List<TextBox>()
                              {new TextBox(){Left=30},new TextBox(){Left=22}};
                List<Label> LabelList = new List<Label>();
                    
                TextBoxList.Sort(ControlComparison);
                LabelList.Sort(ControlComparison);
    现在ControlComparison很简单,你可以每次在Sort方法里复制一份,但是如果它的逻辑很复杂就不行了。
    或者要使用一个别人提供的,不知道内部实现的IComparer<Control>。
      

  3.   

    楼主先把继承搞明白再研究这些东西吧。
    你看看你自己写的Animal和Dog,除了Dog继承了Animal之外一点点关系都没有。
      

  4.   


    额我知道你的意思,你看下这个,主要我是想实验一下,他们的animal的方法通过协变,究竟变了没有。someAnimals.ToList()[0].animal();
    someDogs.ToList()[0].animal();
      

  5.   


    这个是用来解决设计问题,而不是根据这个能创建设计...
    你反过来想,ruby/python/js会有协变和逆变吗?
      

  6.   

    按照你的写法Animal和Dog根本就没什么关系,所以声明类型,调用的就是哪个类型的方法。
    所以someAnimals里面是Animal,调用的就是Animal的方法。
    someDogs里面是Dog,调用的就是Dog的方法。
    虽然都是同一个对象。
      

  7.   


    不好意思我看了很久,并且照着代码敲打,感觉没看出逆变和协变的效果...
    比如这个
    C# code
    private void ProcessControls(IEnumerable<Control> ctrs) 
    {
            
    }private void Action()
    {
         List<TextBox> textboxs = new List<TextBox>();
         List<Label> LabelList = new List<Label>();
         ProcessControls(textboxs);
         ProcessControls(LabelList);
    }难道这样就是协变了吗?如果这样就是协变了话,那么ProcessControls方法也就有比较大的限制吧?只能应用于textboxs和LabelList的共性
    还有你说的第2个:
    C# code
                //如果这个很复杂的一个方法,然后下面的两个控件都是比较
                Comparison<Control> ControlComparison = (ctr1, ctr2) => ctr1.Left - ctr2.Left;            List<TextBox> TextBoxList = new List<TextBox>() { new TextBox() { Left = 30 }, new TextBox() { Left = 22 } };
                List<Label> LabelList = new List<Label>();            TextBoxList.Sort(ControlComparison);
                LabelList.Sort(ControlComparison);
    这个是逆变吗?这个我看不懂= =...
    你所说的“你可以每次在Sort方法里复制一份”
    是指这样吗
    TextBoxList.Sort((ctr1, ctr2) => ctr1.Left - ctr2.Left);
    LabelList.Sort((ctr1, ctr2) => ctr1.Left - ctr2.Left);
      

  8.   

    复制就是那个意思
    ----------------------------------------
    这个是委托逆变。泛型在接口和委托两个地方支持变体(逆变和协变)
    比如说TextBoxList,它的类型是List<TextBox>,所以Sort方法的参数类型就是Comparison<TextBox>(这是一个委托类型)。如果没有逆变,Comparison<Control>就不能转化为Comparison<TextBox>,也就无法使用ControlComparison委托对象。IComparer<T>同理,List<TextBox>的Sort方法要求参数类型为IComparer<TextBox>,如果没有逆变,就不能用一个IComparer<Control>对象作为它的参数。当然,你可以把比较器类实现为开放类型,让它可以产生其他类型的比较器。比如:
    class MyControlComparer<T> : IComparer<T> where T:System.Windows.Forms.Control {}
    当你需要排序任何Control派生类集合的时候可以方便地创建一个对应类型的比较器,但是你仍然需要创建新的比较器对象,而不能复用已有的比较器对象(或许这个比较器有很多的配置选项)。而如果是封闭实现
    class MyControlComparer : IComparer<System.Windows.Forms.Control> { }
    那就像上面所说的,没办法使用了。
      

  9.   


    非常感谢你的耐心回答!!!!!!但是貌似这些都是转换类中的某个方法??我看的还是很朦胧...即使以你的例子来代码写还是很朦胧...那我想问个问题
    现在我的项目
    有父抽象类Animal,有属性:吃,方法:叫
    然后有2个子类:
    猫:Cat,有属性:抓
    狗:Dog,有属性:咬我想在程序初始化中先 Animal animal = null
    然后在判断条件中如果是猫:
    animal = new Cat;
    如果是狗
    animal = new Dog;但是,我想通过逆变来让animal可以使用到Cat或者Dog的属性。也就是整个代码中,我依然使用animal,而不用实例化cat或者dog来增加代码量
      

  10.   

    变体是指类型转换关系的映射,不能完成你想要的功能。比如说string->object的转换,可以映射到string[]和object[]上:string[]->object[],这种映射能力叫做变体。
    映射后的转换关系string[]->object[]和原始的转换关系string->object方向相同,这种变体叫做“协变”;如果方向相反,就叫“逆变”。
    string->object的转换关系可以映射为IComparer<object>->IComparer<string>,和原来的方向相反,所以它是“逆变”。上面这种string->object的转换,本质上是“派生类引用到基类引用的转换”。除了这种转换,还有其他转换,比如基元类型之间的转换,像byte->short->int->long,以及用户自定义的转换。在.NET中,只有派生类引用到基类引用的转换支持变体,其他两种不支持。
    所以虽然byte可以隐式转换为int,但是byte[]不能转换为int[];同样,IEnumerable<byte>不能转换为IEnumerable<int>。变体不仅存在于泛型中,也存在于其他地方,比如上面例子中的数组,另外就是委托和函数的绑定。至于变体体现代码重用这个很明显啊,它可以将原有类型的转换自动映射到基于原有类型的新类型上,这样你同一份代码就可以用在更多的地方。(如果没有变体,这些新类型之间就没有转换关系,需要为每种类型单独写代码。)
      

  11.   

         IEnumerable<Animal> someAnimals = someDogs;这个代码就是协变。.net4.0以前是不能这样写代码的,你不知道以前是怎样的,当然那就不觉得这是什么进步。
      

  12.   

    以前,你要想把someDogs赋值给someAnimals ,根本不可能能。你只能写代码先 new List<Animal>(),然后再逐一将someDogs集合中的元素一个一个地Add进someAnimals里边。
      

  13.   

    至于你说为什么要为对象声明 IEnumerable<Animal> 类型而不直接声明为 IEnumerable<Dog>,为什么要有个 Action<Animal> 而不直接把委托声明为 Action<Dog>,那不就是为了刻意给你做演示嘛!
      

  14.   


    Animal和Cat不能互相转换?只能针对类型?对象之前不能转换么
      

  15.   

    IEnumerable这个是集合吧,单对象协变不了吗?
    就是 dog  = animal ;
      

  16.   

    唉,为什么有的人学编程就那么累呢。协变和逆变是很自然的东西。这好比原先没有一个叫平板电脑的东西。但是它的出现,马上大家就觉得这东西好用。没有人会问,为什么平板电脑体现了好用,或者这东西以前没有,所以我不会用这样的问题。难道你不觉得
    既然 People cnp = new ChinesePeople(); 成立List<Prople> cnp = new List<ChinesePeople>(); 是很自然的事情。不过遗憾的是,在C# 4以前这么写不行。现在行了,难道还有什么需要研究的呢?