该文章中运用了较多的 托付delegate和Lambda表达式,假如你并不熟习这些,请检察我的文章《托付与匿名托付》、《匿名托付与Lambda表达式》以便帮你竖立完全的学问系统。
在C#从降生到发展壮大的过程当中,新学问点不停引入。逆变与协变并非C#首创的,属于后续引入。在Java中一样存在逆变与协变,后续我还会写一篇Java逆变协变的文章,有兴致的朋侪能够关注一下。
逆变与协变,听起来很笼统、深邃,实在很简单。看下面的代码:
class Person { } class Student : Person { } class Teacher: Person { } class Program { static void Main(string[] args) { List<Person> plist = new List<Person>(); plist = new List<Student>(); plist = new List<Teacher>(); } }
在上面的代码中,plist = new List<Student>()、plist = new List<Teacher>()两句发作编译毛病。虽然Person是Student/Teacher的父类,但List<Person>范例却不是List<Student/Teacher>范例的父类,所以上面的赋值语句报范例转换失利毛病。
如上如许的赋值操纵,在C# 4.0之前是不许可的,至于为何不许可,范例安满是主要要素。看下面的示例代码:
List<Person> plist = new List<Student>(); plist.Add(new Person()); plist.Add(new Student()); plist.Add(new Teacher());
以下示例,假定 List<Person> plist = new List<Student>() 许可赋值,那plist虽然范例为List<Person>鸠合,但现实指向确是List<Student>鸠合。plist.Add(new Person()),增添操纵现实挪用的是List<Student>.Add()。Person范例没法平安转换为Student,所以如许的鸠合定义没有意义,所以上面的假定不成立。
但状况在C# 4.0今后发作了变化,并非"不可能发作的事变发作了",而是运用的灵活性做出了新的调解。一样的在C# 4.0中上面的顺序还是不被许可的,但却涌现了破例。从C# 4.0最先,在泛型托付、泛型接口中,许可特别状况的发作(实质上并未发作特别变化,背面申明)。以下示例:
delegate void Work<T>(T item); class Person { public string Name { get; set; } } class Student : Person { public string Like { get; set; } } class Teacher : Person { public string Teach { get; set; } } class Program { static void Main(string[] args) { Work<Person> worker = (p) => { Console.WriteLine(p.Name); }; ; Work<Student> student_worker = (s) => { Console.WriteLine(s.Like); }; student_worker = worker; //此处编译毛病 } }
依据前面的理论支撑,student_worker = worker;的毛病很轻易明白。但此处我们顺序的目标是让 woker 充任 Work<Student> 的功用,今后挪用 student_worker(s)现实挪用的是woker(s)。为了满足我们的需求,须要顺序做2方面的处置惩罚:
1、因在挪用student_worker(s)时,实质实行的是woker(s),所以须要s变量的范例能胜利转换为woker须要的参数范例。
2、须要通知编译器,此处许可将 Work<Person> 范例的对象赋值给 Work<Student>范例的变量。
前提1在挪用时student_worker(),时编译器会提醒请求参数必需是Student范例对象,该对象可胜利转换为Person范例对象。
前提2则须要对Woke托付定义举行调解,调解以下:
delegate void WorkIn<in T>(T item);
托付名字改成WorkIn是为却别修正前后的托付,症结的地方为<in T>。经由过程增添 in 症结字,标注该泛型托付的范例参数T,仅作为托付要领的参数来运用。此时上面的顺序便可胜利编译并实行。
delegate void WorkIn<in T>(T item); class Program { static void Main(string[] args) { WorkIn<Person> woker = (p) => { Console.WriteLine(p.Name); }; WorkIn<Student> student_worker = woker; student_worker(new Student() { Name="tom", Like="C#" }); } }
关于请求范例参数为子范例,许可赋值范例参数为父范例值的这类状况,称为逆变。逆变在C#中须要用 in 标注泛型的范例参数。逆变虽叫逆变,但只是情势上看似父类对象赋值给子类变量,实质上是要领挪用时参数的范例转换。Student s = new Person(),这是不可能的,这不是逆变是毛病。
上面的代码如你能转换为下面的情势,那你就能够忘记逆变,实质比征象更主要
以上就是C#中关于逆变和协变的详解的细致内容,更多请关注ki4网别的相干文章!