文章目录
- 一、知识回顾和问题分析
- 二、为什么泛型委托不行?
-
- 1、泛型类型的严格类型检查
- [2、**`as` 和强制类型转换不能直接使用**](#2、
as
和强制类型转换不能直接使用)
- 三、如何解决这个问题?
- 四、**应用场景**
- 五、总结
- 六、系统泛型委托和协变、逆变
- 专栏推荐
- 完结
一、知识回顾和问题分析
1、回顾强制转换
和as
转换知识
前面我们已经学过强制转换
和as
转换
- 强制类型转换((T)):用于将一个对象显式地转换为指定的类型。如果转换不成功,通常会抛出 InvalidCastException。
- as 操作符:用于尝试将对象转换为指定类型。如果转换失败,返回 null,而不是抛出异常。
csharp
//注意Dog是Animal的子类
Animal animal = new Dog();
//强制类型转换
Dog dog = (Dog)animal;
//as 操作符
Dog dog = animal as Dog;
2、问题分析
根据里氏替换原则:可以用子类替换掉父类
。
前面我们学习了泛型,那我们可不可以使用强制类型转换
或者as
操作符将泛型委托从一个类型转换到另一个类型?
答案是不行的
。
二、为什么泛型委托不行?
1、泛型类型的严格类型检查
在 C# 中,泛型类型是 严格的类型检查
,意味着即使存在继承关系(比如 Son 继承自 Father),泛型类型参数 也被视为不同的类型。也就是说,Produce<Father>
和 Produce<Son>
被认为是两个 不同的类型
,即便 Son 是 Father 的子类。
举例:
csharp
public delegate T Produce<T>();
Produce<Son> produceSon = () => new Son();
Produce<Father> produceFather = produceSon; // 编译错误
编译器报错的原因是 泛型类型 Produce<T>
在内部对类型参数进行检查,不会因为 Son 是 Father 的子类而认为 Produce<Son>
可以转换为 Produce<Father>
。
2、as
和强制类型转换不能直接使用
as
操作符和强制类型转换((T)
)只对相同的类型 或派生类型 有效,泛型类型是严格类型检查
的,所以不行。
例如:
csharp
Produce<Father> produceFather = produceSon as Produce<Father>; // 编译错误
因为 Produce<Son>
和 Produce<Father>
不是相同的类型,produceSon
和 produceFather
无法通过 as
或强制类型转换直接转换。
三、如何解决这个问题?
为了处理这种类型严格检查的问题,C# 引入了协变
和逆变
,通过这些机制我们可以在泛型中更好地处理继承关系,使得泛型类型可以遵循里氏替换原则
。
通过使用 out
(协变)和 in
(逆变)修饰符,C# 显式支持泛型委托的类型转换规则,从而允许不同类型之间的转换,且不破坏类型安全。
1、协变(out
)
通过协变(out
关键字),你可以让泛型委托支持返回值
的类型转换:
csharp
public delegate T Produce<out T>();
Produce<Son> produceSon = () => new Son();
Produce<Father> produceFather = produceSon; // 现在是合法的,协变支持
Father father = produceFather(); // 实际返回的是 Son 类型的对象
这里的关键是 Produce<T>
泛型委托的类型参数 T
使用了 out
关键字,这表示这个委托仅作为返回值使用(即输出类型)。
协变的原理是,如果 Son
是 Father
的子类,那么 Produce<Son>
可以被视作 Produce<Father>
,从而实现类型转换。
2、逆变(in
)
通过逆变(in
关键字),你可以让泛型委托支持输入参数
的类型转换:
csharp
public delegate void Consume<in T>(T item);
Consume<Father> consumeFather = item => Console.WriteLine("Consuming Father");
Consume<Son> consumeSon = consumeFather; // 逆变支持,合法
consumeSon(new Son()); // 实际调用的是 consumeFather
在这个例子中,Consume<T>
泛型委托的类型参数 T
使用了 in
关键字,表示它仅作为输入参数使用。
逆变的原理是,如果 Son
是 Father
的子类,那么 Consume<Father>
可以被视作 Consume<Son>
,从而实现类型转换。
四、应用场景
协变和逆变主要用于泛型接口
、泛型委托
的类型约束中,可以有效提高代码的灵活性和类型安全性。
五、总结
为了使泛型类型可以遵循里氏替换原则
,C# 引入了协变
和逆变
,通过这些机制我们可以在泛型中更好地处理继承关系。
out
和 in
的引入正是为了 安全地实现类型转换 ,而不会导致不兼容类型之间的隐式转换问题。在没有 out
或 in
的情况下,泛型类型参数的转换是不允许的。
六、系统泛型委托和协变、逆变
在 C# 的实际开发中,我们并不需要手动使用 out 和 in 来实现协变和逆变,因为 Action<>
和 Func<>
这些常用的系统泛型委托
,C# 编译器已经默认对输入参数和返回值进行了协变和逆变的处理。
这里主要是要理解out和in的原理,和意义,可能在面试题中会被问到原理性的知识。
专栏推荐
地址 |
---|
【从零开始入门unity游戏开发之------C#篇】 |
【从零开始入门unity游戏开发之------unity篇】 |
【制作100个Unity游戏】 |
【推荐100个unity插件】 |
【实现100个unity特效】 |
【unity框架开发】 |
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~