澳门皇冠官网app接着我们团结一心定义委托试试

不变性

从下面大家了解逆变性的代码编译后都会展开强制转换。假设:这我们不用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>();

天才的自家意识编译成功了,没有此外问题!且还是可以够而且协变、逆变??不对,真的天才了呢?大家运行试试:

澳门皇冠官网app 1

澳门皇冠官网app 2

看来我或者太单纯了,假诺确实如此容易绕过去,Microsoft又何必去搞个out、in关键字。

对于同一个泛型参数,大家既想有协变性又想逆变性,如何做?答案是不可行。这就会并发第两种状态,既不得以协变又不得以逆变。称为不变性。

如(大家在IMotion定义五个方法):

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

地点我们测试过,代码直接强制转换是不可以实现协变、逆变的。那么我们不得不通过out、in来贯彻。假诺现在我们在泛型参数添加out或in属性会怎么?:

澳门皇冠官网app 3

澳门皇冠官网app 4

俺们发现out和in都无法用。在用out时,有个传入参数为泛型 void Match(T
t) 的方法。使用in时,有个重返参数为泛型 T
Show() 的办法。现在就应运而生了是矛更尖锐抑或盾更坚硬的题材了。

末段结果是:都不可以用,既不可以协变,也不可能逆变。此为不变体

小知识:

C#4.0事先 IEnumerable<T> 、 IComparable<T> 、 IQueryable<T> 等接口都不协助可变性,在4.0及然后才支撑。因为4.0从前定义的泛型接口没有添加out、in关键字,有趣味可以切换版本看看。

不变性

从下边大家领略逆变性的代码编译后都会进展强制转换。假设:那大家不用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>();

天才的自我发觉编译成功了,没有另外问题!且仍是可以够而且协变、逆变??不对,真的天才了吧?我们运行试试:

澳门皇冠官网app 5

澳门皇冠官网app 6

由此看来我或者太单纯了,尽管的确这么容易绕过去,Microsoft又何必去搞个out、in关键字。

对于同一个泛型参数,大家既想有协变性又想逆变性,肿么办?答案是不可行。这就会并发第二种情景,既不得以协变又不得以逆变。称为不变性。

如(大家在IMotion定义三个办法):

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

上面我们测试过,代码直接强制转换是不可以实现协变、逆变的。那么大家只可以通过out、in来落实。要是今日大家在泛型参数添加out或in属性会怎么着?:

澳门皇冠官网app 7

澳门皇冠官网app 8

大家发现out和in都不可以用。在用out时,有个传入参数为泛型 void Match(T
t) 的法子。使用in时,有个再次回到参数为泛型 T
Show() 的办法。现在就涌出了是矛更尖锐或者盾更坚硬的题材了。

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

小知识:

C#4.0事先 IEnumerable<T> 、 IComparable<T> 、 IQueryable<T> 等接口都不帮助可变性,在4.0及今后才支撑。因为4.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)
    {
        //假设中间有很多逻辑.....       
    }
}

澳门皇冠官网app 9

为什么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)
    //{
    //    //假设中间有很多逻辑.....         
    //}
}

澳门皇冠官网app 10

此处有多个关键点:

  • 传扬参数(in)是把参数当成父类来用,分明能够逆变(子类当成父类来用[里氏替换原则]),然则却不能够把父类当子类来用(如:子类存在有而父类没有的法门或性质)。
  • 再次来到值(out)再次来到值类型用父类来收取,明显可以协变(父类可以吸收一切子类),但却不可用子类接收父类数据(如:父类代表的靶子不可能强制转给子类[string
    str = (string)objcet])。

。。。是不是有点越想越迷糊,想不知底就渐渐想。自己动出手。

倘若实际想的头大,就把它正是是乌龟的臀部(龟腚\规定)吧,知道是C#做的一种安全范围!

泛型委托的可变性

先使用框架定义的泛型委托Func和Action做例子(不了然的请戳

协变:(string->object)

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

逆变:(object->string)

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

地方代码没有其他问题。

接着我们自己定义委托试试:

澳门皇冠官网app 11

我X,看人不来哦。为啥自定义的寄托却不可以协变呢。

本身看看系统定义的Func到底和大家自定义的有什么样不同:

public delegate TResult Func<out TResult>();

多了一个out,什么鬼:

  • out:对于泛型类型参数,out 关键字指定该项目参数是协变的。 可以在泛型接口和寄托中使用 out 关键字。(来源
  • in:对于泛型类型参数,in 关键字指定该类型参数是逆变的。 能够在泛型接口和嘱托中行使 in 关键字。(来源

那就是说我们得以修改自定义委托:

澳门皇冠官网app 12

完美!

这假如我们要实现逆变性呢:

澳门皇冠官网app 13

直白逆变是不可行的,我们需要修改泛型类型参数:

澳门皇冠官网app 14

俺们发现一切信托参数都变了。本来的再次回到值,改成输入参数才行。

结论:

  • 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#代码:

澳门皇冠官网app 15

结论:

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

如上代码也得以一贯写成:

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

总结

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

协变 -> 相比和谐健康的扭转 ->
子类转父类 [如 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#基础知识巩固

 

同类小说推荐:

http://www.cnblogs.com/haoyifei/p/5760959.html

http://www.cnblogs.com/LoveJenny/archive/2012/03/13/2392747.html

http://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.html

 

泛型接口的可变性

紧接着看框架默认接口:

协变:(子类->父类)

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>
{ }

下一场我们测试协变性:

澳门皇冠官网app 16

相同我们需要把接口 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;

泛型接口的逆变,编译后一样举办了挟持转换:

澳门皇冠官网app 17

理所当然,大家也得以直接写成:

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

泛型委托的可变性

澳门皇冠官网app,先使用框架定义的泛型委托Func和Action做例子(不打听的请戳

协变:(string->object)

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

逆变:(object->string)

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

地点代码没有此外问题。

跟着我们自己定义委托试试:

澳门皇冠官网app 18

我X,看人不来哦。为啥自定义的信托却不可以协变呢。

自我看看系统定义的Func到底和大家自定义的有什么两样:

public delegate TResult Func<out TResult>();

多了一个out,什么鬼:

  • out:对于泛型类型参数,out 关键字指定该品种参数是协变的。 可以在泛型接口和寄托中接纳 out 关键字。(来源
  • in:对于泛型类型参数,in 关键字指定该品种参数是逆变的。 能够在泛型接口和委托中使用 in 关键字。(来源

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

澳门皇冠官网app 19

完美!

这假诺我们要贯彻逆变性呢:

澳门皇冠官网app 20

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

澳门皇冠官网app 21

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

结论:

  • 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#代码:

澳门皇冠官网app 22

结论:

  • 所谓的逆变其实只是编译后举行了威迫类型转换而已。

上述代码也足以直接写成:

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

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

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

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

延长思考

为什么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)
    {
        //假设中间有很多逻辑.....       
    }
}

澳门皇冠官网app 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)
    //{
    //    //假设中间有很多逻辑.....         
    //}
}

澳门皇冠官网app 24

这里有五个关键点:

  • 传播参数(in)是把参数当成父类来用,显明能够逆变(子类当成父类来用[里氏替换原则]),不过却不得以把父类当子类来用(如:子类存在有而父类没有的措施或性能)。
  • 再次回到值(out)再次来到值类型用父类来接过,分明可以协变(父类可以接到一切子类),但却不可用子类接收父类数据(如:父类代表的对象无法强制转给子类[string
    str = (string)objcet])。

。。。是不是有点越想越迷糊,想不知情就渐渐想。自己动动手。

倘若实在想的头大,就把它正是是乌龟的臀部(龟腚\规定)吧,知道是C#做的一种安全范围!

泛型接口的可变性

继之看框架默认接口:

协变:(子类->父类)

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>
{ }

下一场我们测试协变性:

澳门皇冠官网app 25

一如既往大家需要把接口 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;

泛型接口的逆变,编译后同样举办了强制转换:

澳门皇冠官网app 26

理所当然,我们也足以一直写成:

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

总结

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

协变 -> 相比和谐健康的生成 ->
子类转父类 [如 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#基础知识巩固

 

同类小说援引:

http://www.cnblogs.com/haoyifei/p/5760959.html

http://www.cnblogs.com/LoveJenny/archive/2012/03/13/2392747.html

http://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.html

 

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

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

相关文章