泛型和反射

更新 :2018-11-4 web

獲取 namepsace 下的全部 class編程

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

 

 

更新 2018-10-29c#

沒想到這麼多年後既然還能更新 .... 數組

這幾年都沒有寫底層的代碼,都是忙於業務層,不少語法側面知識後來用的時候才比較清楚.性能優化

今天在來談談反射, 我就不看以前寫過什麼了啦. app

想要動態調用代碼就要經過反射. async

好比本來咱們寫個調數據的方法編輯器

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

多簡單,就一行..但若是我說 Entity.Blog 是運行時才能肯定的呢 ?ide

那就要反射了.函數

反射第一步驟是找到 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"                
);
相關文章
相關標籤/搜索