泛型和反射

更新 :2018-11-4 

获取 namepsace 下的所有 class

 var types = Assembly.GetExecutingAssembly().GetTypes().ToList().Where(t => t.Namespace == "Project.Model");

 

 

更新 2018-10-29

没想到这么多年后既然还能更新 .... 

这几年都没有写底层的代码,都是忙于业务层,很多语法侧面知识后来用的时候才比较清楚.

今天在来谈谈反射, 我就不看之前写过什么了啦. 

想要动态调用代码就要通过反射. 

比如原本我们写个调数据的方法

var datas = await Db.Set<Entity.Blog>().ToListAsync();

多简单,就一行..但如果我说 Entity.Blog 是运行时才能确定的呢 ?

那就要反射了.

反射第一步骤是找到 type 

一般上就 2 个方式, typeof(Class), instance.GetType()

var type = typeof(ApplicationDbContext);

然后我们还需要找到 Set<T> 这个方法

var dbSetMethod = type.GetMethod(
    name : nameof(ApplicationDbContext.Set), 
    genericParameterCount : 1,
    types : new Type[] { } // 这个是 params 的类型
);

通过方法名字, params 的数量和类型, 加上 generic 来定位, (如果 generic 多,而且 param type 又涉及到 generic 就不行了,解决方法就是直接把所有 methods 调出来,自己做 filter)

然后 set generic 和调用, 

dbSetMethod.MakeGenericMethod(new Type[] { 放入 runtime 的 ClassType });
dbSetMethod.Invoke(Db, new object[] { }); // 传入 params, 没有就传空 array

到这里我们只写了一半

Db.Set<Entity.Blog>()

接着写 await ToListAsync

ToListAsync 是一个 extension 方法, 是静态方法来的.

一样,先找到 Type 然后找方法出来

var toListAsyncMethod = typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ToListAsync)); //刚巧它只有一个,所以直接用名字就可以了..

set generic

  toListAsyncMethod = toListAsyncMethod.MakeGenericMethod(new Type[] {  runtime Type 放这里  });

最后就是调用,有一点要注意. 

反射 只有 Invoke, 没有 InvokeAsync

所以要调用一个 async 方法的话,我们需要强转. 

比如   

await (Task)method.Invoke(obj, new [] { 'params' } )

string value= await (Task<string>) method.Invoke(obj, new [] { 'params' } ); 如果知道返回类型的话. 

如果你不知道返回类型, 就要用 dynamic 了.

string value = await (dynamic) method.Invoke(obj, new [] { 'params' } ); 

所以最后就是 

IList list = await (dynamic)toListAsyncMethod.Invoke(null, new object[] { dbSet, null }); // 第2个 params 是 CancellationToken, 没有也要放 null 哦
foreach (var data in list)
{
  // data type is object 
}

 

 

 

泛型和反射经常是一起工作的,所以就一次介绍吧.

 

c# 是强类型语言,一般上函数的返回类型和参数的类型都是一早些好的,也就造成了很多时候不像js那样方便使用,不灵话。

所以呢就有了这个泛型,它可以让你的函数和参数在调用的时候才决定类型。

    public T abc<T>(T word)
    {
        return word;
        return default(T); //关键字default可以对引用类型返回nullAble,int类型返回0,初始化一个T的感觉啦
     //return (T)Convert.ChangeType(obj1, typeof(T)); 强转
} abc<string>("x"); //struct 是 值类型 //好处调用的是如果参数是值类型可以不用著名 test(100) 而不需要 test<int>(100); public void test<T>(T number) where T : struct { int z = Convert.ToInt32(number); //调用 test(100); } //下面的不知道好处在哪用在什么地方, public void test2<T>(T lei) where T : class { } public void test3<T>() where T : stooges { } public T test4<T>() where T : new() { T abc = new T(); return abc; } public class stooges { }

加了where 我就不清楚在什么地方用的上了,这个以后再研究

 

反射能让我们的代码运行时动态的获取一些对象或者类的属性值等等,甚至是调用它们。

先来一个常用到的,我们想获取一个对象的全部属性和值, 用js 是 for(var attr in object) { object[attr]=value, attr = attr } 

            var obj = new abc();
            Type T = typeof(abc); //typeof(Class) 而不是 typeof(object) 哦
            Type V = obj.GetType(); //obj.GetType() 就是typeof(object的class)
            PropertyInfo[] attrs = obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); //获取attrs
            foreach (PropertyInfo attr in attrs) 
            {
                string key = attr.Name;  //获取attr name
                object value = attr.GetValue(obj, null); //获取value    
Type type = attr.PropertyType; //类型 }

关键就是那个 Type , 获取Type后就可以做很多了

常用的方法

        T.GetProperty("key").GetValue(obj, null); //read a key value
        T.GetProperty("key").SetValue(obj, "", null); //write a value to key
        //注意如果是字典 
        T.GetProperty("Item").GetValue(obj, new [] {"id"}); //先拿Item 然后才通过 new[] {这里放指定的key}

 

 

再来看看更详细的

    class MyClass
    {
        public int x { get; set; }
        public int y { get; set; }
        public MyClass(int i)
        {
            x = y + i;
        }
        public MyClass(int i, int j)
        {
            x = i;
            y = j;
        }
        public int sum()
        {
            return x + y;
        }
    }

 

 我们想获取这个Class 的构造函数 

            Type t = typeof(MyClass);         
            ConstructorInfo[] constructors = t.GetConstructors();  //使用这个方法获取构造函数列表       
            for (int i = 0; i < constructors.Length; i++)
            {
                ConstructorInfo constructor = constructors[i];
                //构造函数也是方法所以有 GetParameters
                ParameterInfo[] parameters = constructor.GetParameters(); //获取当前构造函数的参数列表
                string paraTypeName = parameters[0].ParameterType.Name; //方法的参数类型名称
                string paraName = parameters[0].Name;// 方法的参数名                 
            }          
            //调用构造函数
            object[] args = new object[2];
            args[0] = 10;
            args[1] = 20;
            //不用new 直接实例化
            object instance = constructors[0].Invoke(args);  //实例化一个这个构造函数有两个参数的类型对象,如果参数为空,则为null
object instance = (t)Activator.CreateInstance(t); 还有这种实例的方法,不清楚可以放参数没有

 

调用方法

MethodInfo[] methods = T.GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance);
foreach (MethodInfo method in methods)
{
    string return_name = method.ReturnType.Name; //返回方法的返回类型
    string name = method.Name;
    if (name.Equals("sum", StringComparison.Ordinal)) //指定方法名调用
    {
        int value = (int)method.Invoke(instance, new object[] { 5, "x" });  //instance是之前实例好的对象,方法就是在这个对象之中, parameters 随便放
    }
}

 

 有时想知道一个类是不是接口,或者是否实现了某个接口可以这样子

    public interface IgetStringAble
    {
        string getString();
    }
    public class someClass : IgetStringAble
    {
        public string getString()
        {
            return "";
        }
    }
    protected void Page_Load(object sender, EventArgs e)
    {
        var obj = new someClass();
        Type type = obj.GetType();
        Type interface_ = typeof(IgetStringAble);
        bool is_interface = interface_.IsInterface; //还有很多类型可以is的
        bool isThisImplementsThatInterface = interface_.IsAssignableFrom(type); //这里interface要放前面哦  
    }

下面是一些参考

        Type t = typeof(MyClass);
        Console.WriteLine("----------------Method------------------");
        MethodInfo[] methods = t.GetMethods();
        foreach (MethodInfo method in methods)
        {
            Console.WriteLine("Method:" + method);
            //Console.WriteLine(method); 
            //Console.WriteLine("返回值:" + method.ReturnParameter); 
        }
        Console.WriteLine("---------------Field-------------------"); //字段 ,比如这种  private static string name; 
        FieldInfo[] fields = t.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
        foreach (FieldInfo field in fields)
        {
            Console.WriteLine("Field:" + field);
        }
        Console.WriteLine("--------------Member--------------------"); //成员即方法和属性
        MemberInfo[] members = t.GetMembers();
        foreach (MemberInfo member in members)
        {
            Console.WriteLine("Member:" + member);
        }
        Console.WriteLine("--------------Property--------------------"); //属性
        PropertyInfo[] properties = t.GetProperties();
        foreach (PropertyInfo property in properties)
        {
            Console.WriteLine("Property:" + property);
        }
        Console.WriteLine("--------------Constructor--------------------"); //构造函数
        ConstructorInfo[] constructors = t.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
        foreach (ConstructorInfo constructor in constructors)
        {
            Console.WriteLine("Constructor:" + constructor);
        }     

 

调用不用记太清楚,主要知道什么东西可以用反射获取和调用就可以了。

比较长使用的地方是用泛型写方法的时候,比如我们的泛型是一个不确定的类,然后我们要获取attr值等等的,就很好可以使用反射了。 

还有通过string来调用方法,这个在js很长用到,比如 obj["methodName"](); 这里也可以用到反射来实现。

p.s : 反射的性能是很慢的,也可以说动态就是慢,这个很正常啦,反射的性能优化可以参考使用动态编程之类的,不过这里就不提先啦。

 

反射-泛型-扩展方法 小笔记

    public class Abcd
    {

    }
    public static class Exten
    {
        public static string getxx<T>(this Abcd obj, Func<T, bool> deg)
        {
            return "xx";
        }
        public static string getxx<T>(this Abcd obj, Func<T, bool, int> deg)
        {
            return "xx";
        }
    }
    Type typeAbcd = typeof(Abcd);
    Type typeExten = typeof(Exten);

    IEnumerable<MethodInfo> methods = typeExten.GetMethods().Where(m => m.Name == "getxx"); 
    //当方法重载的时候,或许我们得判断它们的泛型来选择我们要的方法
    foreach (MethodInfo method in methods)
    {
        //一个方法理应先注入类型才能调用
        //注入泛型的类型,一次可以多个,将返回新method
        MethodInfo newMethod = method.MakeGenericMethod(new Type[] { typeof(string) });
        //获取泛型的类型,如果还没有注入的话,这里拿到的Type Name 将会是我们匿名的名字,比如 "T"           
        IEnumerable<Type> args = method.GetParameters()[1].ParameterType.GetGenericArguments(); 
    }
    Type func = typeof(Func<>);
    func.MakeGenericType(new Type[] { typeof(string), typeof(bool) }); //注入类型可以这样
            
    //最终调用:
    MethodInfo finalMethod = methods.First().MakeGenericMethod(typeof(string));
    Func<string,bool> xzzz = delegate(string a) { return true; };
    //记住哦这是个静态方法,所以第一个参数是null,params的第一个才是实例.
    finalMethod.Invoke(null, new object[] { new Abcd(), xzzz });

注:

-当有重载方法是可能需要通过它们的泛型来做选择

-方法有泛型必须先注入好类型才能调用.

-获取还没注入的泛型,返回Type,但是name是我们的匿名 比如 : <TGG> 的话就是 "TGG"

-扩展方法调用时,实例是放进第二个参数里的.

 

用 string 从 Assembly 获取到Class

/*DLL Reflection */
//get running assembly
Assembly ass1 = Assembly.GetExecutingAssembly(); 
//load dll , web config need got reference 
//get inside appcode .cs Class (appcode maybe no need set config)
Assembly ass2 = Assembly.Load("App_Code.6hckh19v, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); 
//Assembly ass3 = Assembly.LoadFrom("antlr3.runtime.dll"); //load by path, don use better
Assembly[] assAll = AppDomain.CurrentDomain.GetAssemblies(); //get all assembly
          
/*Class Reflection*/
//4 ways get Type   
//get type by AssemblyQualifiedName , first parameter is namespace+className 
//use normal get Class type see the AssemblyQualifiedName 1st
Type type1 = Type.GetType("reflection_Default+DemoClass, App_Web_d234vjrv, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); 
Type type2 = typeof(DemoClass);       
Type type3 = new DemoClass().GetType();
Type[] types = ass1.GetTypes(); //get Class form assembly
    /*
Type类的属性:
    Name 数据类型名
    FullName 数据类型的完全限定名(包括命名空间名)
    Namespace 定义数据类型的命名空间名
    IsAbstract 指示该类型是否是抽象类型
    IsArray   指示该类型是否是数组
    IsClass   指示该类型是否是类
    IsEnum   指示该类型是否是枚举
    IsInterface    指示该类型是否是接口
    IsPublic 指示该类型是否是公有的
    IsSealed 指示该类型是否是密封类
    IsValueType 指示该类型是否是值类型
Type类的方法:
    GetConstructor(), GetConstructors():返回ConstructorInfo类型,用于取得该类的构造函数的信息
    GetEvent(), GetEvents():返回EventInfo类型,用于取得该类的事件的信息
    GetField(), GetFields():返回FieldInfo类型,用于取得该类的字段(成员变量)的信息
    GetInterface(), GetInterfaces():返回InterfaceInfo类型,用于取得该类实现的接口的信息
    GetMember(), GetMembers():返回MemberInfo类型,用于取得该类的所有成员的信息
    GetMethod(), GetMethods():返回MethodInfo类型,用于取得该类的方法的信息
    GetProperty(), GetProperties():返回PropertyInfo类型,用于取得该类的属性的信息
可以调用这些成员,其方式是调用Type的InvokeMember()方法,或者调用MethodInfo, PropertyInfo和其他类的Invoke()方法。 
*/ //Type attrs and method (some like isArray? isInterface? , get field, method, attrs...)

//check a class is support some interface            
bool isSupportInterface2 = type3.GetInterfaces().Contains(typeof(IReadAble));
bool isSupportInterface3 = typeof(IReadAble).IsAssignableFrom(type3); //focus the sequence 顺序, interface in front

//check is a class is sub class of some class           
bool isSubClass2 = type3.IsSubclassOf(typeof(ParentClass));
bool isSubClass3 = typeof(ParentClass).IsAssignableFrom(type3); //parentClass in front

//2 way for new Class
DemoClass demo1 = (DemoClass)Activator.CreateInstance(type3); //use Activator           
ConstructorInfo constructor = type3.GetConstructor(new Type[0]); //use constructor no parameter
DemoClass demo2 = (DemoClass)constructor.Invoke(new object[0]); //no parameter

//BindingFlags.DeclaredOnly ( no includ inherit )
//BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static ( common use )
//指定 BindingFlags.FlattenHierarchy 以便沿层次结构向上包括 public 和 protected 静态成员;不包括继承类中的 private 静态成员。( unknow )

 

更新 2016-03-07 

反射的概念 

反射出来的东西通常就是 object,你必须去强转它(让我们来告诉编辑器这个类到底是什么类),然后使用。

 

反射extension method 和 async method 

public static class DbSetExtensions
{      
    public static async Task<DbSet<T>> check<T>(this DbSet<T> dbSet) where T : class, IEntityWithResourceOwner
    {
        var result = await dbSet.AsNoTracking().SingleOrDefaultAsync();
        return dbSet;
    }
}

public interface IEntityWithResourceOwner
{
    int id { get; set; }
    int resourceOwnerId { get; set; }
} 

Product product = new Product();
Type type = product.GetType();                 
var setMethod = typeof(DB).GetMethod("Set", new Type[0]).MakeGenericMethod(type);
var dbSet = setMethod.Invoke(db, new object[0]);
var checkMethod = typeof(DbSetExtensions).GetMethod("check").MakeGenericMethod(type); 反射静态方法            
await (Task)checkMethod.Invoke(null, new object[] { dbSet }); //第一个参数是null,把对象放入第一param,最后 强转成 Task

 

反射泛型类

refer : https://msdn.microsoft.com/en-us/library/w3f99sx1.aspx  ctrl+f : The following table shows the syntax you use with GetType for various types.

public class Abc<T> where T : class
{
    public string ttc(T str)
    {
        return "z";
    }
}

Type tt = Type.GetType("Project.Controllers.Abc`1");
Type ggc = typeof(Abc<>);               
Type cc = typeof(string);
Type Abc = tt.MakeGenericType(cc);
Type AbcTwo = ggc.MakeGenericType(cc);

 

反射 + 默认参数

method.Invoke(test, new object[] { "ttc" , Type.Missing});

使用 Type.Missing 来表示空参数

 

反射 Task<T>

refer : http://stackoverflow.com/questions/34814878/await-the-result-of-tasktderived-using-reflection-in-a-non-generic-method

MethodInfo getMethod = foreignResourceService.GetType().GetMethod("get", new Type[] { typeof(DB), typeof(User), typeof(int), typeof(bool) });
Task task = (Task)getMethod.Invoke(foreignResourceService, new object[] { db, user, foreignKeyId, Type.Missing });
await task;
IQueryable<IEntity> query = (IQueryable<IEntity>)(task.GetType().GetProperty("Result").GetValue(task));

先弄成 task , 然后 await , 然后get Result, 然后强转

 

反射 Method<T>

var method = type.GetMethods().ToList().SingleOrDefault(m => 
    m.Name == "Set" && 
    m.IsGenericMethod && 
    m.GetGenericArguments().Count() == 1 && 
    m.GetGenericArguments().Single().Name == "TEntity"                
);

 

posted @ 2014-08-07 10:16  兴杰  阅读(5997)  评论(1编辑  收藏  举报