委托协变用out修饰返回类型,使func可赋值给func;逆变用in修饰参数类型,使action可接收action;二者均需泛型参数仅出现在对应位置且满足类型安全约束。

委托协变:用 out 修饰返回类型,允许子类转父类
协变发生在委托的返回值上。当你声明一个返回 string 的委托,却想用它指向返回 object 的方法时,编译器会报错——除非你在泛型参数上加 out。因为 string 是 object 的子类,而“更具体的类型能安全当更宽泛的类型用”就是协变的本质。
典型场景是统一处理不同具体类型的工厂或转换逻辑:
-
Func<t></t>内置委托本身就用了out T,所以Func<string></string>可赋值给Func<object></object> - 自定义委托必须显式加
out,且该参数只能出现在返回位置(不能作参数、不能在泛型约束中被用作基类) - 错误写法:
public delegate T MyFunc<t>() where T : class</t>—— 没有out就不支持协变,MyFunc<string></string>无法转成MyFunc<object></object>
委托逆变:用 in 修饰参数类型,允许父类转子类
逆变作用于委托的输入参数。比如你有一个接受 object 的委托,但实际想传入只处理 string 的方法——这看似危险,实则安全:只要方法能处理更宽泛的输入,那传更具体的子类当然没问题。这时就得靠 in。
常见于事件处理器、比较器、谓词等以输入为主的操作:
-
Action<t></t>和Predicate<t></t>都用了in T,所以Action<object></object>可接收Action<string></string> -
in参数只能出现在形参位置,不能用于返回值、不能参与where T : SomeBase这类约束(否则协变/逆变会被禁用) - 若委托同时有输入和输出,可混合使用:
Func<in t out r></in>,.NET 中的Func<t tresult></t>就是这样设计的
为什么加了 in/out 还编译失败?常见限制条件
协变和逆变不是万能的,C# 对其施加了严格的类型安全性检查。即使语法正确,以下情况仍会拒绝转换:
- 泛型参数出现在「非协变/逆变位置」:比如
out T却在方法体内把T当字段类型存储,或作为ref/out参数传递 - 存在装箱/拆箱隐式转换干扰:比如
Func<int></int>不能转成Func<object></object>,因为int是值类型,object是引用类型,这不是继承关系而是装箱,out不覆盖此规则 - 委托签名不匹配:协变只影响返回类型,参数数量、顺序、名称、是否
params等仍需完全一致 - 跨程序集时,目标框架版本太低(如 .NET Framework 4.0 之前不支持泛型变体)
实际调试时怎么快速判断该用 in 还是 out?
别记规则,看数据流向。打开你的委托定义,盯住那个泛型参数 T:
- 如果
T只出现在返回值位置(或yield return、get访问器),就用out T - 如果
T只出现在参数位置(包括ref以外的普通参数、params T[]),就用in T - 如果
T同时出现在参数和返回值里(比如T DoSomething(T input)),那它既不能协变也不能逆变,不能加任何修饰符 - VS 提示 “cannot convert … because the type parameter … is not valid variance position” 就说明你放错地方了
真正容易被忽略的是:变体只对泛型委托类型生效,对具体委托实例(如 new Func<string>(...)</string>)没意义;而且一旦用了 in/out,该参数在委托体内就变成只读(协变返回值不可作为输入,逆变参数不可作为返回),这是编译器强制的保护机制。









