威尼斯人线上娱乐

那么些年搞不懂的术语,协理明白

2 4月 , 2019  

  前几日回首了前头看的《深远精晓C#》那本书中的泛型章节,在这之中对泛型的可变性的通晓。泛型可变性分二种:协变和逆变。逆变也又称为抗变。

近年来在看《C#尖端编制程序(第八版)》那本书,看到了泛型接口那章。个中关于协变和逆变没太精晓,讲得有点坑爹,网上查了无数材质,总算(感觉)弄通晓了,来此处记录一下。

【转】这几个年搞不懂的术语、概念:协变、逆变、不变体

简述什么是协变性、逆变性、不变性

  • 协变性,如:string->object (子类到父类的更换)
  • 逆变性,如:object->string (父类到子类的转移)
  • 不变性,基于下边二种处境,不可变。具体下边再做分析。

 怎么明白这七个名词的意思:

壹、协变和逆变是怎么?

先从字面上理解 协变(Covariance)、逆变(Contravariance)。
co- 是英文中表示“协同”、“同盟”的前缀。协变 的字面意思正是“与变化的动向1致”。
contra- 是英文中象征“相反”的前缀,逆变 的字面意思正是是
“与转移趋势相反”。
那么难题来了,那里的 变化方向 指的是怎么样?
C# 中对于指标(即对象引用),仅存在一种隐式类型转换,即
子类型的目的引用到父类型的靶子引用的变换。那里的变化指的正是那种
子->父 的类型转换。
object o = “hello”;
//string (子类)类型的引用转换为 object (父类)类型的引用
协变与逆变即便从名字上看是八个完全相反的转移,但事实上只是“子类型引用到父类型引用”那一进度在函数中使用的
多个例外阶段 而已,接下去将详细表达那一点。

简述什么是协变性、逆变性、不变性

  • 协变性,如:string->object (子类到父类的转移)
  • 逆变性,如:object->string (父类到子类的转换)
  • 不变性,基于上面三种景况,不可变。具体上面再做分析。

泛型委托的可变性

先利用框架定义的泛型委托Func和Action做例子(不打听的请戳)

协变:(string->object)

Func<string> func1 = () => "农码一生";
Func<object> func2 = func1;

逆变:(object->string)

Action<object> func3 = t => { };
Action<string> func4 = func3;

上边代码未有此外难题。

随即大家和好定义委托试试:

威尼斯人线上娱乐 1

小编X,看人不来哦。为啥自定义的嘱托却不可能协变呢。

本身看看系统定义的Func到底和大家自定义的有啥两样:

public delegate TResult Func<out TResult>();

多了三个out,什么鬼:

  • out:对于泛型类型参数,out 关键字钦定该类型参数是协变的。 能够在泛型接口和委托中运用 out 关键字。(来源)
  • in:对于泛型类型参数,in 关键字钦点该品种参数是逆变的。 能够在泛型接口和嘱托中应用 in 关键字。(来源)

那就是说大家能够修改自定义委托:

威尼斯人线上娱乐 2

完美!

那如果大家要完成逆变性呢:

威尼斯人线上娱乐 3

直白逆变是不可行的,大家必要修改泛型类型参数:

威尼斯人线上娱乐 4

大家发现全数信托参数都变了。本来的重回值,改成输入参数才行。

结论:

  • in->输入参数->可逆变(父类到子类的变型[如 object->string])
  • out->重返值->可协变(子类到父类的转变[如 string->object])

 

假设:如若泛型参数中既存在in又存在out改怎么样:

delegate Tout MyFunc<in Tin, out Tout>(Tin obj);

MyFunc<object, string> str1 = t => "农码一生";
MyFunc<string, string> str2 = str1;//第一个泛型的逆变(object->string)
MyFunc<object, object> str3 = str1;//第二个泛型的协变(string->object)
MyFunc<string, object> str4 = str1;//第一个泛型的逆变和第二个泛型的协变

以上都以未曾难题的。 

然后大家看看编译后的C#代码:

威尼斯人线上娱乐 5

结论:

  • 所谓的逆变其实只是编写翻译后展开了挟持类型转换而已。

如上代码也得以直接写成:

//delegate Tout MyFunc<in Tin, out Tout>(Tin obj);
MyFunc<string, string> str5 = t => "农码一生";
MyFunc<object, object> str6 = t => "农码一生";
MyFunc<string, object> str7 = t => "农码一生";

  壹:协变即为在泛型接口类型中选拔out标识的品类参数。协变的字面意思是“与转变的趋向壹致”贰逆变那便是用in来标识的泛型接口类型的档次参数。逆变的字面意思是“与变化的大势相反”

二、使用函数的不等等级产生的类型转换

一旦有1函数,接收 object 类型的参数,输出 string 类型的重临值:

string Method(object o)
{
    return "abc";
}

这便是说在Main函数中大家能够那样调用它:

string s = "abc";
object o = Method(s);

专注,那里发生了五回隐式类型转换:

  1. 在向函数输入时,参数 s 由 string 类型转换为 object 类型
  2. 那么些年搞不懂的术语,协理明白。在函数输出(再次回到)时,重返值 由 string 类型转换为 object 类型
    咱俩那边可以视作是函数签名可发生转换(不论函数的内容,不影响结果):
  3. string Method(object o) 可转移为 string Method(string o)
  4. string Method(string o) 可转换为 object Method(string o)
    也正是说,在函数输入时,函数的 输入类型 可由 object 变换为
    string,父->子
    在函数输出时,函数的 输出类型 可由string变换为object,子->父

泛型委托的可变性

先利用框架定义的泛型委托Func和Action做例子(不打听的请戳)

协变:(string->object)

Func<string> func1 = () => "农码一生";
Func<object> func2 = func1;

逆变:(object->string)

Action<object> func3 = t => { };
Action<string> func4 = func3;

上边代码未有其他难题。

继而我们友好定义委托试试:

威尼斯人线上娱乐 6

作者X,看人不来哦。为啥自定义的嘱托却不能够协变呢。

本人看看系统定义的Func到底和我们自定义的有怎么着区别:

public delegate TResult Func<out TResult>();

多了三个out,什么鬼:

  • out:对于泛型类型参数,out 关键字钦定该品种参数是协变的。 能够在泛型接口和委托中央银行使 out 关键字。(来源)
  • in:对于泛型类型参数,in 关键字钦点该品种参数是逆变的。 能够在泛型接口和信托中利用 in 关键字。(来源)

那么我们得以修改自定义委托:

威尼斯人线上娱乐 7

完美!

那如果大家要促成逆变性呢:

威尼斯人线上娱乐 8

一向逆变是不可行的,咱们要求修改泛型类型参数:

威尼斯人线上娱乐 9

我们发现1切信托参数都变了。本来的重回值,改成输入参数才行。

结论:

  • in->输入参数->可逆变(父类到子类的变型[如 object->string])
  • out->再次回到值->可协变(子类到父类的转变[如 string->object])

 

假使:借使泛型参数中既存在in又存在out改怎么着:

delegate Tout MyFunc<in Tin, out Tout>(Tin obj);

MyFunc<object, string> str1 = t => "农码一生";
MyFunc<string, string> str2 = str1;//第一个泛型的逆变(object->string)
MyFunc<object, object> str3 = str1;//第二个泛型的协变(string->object)
MyFunc<string, object> str4 = str1;//第一个泛型的逆变和第二个泛型的协变

上述都以未曾难题的。 

接下来大家看看编译后的C#代码:

威尼斯人线上娱乐 10

结论:

  • 所谓的逆变其实只是编译后开始展览了威吓类型转换而已。

以上代码也能够直接写成:

//delegate Tout MyFunc<in Tin, out Tout>(Tin obj);
MyFunc<string, string> str5 = t => "农码一生";
MyFunc<object, object> str6 = t => "农码一生";
MyFunc<string, object> str7 = t => "农码一生";

泛型接口的可变性

随着看框架私下认可接口:

协变:(子类->父类)

IEnumerable<string> list = new List<string>();
IEnumerable<object> list2 = list;

逆变:(父类-> 子类)

IComparable<object> list3 = null;
IComparable<string> list4 = list3;

接下去大家尝试自定泛型接口:

首先定义测试项目、接口:

// 人
public class People
{ }
//老师(继承People[人])
public class Teacher : People
{ }
//运动
public interface IMotion<T>
{ }
//跑步
public class Run<T> : IMotion<T>
{ }

接下来大家测试协变性:

威尼斯人线上娱乐 11

平等我们须要把接口 interface
IMotion<T> 定义为 interface IMotion<out T> 

//运动
public interface IMotion<out T>{}

IMotion<Teacher> x = new Run<Teacher>();
IMotion<People> y = x;

若果我们要测试逆变性,则要求把 interface IMotion<T>
 定义为 interface IMotion<in T> 

//运动
public interface IMotion<in T>{}

IMotion<People> x2 = new Run<People>();
IMotion<Teacher> y2 = x2;

泛型接口的逆变,编写翻译后壹致进行了强制转换:

威尼斯人线上娱乐 12

理所当然,大家也能够间接写成:

IMotion<Teacher> y3 = new Run<People>();

  须要小心的是无论协变仍旧逆变也只能在泛型接口中来使用。

三、驾驭泛型接口中的 in、out参数

从未有过点名in、out的意况
倘使有1泛型接口,并且有3个类完成了此接口:

interface IDemo<T>
{
    T Method(T value);
}
public class Demo : IDemo<string>
{
    //实现接口 IDemo<string>
    public string Method(string value)
    {
        return value;
    }
}

在Main函数中那样写:
IDemo<string> demoStr = new Demo();
IDemo<object> demoObj = demoStr;
上面包车型地铁那段代码中的第2行李包裹蕴了四个若是:
I德姆o<string> 类型可以隐式转换为 I德姆o<object> 类型
那乍看上去就如“子类型引用转换为父类型引用”
一样,然则很不满,他们并区别。假诺能够开始展览隐式类型转换,那就象征:
string Method(string value) 能转换为 object Method(object value)
从上一节中大家领悟,在函数那输入和输出阶段,其种类可转变方向是区别的。所以在C#中,要想使用泛型接口类型的隐式转换,须要研讨“输入”和“输出”二种情况。
接口仅用于出口的动静,协变

interface IDemo<out T>
{
    //仅将类型 T 用于输出
    T Method(object value);
}
public class Demo : IDemo<string>
{
    //实现接口
    public string Method (object value)
    {
        //别忘了类型转换!
        return value.ToString();
    }
} 

在Main函数中这样写:
IDemo<string> demoStr = new Demo();
IDemo<object> demoObj = demoStr;
可将 string Method (object value) 转换为 object Method (object value)
即可将 I德姆o<string> 类型转换为 I德姆o<object> 类型。
仅从泛型的类型上看,这是 “子->父”
的变换,与第3节中涉嫌的转换方向相同,称之为“协变”。
接口仅用于输入的场所,逆变
同理大家得以给 T 加上 in 参数:

interface IDemo<in T>
{
    //仅将类型 T 用于输入
    string Method(T value);
}
public class Demo : IDemo<object>
{
    //实现接口
    public string Method (object value)
    {
        return value.ToString();
    }
} 

在Main函数中如此写:
IDemo<object> demoObj = new Demo();
IDemo<string> demoStr = demoObj;
此地可将 string Method (object value) 转换为 string Method (string
value)
即可将 I德姆o<object> 类型转换为 IDemo<string> 类型。
仅从泛型的门类上看,那是 “父->子”
的更换,与第贰节中涉及的转换方向相反,称之为“逆变”,有时也译作“抗变”或“反变”。

泛型接口的可变性

随后看框架暗中认可接口:

协变:(子类->父类)

IEnumerable<string> list = new List<string>();
IEnumerable<object> list2 = list;

逆变:(父类-> 子类)

IComparable<object> list3 = null;
IComparable<string> list4 = list3;

接下去大家摸索自定泛型接口:

第3定义测试项目、接口:

// 人
public class People
{ }
//老师(继承People[人])
public class Teacher : People
{ }
//运动
public interface IMotion<T>
{ }
//跑步
public class Run<T> : IMotion<T>
{ }

然后大家测试协变性:

威尼斯人线上娱乐 13

同一大家供给把接口 interface
IMotion<T> 定义为 interface IMotion<out T> 

//运动
public interface IMotion<out T>{}

IMotion<Teacher> x = new Run<Teacher>();
IMotion<People> y = x;

假定大家要测试逆变性,则要求把 interface IMotion<T>
 定义为 interface IMotion<in T> 

//运动
public interface IMotion<in T>{}

IMotion<People> x2 = new Run<People>();
IMotion<Teacher> y2 = x2;

泛型接口的逆变,编写翻译后同样进行了强制转换:

威尼斯人线上娱乐 14

自然,咱们也能够从来写成:

IMotion<Teacher> y3 = new Run<People>();

不变性

从地点大家清楚逆变性的代码编写翻译后都会实行强制转换。假设:那大家不用out、in直接手动强制转换是不是能够?:

// 人
public class People { }
//老师(继承People[人])
public class Teacher : People { }
//运动
public interface IMotion<T> { }
//跑步
public class Run<T> : IMotion<T> { }

//协变
IMotion<Teacher> x = new Run<Teacher>();
IMotion<People> y = (IMotion<People>)x;

//逆变
IMotion<People> x2 = new Run<People>();
IMotion<Teacher> y2 = (IMotion<Teacher>)x2;
IMotion<Teacher> y3 = (IMotion<Teacher>)new Run<People>();

天才的本人发现编写翻译成功了,未有其余难点!且还是能而且协变、逆变??不对,真的天才了啊?我们运行试试:

威尼斯人线上娱乐 15

威尼斯人线上娱乐 16

由此看来作者要么太单纯了,要是的确如此简单绕过去,Microsoft又何必去搞个out、in关键字。

对此同二个泛型参数,大家既想有协变性又想逆变性,怎么办?答案是不可行。那就会并发第两种景况,既不得以协变又不可能逆变。称为不变性。

如(大家在IMotion定义三个主意):

//运动
public interface IMotion<T>
{
    T Show();
    void Match(T t);
}

上边我们测试过,代码直接强制转换是不能兑现协变、逆变的。那么我们只能通过out、in来完结。若是前天大家在泛型参数添加out或in属性会怎样?:

威尼斯人线上娱乐 17

威尼斯人线上娱乐 18

咱俩发现out和in都不可能用。在用out时,有个传入参数为泛型 void Match(T
t) 的不贰秘诀。使用in时,有个重临参数为泛型 T
Show() 的方法。未来就出现了是矛更尖锐要么盾更坚硬的题材了。

最终结果是:都无法用,既不可能协变,也无法逆变。此为不变体

小知识:

C#4.0事先 IEnumerable<T> 、 IComparable<T> 、 IQueryable<T> 等接口都不接济可变性,在四.0及之后才支撑。因为四.0事先定义的泛型接口未有添加out、in关键字,有趣味能够切换版本看看。

  先来举个为主的例证,来增加你对可变性的领会。在C#中有隐式类型转换,比如:

四、总结

上述只谈谈了协变与逆变在方式中的情形,其实在性质中状态也相类似,不再表达。
只怕大家也发现了,所谓“协”与“逆”都以只是1种表象,其内在精神为同样进程。
“协变”与“逆变”中的“协”与“逆”表示泛型接口在将品种参数仅用于输入或输出的事态下,其项目参数的隐式转换所遵守的法则。

不变性

从上边大家明白逆变性的代码编译后都会议及展览开强制转换。假设:那大家不用out、in直接手动强制转换是不是能够?:

// 人
public class People { }
//老师(继承People[人])
public class Teacher : People { }
//运动
public interface IMotion<T> { }
//跑步
public class Run<T> : IMotion<T> { }

//协变
IMotion<Teacher> x = new Run<Teacher>();
IMotion<People> y = (IMotion<People>)x;

//逆变
IMotion<People> x2 = new Run<People>();
IMotion<Teacher> y2 = (IMotion<Teacher>)x2;
IMotion<Teacher> y3 = (IMotion<Teacher>)new Run<People>();

天赋的自家意识编写翻译成功了,未有任何难题!且还可以够而且协变、逆变??不对,真的天才了呢?大家运维试试:

威尼斯人线上娱乐 19

威尼斯人线上娱乐 20

如上所述小编大概太单纯了,要是确实如此不难绕过去,Microsoft又何必去搞个out、in关键字。

对此同一个泛型参数,大家既想有协变性又想逆变性,如何是好?答案是不可行。那就会现出第叁种情况,既不得以协变又无法逆变。称为不变性。

如(大家在IMotion定义多个章程):

//运动
public interface IMotion<T>
{
    T Show();
    void Match(T t);
}

上面大家测试过,代码直接强制转换是不能够促成协变、逆变的。那么我们只可以通过out、in来贯彻。假设今天大家在泛型参数添加out或in属性会怎么着?:

威尼斯人线上娱乐 21

威尼斯人线上娱乐 22

作者们发现out和in都不可能用。在用out时,有个传入参数为泛型 void Match(T
t) 的主意。使用in时,有个再次来到参数为泛型 T
Show() 的诀要。现在就涌出了是矛更尖锐抑或盾更坚硬的标题了。

说起底结果是:都不可能用,既不能够协变,也无法逆变。此为不变体

小知识:

C#4.0以前 IEnumerable<T> 、 IComparable<T> 、 IQueryable<T> 等接口都不帮助可变性,在4.0及然后才支撑。因为肆.0在此之前定义的泛型接口未有添加out、in关键字,有趣味能够切换版本看看。

拉开思索

为什么in[输入参数]就不得不逆变?分析如下:

// 人
public class People { }
//老师(继承People[人])
public class Teacher : People
{
    //薪水
    public decimal Salary { get; set; }
}

//运动
public interface IMotion<in T>
{
    void Match(T t);
}
//跑步
public class Run<T> : IMotion<T>
{
    public void Match(T t)
    {
        //假设中间有很多逻辑.....       
    }
}

威尼斯人线上娱乐 23

为什么out[返回值]只得协变?解析如下:

// 人
public class People { }
//老师(继承People[人])
public class Teacher : People
{
    //薪水
    public decimal Salary { get; set; }
}

//运动
public interface IMotion<out T>
{
    T Show();
    //void Match(T t);
}
//跑步
public class Run<T> : IMotion<T>
{
    public T Show()
    {
        return default(T);
    }
    //public void Match(T t)
    //{
    //    //假设中间有很多逻辑.....         
    //}
}

威尼斯人线上娱乐 24

此间有多个关键点:

  • 盛传参数(in)是把参数当成父类来用,明显能够逆变(子类当成父类来用[里氏替换原则]),然则却不能把父类当子类来用(如:子类存在有而父类未有的方式或品质)。
  • 再次来到值(out)重返值类型用父类来接收,明显能够协变(父类能够接收1切子类),但却不可用子类接收父类数据(如:父类代表的靶子不能强制转给子类[string
    str = (string)objcet])。

。。。是还是不是有点越想越迷糊,想不晓得就稳步想。自个儿动动手。

设若实际想的头大,就把它当成是乌龟的臀部(龟腚\规定)吧,知道是C#做的一种安全限制!

  string str = "nibian";
  object str1 = str;
  Console.WriteLine(str1);
协变

当泛型接口类型仅用于出口(使用重要词
out),其种类参数隐式转换所遵从的法则与目的引用的类型转换规律相同,称之为“协变”

延伸思量

为什么in[输入参数]就只能逆变?分析如下:

// 人
public class People { }
//老师(继承People[人])
public class Teacher : People
{
    //薪水
    public decimal Salary { get; set; }
}

//运动
public interface IMotion<in T>
{
    void Match(T t);
}
//跑步
public class Run<T> : IMotion<T>
{
    public void Match(T t)
    {
        //假设中间有很多逻辑.....       
    }
}

威尼斯人线上娱乐 25

为什么out[返回值]唯其如此协变?解析如下:

// 人
public class People { }
//老师(继承People[人])
public class Teacher : People
{
    //薪水
    public decimal Salary { get; set; }
}

//运动
public interface IMotion<out T>
{
    T Show();
    //void Match(T t);
}
//跑步
public class Run<T> : IMotion<T>
{
    public T Show()
    {
        return default(T);
    }
    //public void Match(T t)
    //{
    //    //假设中间有很多逻辑.....         
    //}
}

威尼斯人线上娱乐 26

此地有两个关键点:

  • 盛传参数(in)是把参数当成父类来用,明显能够逆变(子类当成父类来用[里氏替换原则]),可是却无法把父类当子类来用(如:子类存在有而父类未有的格局或品质)。
  • 再次回到值(out)重回值类型用父类来收纳,鲜明能够协变(父类能够接过1切子类),但却不可用子类接收父类数据(如:父类代表的指标不能够强制转给子类[string
    str = (string)objcet])。

。。。是还是不是有点越想越迷糊,想不知底就稳步想。本人动入手。

壹旦实际想的头大,就把它当成是水龟的臀部(龟腚\规定)吧,知道是C#做的壹种安全范围!

总结

关于泛型接口、泛型委托的可变性:

协变 -> 比较和谐健康的扭转 ->
子类转父类 [如 string转object] -> 必须有out标识 [返回值]

逆变 -> 逆天的生成 ->
父类转子类 [如object转string] -> 必须有in标识 [传扬参数]  (老爸变外甥,越活越青春,还不够逆天啊?)

所谓的逆变,会在编写翻译后的C#代码中开始展览强制类型转换。

示例:

  • IEnumerable<string> list
    = new List<string>();  
    IEnumerable<object> list2
    = list; //协变
    IEnumerable<object> list二= new List<string>();
     //(也足以直接写成那样)

  • IComparable<object> list3
    = null;
    IComparable<string> list4
    = list3; //逆变  编译后 [ IComparable<string> list4 = (IComparable<string>) list3;]

注意:

  • 不协助类的花色参数的可变性
  • 唯有泛型接口和泛型委托能够具有可变的品类参数(out、in)
  • 可变性只帮助引用转换。(不能够用来值类型)
  • 种类参数使用了 out 恐怕 ref 将禁止可变性

 

好了,明天就到那里。没啥高深的技能知识,主要为了解协变、逆变、不变体等术语和定义。

本文已联合至索引目录:《C#基础知识巩固》

 

同类小说推荐:

 

  

逆变

当泛型接口类型仅用于输入(使用主要词
in),其系列参数隐式转换所依据的原理与目的引用的类型转换规律相反,称之为“逆变”、“抗变”或“反变”。

总结

至于泛型接口、泛型委托的可变性:

协变 -> 比较和谐健康的变更 ->
子类转父类 [如 string转object] -> 必须有out标识 [返回值]

逆变 -> 逆天的扭转 ->
父类转子类 [威尼斯人线上娱乐 ,如object转string] -> 必须有in标识 [传播参数]  (老爹变孙子,越活越青春,还不够逆天啊?)

所谓的逆变,会在编写翻译后的C#代码中展开强制类型转换。

示例:

  • IEnumerable<string> list =
    new List<string>();  
    IEnumerable<object> list2
    = list; //协变
    IEnumerable<object> list2= new List<string>();
     //(也可以一向写成那样)

  • IComparable<object> list3
    = null;
    IComparable<string> list4
    = list3; //逆变  编译后 [ IComparable<string> list4 = (IComparable<string>) list3;]

注意:

  • 不援助类的类型参数的可变性
  • 只有泛型接口和泛型委托能够具有可变的品种参数(out、in)
  • 可变性只扶助引用转换。(不能够用于值类型)
  • 类型参数使用了 out 或许 ref 将禁止可变性

 

好了,前日就到此地。没啥高深的技艺知识,首要为明白协变、逆变、不变体等术语和定义。

本文已联手至索引目录:《C#基础知识巩固》

 

同类小说推荐:

 

威尼斯人线上娱乐 27

  我们都掌握string类型是object类型的子类型,即string->object为子->父;从子类型到父类型的转换是隐式类型转换。

举个例子:

 interface IDemo<out T>
    {
        T Method(string str);
    }
    class One : IDemo<string>
    {
        public string Method(string str)
        {
            return str;
        }
    }
    class Two : IDemo<object>
    {
        public object Method(string str)
        {
            return str;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            IDemo<string> one = new One();
            IDemo<object> two;
            two = one;
            Console.WriteLine(two.Method("2222"));
        }
    }

  

因为string是object类型的子类,在此地I德姆o<string>和I德姆o<object>类型之间也存在着类型转换的,从I德姆o<string>向I德姆o<object>进行的更换能够当做是子类型向父类型的隐式转换,它的这些用法便是协变。(那里不可不要用out)

反而的大家在来看一下关于逆变的代码:

 interface IDemo<in T>
    {
        string Method(T str);
    }
    class One : IDemo<string>
    {
        public string Method(string str)
        {
            return str;
        }
    }
    class Two : IDemo<object>
    {
        public string Method(object str)
        {
            return str.ToString();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            IDemo<object> two = new Two();
            IDemo<string> one;
            one = two;
            Console.WriteLine(one.Method("2222"));
        }
    }

  同样的从IDemo<object>类型向IDemo<string>类型的转移,然而在此间大家却将父类型隐式转换为子类型的用法叫逆变。(那里不可不要用in)

小结:协变和逆变正是在泛型接口类型将项目参数作为输入和出口的情形下,对品种间实行隐式转换的法则。


相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图