旗下导航:搜·么
当前位置:网站首页 > .Net教程 > 正文

C#中关于逆变和协变的详解【C#.Net教程】,csharp,.net,详解

作者:搜教程发布时间:2019-11-27分类:.Net教程浏览:30评论:0


导读:这篇文章主要为人人细致引见了C#逆变与协变的相干材料,具有肯定的参考价值,感兴致的小伙伴们能够参考一下该文章中运用了较多的托付delegate和Lambda表达式,假如你...
这篇文章主要为人人细致引见了C#逆变与协变的相干材料,具有肯定的参考价值,感兴致的小伙伴们能够参考一下

该文章中运用了较多的 托付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网别的相干文章!

标签:csharp.net详解


欢迎 发表评论: