精华内容
参与话题
问答
  • C#协变逆变

    2019-01-07 13:58:07
    1.泛型的协变逆变: 注意 :只能放在接口或者委托的泛型参数前面 1).委托泛型参数 static class TestConsole { /// <summary> /// The main entry point for the application. /// </summary> ...

     1.泛型的协变逆变:

    注意 :只能放在接口或者委托的泛型参数前面

    1).委托泛型参数

    static class TestConsole
        {
            /// <summary>
            /// The main entry point for the application.
            /// </summary>
            [STAThread]
            static void Main()
            {
                //-----协变(可以接受参数的子类作为实参,用out标记形参,该参数只能作为输出,比如作为方法的返回值);
                //-----逆变(可以接受参数的父类作为实参,用in标记形参,该参数只能作为输入,比如作为方法的参数)。
    
                //委托
                //public delegate TResult Func<in T, out TResult>(T arg)
                Func<Object, ArgumentException> fn1 = Test1;
                Func<String, Exception> fn2 = Test2;
                fn2 = fn1;//让fn2来执行fn1的方法,是允许的
    
                //泛型
                //public interface IEnumerable<out T> : IEnumerable
                GenTestFather(new Son[] { });
            }
    
            private static ArgumentException Test1(Object obj)
            {
                return new ArgumentException();
            }
    
            private static Exception Test2(String str)
            {
                return new Exception();
            }
    
            public static void GenTestFather(IEnumerable<Father> fathers)
            {
    
            }
            
        }
        public class Father
        {
            string Name { get; set; }
            
        }
    
        public class Son : Father
        {
    
        }

     2).接口泛型参数

    //泛型逆变
    ICustomerListIn<Sparrow> customerList1 = new CustomerListIn<Bird>();
    
    
     public interface ICustomerListIn<in T>
        {
            //T Get();
    
            void Show(T t);
        }
    
        public class CustomerListIn<T> : ICustomerListIn<T>
        {
            //public T Get()
            //{
            //    return default(T);
            //}
    
            public void Show(T t)
            {
    
            }
        }

    2.委托的协变逆变:

    将方法绑定到委托时,C#和CLR都允许引用类型的协变性和逆变性;

    协变性是指方法能返回从委托的返回类型派生的一个类型。

    逆变性是指方法获取的参数可以是委托的参数类型的基类。

    注意:不需要标记out  或 in 

    参考CLR.via.C# 【初始委托】

    3.理解;

    为什么返回值可以接受协变?

    里氏替换原则,基类出现的地方可以用子类替换。

    为什么参数可以接受逆变?

    如果接受了基类参数的方法或者泛型接口实例,然而在参数真正赋实参时,只能接受委托和泛型接口中定义中的类型,也就是父类类型,根据里氏替换原则,也是可以的

    详细理解,可参:https://www.cnblogs.com/vd630/p/4572946.html

    展开全文
  • C# 协变 逆变

    2013-03-16 15:47:00
    1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace test 7 { 8 class Program 9 { 10 static void M...
      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 
      6 namespace test
      7 {
      8     class Program
      9     {
     10         static void Main(string[] args)
     11         {
     12             Dog dog=new Dog();
     13             Animal animal=dog;
     14 
     15             Animal a1=new Animal();
     16             a1.Name="A1";
     17             Animal a2=new Animal();
     18             a2.Name="A2";
     19 
     20           
     21 
     22            //接口=OUT           
     23            // IEnumerable<Dog>
     24            // IEnumerator<Dog> 
     25            // IQueryable<Animal>
     26            // IGrouping<int,string>
     27 
     28             // 接口=IN
     29             //  IComparer<Dog>
     30             //  IComparable<Dog>
     31             //  IEqualityComparer<Dog>
     32 
     33             //  委托  IN | OUT       
     34             //  Action<int>
     35             //  Func<int,string>
     36             //  Converter<int,string>
     37             //  Comparison<int>
     38             //  Predicate<int>
     39 
     40 
     41             var listdog=GetDogs();
     42             IEnumerable<Animal> list1=listdog;
     43             foreach (var item in list1)
     44             {
     45                 item.Call();
     46             }
     47 
     48             List<Animal> list2 = listdog.Select(d => (Animal)d).ToList();
     49             foreach (var item in list2)
     50             {
     51                 item.Call();
     52             }
     53          
     54             
     55 
     56             //协变: 具体 赋给 抽象 
     57             IOut<Dog> myDogs = new TestOut<Dog>();
     58             IOut<Animal> myAnimals = myDogs;
     59             IOut<Animal> animals222=new TestOut<Dog>();
     60 
     61        
     62 
     63             //逆变: 抽象 赋给 具体 
     64             IIn<Animal> myAnimals2 = new TestIn<Animal>();            
     65             IIn<Dog> myDogs2 = myAnimals2;
     66             IIn<Dog> dogsss=new TestIn<Animal>();
     67 
     68 
     69             Console.ReadKey();
     70         }
     71 
     72         static IEnumerable<Animal> GetDogs()
     73         {
     74             Dog dog1=new Dog();
     75             dog1.Name="one";
     76             dog1.Age=11;
     77             Dog dog2=new Dog();
     78             dog2.Name="two";
     79             dog2.Age=22;
     80 
     81                DogEqual e=new DogEqual();
     82                e.Equals(dog1, dog2);
     83             
     84 
     85 
     86             List<Dog> listdog=new List<Dog>();
     87             listdog.Add(dog1);
     88             listdog.Add(dog2);
     89             return listdog;
     90         }
     91 
     92         static void ShowDog(IIn<Dog> dog)
     93         {
     94             
     95         }
     96 
     97         public interface IAnimal
     98         {
     99             void Call();
    100         }
    101         public  class Animal:IAnimal
    102         {
    103             public string Name;
    104             public virtual void Call()
    105             {
    106                 Console.WriteLine("Animal");
    107             }
    108         }
    109         public class Dog : Animal
    110         {
    111             public new string Name;
    112             public int Age;
    113             public override void Call()
    114             {
    115                 Console.WriteLine("Dog!");
    116             }
    117         }
    118 
    119         //out 协变  用于返回
    120         public interface IOut<out T>
    121         {
    122             T GetElement();
    123            
    124         }
    125 
    126         public class TestOut<T> : IOut<T> where T:new()
    127         {
    128             public T GetElement()
    129             {
    130                 return new T();
    131             }
    132         }
    133 
    134         //in 逆变 用于传入
    135         public interface IIn<in T>
    136         {
    137             void ChangeT(T t);
    138         }
    139         public class TestIn<T> : IIn<T> where  T:IAnimal
    140         {
    141             public void ChangeT(T t)
    142             {
    143                 t.Call();
    144             }
    145         }
    146 
    147 
    148         public class animalEqual : IEqualityComparer<Animal>
    149         {
    150 
    151             public bool Equals(Animal x, Animal y)
    152             {
    153 
    154                 return x.Name.Equals(y.Name);
    155             }
    156 
    157             public int GetHashCode(Animal obj)
    158             {
    159                 return 1;
    160             }
    161 
    162         }
    163 
    164 
    165 
    166         public class DogEqual : IEqualityComparer<Dog>
    167         {
    168 
    169             public bool Equals(Dog x, Dog y)
    170             {
    171 
    172                 return x.Name.Equals(y.Name) && x.Age==y.Age;
    173             }
    174 
    175             public int GetHashCode(Dog obj)
    176             {
    177                 return 1;
    178             }
    179 
    180         }
    181         
    182     }
    183 }

     逆变:http://msdn.microsoft.com/zh-cn/library/ms173174%28VS.80%29.aspx

     

    转载于:https://www.cnblogs.com/AspDotNetMVC/archive/2013/03/16/2822228.html

    展开全文
  • C# 协变逆变(泛型修饰符in和out)

    千次阅读 2015-08-04 10:02:27
    in和out用在范型interface和delegate中, 用来支持逆变协变(in是逆变,out是协变)。协变保留赋值兼容性,逆变与之相反。 下例中,泛型接口类型参数T有out修饰符,所以  a) 支持协变,ITest b = new D(); 可以...

    in和out用在范型interface和delegate中, 用来支持逆变和协变(in是逆变,out是协变)。协变保留赋值兼容性,逆变与之相反。


    下例中,泛型接口类型参数T有out修饰符,所以 
    a) 支持协变,ITest<Base> b = new D(); 可以编译通过,而ITest<Derive> d = new B();出错;
    b) T类型只能用过函数返回值,故 T GetInstance(); 编译通过,而 void ShowMe(T obj); 出错;


    如果是改为in修饰符,则支持逆变,上述情况相反!


    in和out泛型修饰符只能用在泛型接口和泛型委托里面,这样的泛型接口和泛型委托称为变体。

        class Base
        {
            
        }
    
        class Derive : Base
        {
            
        }
    
        interface ITest<out T>
        {
            T GetInstance();
    <span style="white-space:pre">	</span>void ShowMe(T obj);
        }
    
        class D : ITest<Derive>
        {
            
        }
    
        class B : ITest<Base>
        {
            
        }
    
        class Program
        {
            private static void Main(string[] args)
            {
                ITest<Derive> d = new B();
                ITest<Base> b = new D();
            }
        }
    



    .Net中典型的变体:
    public delegate TResult Func<out TResult>();// TResult类型是返回值类型
    public delegate TResult Func<in T, out TResult>(T arg); //输入类型T,返回类型TResult
    public interface IEnumerable<out T> : IEnumerable // T只出现在返回类型中
    public interface IEnumerator<out T> : IDisposable, IEnumerator
    public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable // 实现变体接口IEnumerable<T>
    public class Queue<T> : IEnumerable<T>, ICollection, IEnumerable //  实现变体接口 IEnumerable<T>





    展开全文
  • 主要介绍了c#协变逆变,以实例形式详细讲述了协变和逆变的原理与具体用法,具有一定的学习借鉴价值,需要的朋友可以参考下
  • 详解c# 协变逆变

    2020-12-16 19:34:19
    协变逆变在泛型参数中的表现方式,out关键字表示协变,in关键字表示逆变。二者只能在泛型接口或者委托中使用。 理解协变逆变 看完上面的定义是不是一脸懵逼~~~。看不懂就对了,且定义语句的歧义性很大。让我们...
  • C#协变逆变

    千次阅读 2018-04-28 17:37:11
    C#协变逆变在学习C#过程中要灵活运用接口与委托那么协变与逆变是重中之重,本人开始学习时也处于懵懂状态,经过几天的查阅与思考才得出答案。先来看一个例1: class Animal  {  public int NumberOfLegs = 4; ...

    C#协变与逆变

    在学习C#过程中要灵活运用接口与委托那么协变与逆变是重中之重,本人开始学习时也处于懵懂状态,经过几天的查阅与思考才得出答案。

    先来看一个例1

        class Animal
        {
            public int NumberOfLegs = 4;
        }
    
    
        class Dog : Animal
        {
    
    
        }
    
    
        class Program
        {
            static void Main(string[] args)
            {
                Animal a1 = new Animal();
                Animal a2 = new Dog();//将Dog类型隐式转化为Animal类型
                Console.WriteLine("Number of dog legs:" + a2.NumberOfLegs);
            }
        }
    

    从上面这个例子中我们可以看到,基类是Animal,有一个Dog类从Animal类派生。在Main中,我们创建了一个Dog类型的对象,并且将他成功赋值给Animal类型对象的变量a2。所以说每一个变量都有一种类型,你可以将派生类对象的实例赋值给基类的变量,这叫做赋值兼容性

    那么为什么可以将不同类型的引用转化呢?因为派生类包含了基类的信息,当派生类发现自己要转化和自己基类一个类型时,编译器知道这种做法是完全安全的,因为即将被转化的类型与自己基类型一样,所以不会造成基类里面的数据类型不匹配,所以编译器允许这样的类型转化。而反过来如果Animal类型要转化为Dog那么编译器会报错,这个就是因为如果基类要转化为与自己派生类一样的类型,那么必定会缺省派生类派生出来的那部分信息,这样是非常不安全的,编译器阻止这样做。总结一点,赋值兼容性实际是就是数据转化的安全性。

    协变

    先来看一个例2

         class Animal
        {
            public int Legs = 4;
        }
    
    
        class Dog : Animal
        {
    
    
        }
    
    
        delegate T Factory<T>();
    
    
        class Program
        {
            static Dog MakeDog()
            {
                return new Dog();
            }
    
    
            static void Main(string[] args)
            {
                Factory<Dog> dogMaker = MakeDog;//创建委托对象
                Factory<Animal> animalMaker = MakeDog;//尝试赋值委托对象,转化成功
                Console.WriteLine(animalMaker().Legs);
            }
        }

    先让dogMaker方法执行了返回Dog类型然后再把返回的Dog类型转化为Animal类型(Dog类型转换为Animal类型上面陈述了符合赋值兼容性),所以转化成功。

    而另一个例3

     class Animal
        {
            public int Legs = 4;
        }
    
    
        class Dog : Animal
        {
    
    
        }
    
    
        delegate T Factory<T>();
    
    
        class Program
        {
            static Dog MakeDog()
            {
                return new Dog();
            }
    
    
            static void Main(string[] args)
            {
                Factory<Dog> dogMaker = MakeDog;//创建委托对象
                Factory<Animal> animalMaker = dogMaker;//尝试赋值委托对象,编译器报错。
                Console.WriteLine(animalMaker().Legs);
            }
        }
    

    首先说一点委托和类一样,是一种用户自定义类型。但类表示的是数据和方法的集合,而委托是持有一个或多个方法,以及一系列预定义操作,委托算是特殊的类。上面例子问题在于尽管Dog是Animal的派生类,但是委托Factory<Dog>没有从委托Factory<Animal>派生,两个委托对象是平级的(就是两个互不相干的类),所以赋值兼容性不适用。

    那么怎样才可行呢?其实我们看到例2是能成功转换的,所以按照例2原理,加上out关键字即可(请看例4),这样就是强调编译器准备将返回值转化为目标值,这样委托的返回值与方法返回值就匹配了。其实本质上来讲就是根据的赋值兼容性。

    例4

    class Animal
        {
            public int Legs = 4;
        }
    
    
        class Dog : Animal
        {
    
    
        }
    
    
        delegate T Factory<out T>();
    
    
        class Program
        {
            static Dog MakeDog()
            {
                return new Dog();
            }
    
    
            static void Main(string[] args)
            {
                Factory<Dog> dogMaker = MakeDog;//创建委托对象
                Factory<Animal> animalMaker = dogMaker;//成功转换
                Console.WriteLine(animalMaker().Legs);
            }
        }

    逆变

    先来看看例5:

      class Animal
        {
        }
    
       class Dog : Animal
        {
        }
    
        class Program
        {
            delegate Animal Factory<T>();
            static Animal MakeAnimal()
            {
                return new Animal();
            }
    
            static void Main(string[] args)
            {
                Factory<Animal> animalMaker = MakeAnimal;
                Factory<Dog> dogmaker = animalMaker;//编译器报错,转换失败
            }
        }

    这个例子有两点问题,第一:这两个委托对象之间是平级的,没有继承关系,不能转换。第二:你会说像协变一样我加一个out关键字来要求编译器转换委托方法的返回值,但是Dog是父类,而Animal是子类,要求子类转化为父类是不行的,上面提到过因为不安全,但是怎么样才能转换呢?其实你发现可以先让dogmaker委托方法返回值降一级,就是将派生类Dog转化为基类Animal,然后就与animalMaker对应起来了,这个顺序与协变相反,但是仍然是父类转化为子类,本质上满足赋值兼容性,所以我们要加in关键字要求编译器准备好这样的变换。这就是例6

        class Animal
        {
        }
    
    
        class Dog : Animal
        {
        }
    
    
        class Program
        {
            delegate Animal Factory<in T>();
            static Animal MakeAnimal()
            {
                return new Animal();
            }
    
    
            static void Main(string[] args)
            {
                Factory<Animal> animalMaker = MakeAnimal;
                Factory<Dog> dogmaker = animalMaker;//编译器正常,转换成功
            }
        }
    协变与逆变就说到这里,要是我先学的是C++那么这些东西我理解得更快了=~=



    展开全文
  • C# 协变逆变

    2020-05-18 15:21:06
    C#中,协变逆变能够实现数组类型、委托类型和泛型类型参数的隐式引用转换。协变保留分配兼容性,逆变则与之相反。 协变:能够使用与原始指定的派生类型相比,派生程度更大的类型。 逆变:能够使用派生程度更小的...
  • C#协变逆变

    2019-08-19 11:02:15
    C# 中,协变逆变能够实现数组类型、委托类型和泛型类型参数的隐式引用转换。协变保留分配兼容性,逆变则与之相反。 关键字(in/out) 对于泛型类型参数,in关键字可指定类型参数是逆变的。可以在泛型接口和...
  • C# 协变逆变

    2017-12-20 13:04:00
    今天谈一谈协变逆变协变:通过协变,可以使用与泛型参数指定的派生类型相比,派生程度更大的类型。这样可以对委托类型和实现变体接口的类进行隐式转换。引用类型支持协变逆变,但值类型不支持 逆变:通过逆变...
  • c#协变逆变

    2021-01-07 20:56:09
    协变:父类变成子类 in和out:只能用来修饰委托和接口 类和结构体不行 in:只能当做参数 逆变 out:只能当做返回值,不能当形参 协变 以下都是错误的: ​ delegate T TestIn<in T>(T t); //不能返回T类型 ​ ...
  • C# 泛型 协变 逆变

    2020-07-08 11:24:11
    using System; using System.Text; namespace LyTest { class Animal { } class Bear : Animal { } class Camel : Animal { } public class Stack<T> : IPoppable<... T[] data = new .
  • msdn解释如下: “协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。 “逆变”则是指能够使用派生程度更小的类型。...上面是个人对协变逆变的理解,比起记住那些派生,类型,原始指定,更大,...
  • c# 协变逆变的理解

    2019-09-30 05:23:20
    1. 是什么 1.1 协变 协变指能够使用比原始指定的派生类型的派生程度更小(不太具体的)的类型。如 string 到 object 的转换。多见于类型参数用作方法的...泛型类型参数支持协变逆变,可在分配和使用泛型类型方面...

空空如也

1 2 3 4 5 ... 20
收藏数 655
精华内容 262
关键字:

c# 协变 逆变

c# 订阅