泛型和反射

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

 

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"                
);

 

關鍵詞:type 型別 typeof public 方法 new class method string object

相關推薦:

網路程式設計進階:併發程式設計之多執行緒

小白學Python---第二週小結(字串和常用資料結構)

微信小程式如何實現自動退款功能?

可快速生成增刪curd改查功能的外掛

Python自學day-7

部落格園之登入

33.Django ModelForm

Django REST framework 之分頁,檢視,路由,渲染器

python基礎1—零碎

爬蟲-破解驗證碼