我们再改动下IBar,发现出现另外一处编译失败
interface IBar<in T> { void GetValue(T t);//编译成功 //T GetValue(T t);//编译失败 T不能作为返回值输出,用in约束T支持逆变,T可以作为返回值输出 } IBar<FooBase> bar = new Bar<FooBase>(); IBar<Foo> bar1 = (IBar<Foo>)bar;//编译通过,运行时不报错 IBar<Foo> bar1 = bar;//编译通过,运行时不报错因此我们可以得出以下结论:
由于FooBase是Foo的父类,并不包含子类的自由的成员,转为为子类Foo是类型不安全的,因此在运行时强式转换的报错了,但编译期是不能够确认的
在为泛型接口用in标识其类型参数支持逆变后,in约束其接口成员不能将其作为返回值(输出值),我们会发现协变和逆变正是一对反义词
这里提一句,值类型是不支持协变和逆变的
同样的泛型委托Action就是个逆变的例子:
public delegate void Action<in T>(T obj); 五.泛型的反射我们先来看看以下代码:
static void Main(string[] args) { var lsInt = new ArrayExpandable<int>(); lsInt.Add(1); var lsStr = new ArrayExpandable<string>(); lsStr.Add("ryzen"); var lsStr1 = new ArrayExpandable<string>(); lsStr.Add("ryzen"); }然后通过ildasm查看其IL,开启视图-》显示标记值,查看Main方法:
void Main(string[] args) cil managed { .entrypoint // 代码大小 52 (0x34) .maxstack 2 .locals /*11000001*/ init (class MetaTest.ArrayExpandable`1/*02000003*/<int32> V_0, class MetaTest.ArrayExpandable`1/*02000003*/<string> V_1, class MetaTest.ArrayExpandable`1/*02000003*/<string> V_2) IL_0000: nop IL_0001: newobj instance void class MetaTest.ArrayExpandable`1/*02000003*/<int32>/*1B000001*/::.ctor() /* 0A00000C */ IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldc.i4.1 IL_0009: callvirt instance void class MetaTest.ArrayExpandable`1/*02000003*/<int32>/*1B000001*/::Add(!0) /* 0A00000D */ IL_000e: nop IL_000f: newobj instance void class MetaTest.ArrayExpandable`1/*02000003*/<string>/*1B000002*/::.ctor() /* 0A00000E */ IL_0014: stloc.1 IL_0015: ldloc.1 IL_0016: ldstr "ryzen" /* 70000001 */ IL_001b: callvirt instance void class MetaTest.ArrayExpandable`1/*02000003*/<string>/*1B000002*/::Add(!0) /* 0A00000F */ IL_0020: nop IL_0021: newobj instance void class MetaTest.ArrayExpandable`1/*02000003*/<string>/*1B000002*/::.ctor() /* 0A00000E */ IL_0026: stloc.2 IL_0027: ldloc.1 IL_0028: ldstr "ryzen" /* 70000001 */ IL_002d: callvirt instance void class MetaTest.ArrayExpandable`1/*02000003*/<string>/*1B000002*/::Add(!0) /* 0A00000F */ IL_0032: nop IL_0033: ret } // end of method Program::Main打开元数据表将上面所涉及到的元数据定义表和类型规格表列出:
metainfo:
-----------定义部分 TypeDef #2 (02000003) ------------------------------------------------------- TypDefName: MetaTest.ArrayExpandable`1 (02000003) Flags : [Public] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit] (00100001) Extends : 0100000C [TypeRef] System.Object 1 Generic Parameters (0) GenericParamToken : (2a000001) Name : T flags: 00000000 Owner: 02000003 Method #8 (0600000a) ------------------------------------------------------- MethodName: Add (0600000A) Flags : [Public] [HideBySig] [ReuseSlot] (00000086) RVA : 0x000021f4 ImplFlags : [IL] [Managed] (00000000) CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: Var!0 1 Parameters (1) ParamToken : (08000007) Name : value flags: [none] (00000000) ------类型规格部分 TypeSpec #1 (1b000001) ------------------------------------------------------- TypeSpec : GenericInst Class MetaTest.ArrayExpandable`1< I4> //14代表int32 MemberRef #1 (0a00000c) ------------------------------------------------------- Member: (0a00000c) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. MemberRef #2 (0a00000d) ------------------------------------------------------- Member: (0a00000d) Add: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: Var!0 TypeSpec #2 (1b000002) ------------------------------------------------------- TypeSpec : GenericInst Class MetaTest.ArrayExpandable`1< String> MemberRef #1 (0a00000e) ------------------------------------------------------- Member: (0a00000e) .ctor: CallCnvntn: [DEFAULT] hasThis ReturnType: Void No arguments. MemberRef #2 (0a00000f) ------------------------------------------------------- Member: (0a00000f) Add: CallCnvntn: [DEFAULT] hasThis ReturnType: Void 1 Arguments Argument #1: Var!0这时候我们就可以看出,元数据为泛型类ArrayExpandable<T>定义一份定义表,生成两份规格,也就是当你实例化类型参数为int和string的时候,分别生成了两份规格代码,同时还发现以下的现象:
var lsInt = new ArrayExpandable<int>();//引用的是类型规格1b000001的成员0a00000c .ctor构造 lsInt.Add(1);//引用的是类型规格1b000001的成员0a00000d Add var lsStr = new ArrayExpandable<string>();//引用的是类型规格1b000002的成员0a00000e .ctor构造 lsStr.Add("ryzen");//引用的是类型规格1b000002的成员0a00000f Add var lsStr1 = new ArrayExpandable<string>();//和lsStr一样 lsStr.Add("ryzen");//和lsStr一样