C#基础之泛型

2019-12-30 13:42:19王冬梅

where T : struct 值类型约束,T必须为值类型。

where T:class 引用类型约束,T必须为引用类型。

where T:new() 构造器约束,T必须拥有公共无参构造函数且new()约束放在最后。

where T:U 裸类型约束,T必须是U或派生自U。

where T:BaseClass 基类约束,T必须为BaseClass类或其子类。

where T:Interface 接口约束,T必须为指定的接口或其实现接口。

3.反射创建泛型

  和非泛型类一样,利用反射可以在运行时获取关于泛型类的成员信息。在学习过程我没想到竟然还可以使用反射创建泛型类,更神奇的是还可以在代码里直接写IL指令,代码如下所示。流程上还是那个规则,创建程序集-模块-类-字段和方法,其中最主要的就是Builder结尾的一系列方法。有一个不好理解的地方就是为方法添加方法体,正常的逻辑是直接调用ReturnType的有参构造函数创建List<TName1>对象,可是在.NET里并没有这样的方法,注意这里ReturnType已经是绑定了TName1的List对象而不是普通的List<T>。所以我们需要拿到List<T>这个类型的有参构造函数,它被封装在cInfo对象里,然后再将我们的ReturnType和cInfo关联起来得到List<TName1>的构造函数。除了构造函数中的T需要替换为TName1外,参数IEnumerable<T>中的T也要被替换为TName1,体现在代码里是这一句ienumOf.MakeGenericType(TFromListOf),最后它将随构造函数一起与TName1进行关联。在创建Hello方法我将它设置为静态的,原本我是想设置为实例方法然后调试时去看看是否真的添加了这个方法,不过很奇怪我创建的实例o作为Invoke的参数总是报错提示无法找到方法入口,监视o发现里面根本没有Hello方法,在静态方法下调试也没有从o里看到有关方法的信息,如果读者你对此有自己的想法欢迎留言,如有错误还请指出。


public class BaseClass { }
 public interface IInterfaceA { }
 public interface IInterfaceB { }
 //作为TName1的类型参数
 public class ClassT1 { }
 //作为TName2的类型参数
 public class ClassT2 :BaseClass,IInterfaceA, IInterfaceB { }
 public class ReflectionT
 {
  public void CreateGeneric()
  {
   //创建一个名为”ReflectionT“的动态程序集,这个程序集可以执行和保存。
   AppDomain myDomain = AppDomain.CurrentDomain;
   AssemblyName assemblyName = new AssemblyName("ReflectionT");
   AssemblyBuilder assemblyBuilder = myDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
   //在这个程序集中创建一个与程序集名相同的模块,接着创建一个类MyClass。
   ModuleBuilder moudleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, assemblyName.Name + ".dll");
   TypeBuilder myType = moudleBuilder.DefineType("MyClass", TypeAttributes.Public);
   //创建类型参数名,将达到这样的效果:public MyClass<TParam1,TParam2>
   string[] tNames = { "TName1", "TName2" };
   GenericTypeParameterBuilder[] gtps = myType.DefineGenericParameters(tNames);
   GenericTypeParameterBuilder tName1 = gtps[0];
   GenericTypeParameterBuilder tName2 = gtps[1];
   //为泛型添加约束,TName1将会被添加构造器约束和引用类型约束
   tName1.SetGenericParameterAttributes(GenericParameterAttributes.DefaultConstructorConstraint | GenericParameterAttributes.ReferenceTypeConstraint);
   //TName2达到的效果将是:where TName2:ValueType,IComparable,IEnumerable
   Type baseType = typeof(BaseClass);
   Type interfaceA = typeof(IInterfaceA);
   Type interfaceB = typeof(IInterfaceA);
   Type[] interfaceTypes = { interfaceA, interfaceB };
   tName2.SetBaseTypeConstraint(baseType);
   tName2.SetInterfaceConstraints(interfaceTypes);
   /*为泛型类MyClass添加字段:
   private string name;
   public TName1 tField1;
    */
   FieldBuilder fieldBuilder = myType.DefineField("name", typeof(string), FieldAttributes.Public);
   FieldBuilder fieldBuilder2 = myType.DefineField("tField1", tName1, FieldAttributes.Public);
   //为泛型类添加方法Hello
   Type listType = typeof(List<>);
   Type ReturnType = listType.MakeGenericType(tName1);
   Type[] parameter = { tName1.MakeArrayType() };
   MethodBuilder methodBuilder = myType.DefineMethod(
    "Hello",        //方法名
    MethodAttributes.Public | MethodAttributes.Static, //指定方法的属性
    ReturnType,      //方法的放回类型
    parameter);       //方法的参数
   //为方法添加方法体
   Type ienumOf = typeof(IEnumerable<>);
   Type TFromListOf = listType.GetGenericArguments()[0];
   Type ienumOfT = ienumOf.MakeGenericType(TFromListOf);
   Type[] ctorArgs = { ienumOfT };
   ConstructorInfo cInfo = listType.GetConstructor(ctorArgs);
   //最终的目的是要调用List<TName1>的构造函数 : new List<TName1>(IEnumerable<TName1>);
   ConstructorInfo ctor = TypeBuilder.GetConstructor(ReturnType, cInfo);
   //设置IL指令
   ILGenerator msil = methodBuilder.GetILGenerator();
   msil.Emit(OpCodes.Ldarg_0);
   msil.Emit(OpCodes.Newobj, ctor);
   msil.Emit(OpCodes.Ret);
   //创建并保存程序集
   Type finished = myType.CreateType();
   assemblyBuilder.Save(assemblyName.Name + ".dll");
   //创建这个MyClass这个类
   Type[] typeArgs = { typeof(ClassT1), typeof(ClassT2) };
   Type constructed = finished.MakeGenericType(typeArgs);
   object o = Activator.CreateInstance(constructed);
   MethodInfo mi = constructed.GetMethod("Hello");
   ClassT1[] inputParameter = { new ClassT1(), new ClassT1() };
   object[] arguments = { inputParameter };
   List<ClassT1> listResult = (List<ClassT1>)mi.Invoke(null, arguments);
   //查看返回结果中的数量和完全限定名
   Console.WriteLine(listResult.Count);
   Console.WriteLine(listResult[0].GetType().FullName);
   //查看类型参数以及约束
   foreach (Type t in finished.GetGenericArguments())
   {
    Console.WriteLine(t.ToString());
    foreach (Type c in t.GetGenericParameterConstraints())
    {
     Console.WriteLine(" "+c.ToString());
    }
   }
  }
 }