亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#基礎之泛型

2020-01-24 01:01:32
字體:
來源:轉載
供稿:網友

1.泛型的本質

  泛型的好處不用多說,在.NET中我看到有很多技術都是以泛型為基礎的,不過因為不懂泛型而只能對那些技術一臉茫然。泛型主要用于集合類,最主要的原因是它不需要裝箱拆箱且類型安全,比如很常用的List<T>。對于List<T>我以后還想進行深究,現在我寫了一個超簡版的MyList<T>集合,如下面第一段代碼所示。代碼很簡單,但在寫的過程中有一個細節,如果我為listInt賦值string類型的變量時編譯器會提示報錯。編譯器很智能,但是從這個現象中你會不會好奇泛型中的T是在什么情況下指定的呢,是生成IL時還是JIT動態編譯時?老方法我將exe放入Reflector工具中,發現IL代碼中全是T,這說明在編譯時T僅僅只是一個占位符,真真的替換是在運行時動態替換的??墒窃趯懛盒皖悤r代碼只有一份,那我為MyList創建int、string類型的對象時這個代碼是如何公用的呢?對于值類型集合比如listInt,由于最終需要替換T,那么肯定是有一份完整的代碼里面T被替換為int。對于引用類型,因為變量只是一個指向堆中的指針,因此代碼只有一份??偨Y起來就是值類型代碼有多份而引用類型代碼只有一份,另外編寫自定義泛型代碼時最好使用有意義的T,比如.net中常見的TResult表示返回值,這樣可讀性較好。

class Program {  static void Main(string[] args)  {   MyList<int> listInt = new MyList<int>();   MyList<string> listString = new MyList<string>();   listInt.Add(24);   listInt[1] = 5;   listString[2] = "ha ha";  } } public class MyList<T> {  T[] array;  int current = -1;  public MyList()  {   array = new T[10];  }  public void Add(T t)  {   current++;   if (current < 10)    array[current] = t;  }  public T this[int index]  {   get   {    return array[index];   }   set   {    array[index] = value;   }  } }

2.泛型規范

  這個很重要,主要包括約束和default。.NET是推薦我們開發者盡可能的多使用約束,因為約束越多越可以保證程序不會出錯。泛型約束由where指定,六種約束如下所示。這些約束可以單獨使用也可以一起使用,但也有不能一起使用的比如值類型與引用類型約束。關于default的作用我們可以思考這樣一個問題,如果在泛型類中我們需要初始化一個T變量。因為T既有可能是值類型也有可能是引用類型,所以不能直接用new或等于0。那如何判斷T是值類型還是引用類型呢?這里就要用到default,對于引用類型default(T)將返回null,對于數值類型default(T)將返回0。這里之所以寫數值類型是因為值類型還可能是結構體,default會將結構體中的成員初始化為0或null。還有一種特殊情況就是可空值類型,此時將返回Nullable<T>,這樣初始變量直接使用T t=default(T)就可以了。雖然泛型類給人帶來了神秘感,不過運行時它的本質就是一個普通的類,因此依舊具有類的特性比如繼承。這為我們開發者帶來了很多好處,比如我想要有一個int集合類,它除了有List<int>的功能外還有自定義的某些功能,這時候只需MyList : List<int>就可以得到想要的效果了,非常方便。

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());    }   }  } }

4.泛型中的out和in

  在VS查看IEnumerable<T>的定義時會看到在T前面有一個out,與其對應的還有一個in。這就是.NET中的協變與逆變,剛開始筆者對于這2個概念很暈,主要以下4個疑惑,我想如果你解決了的話應該也會有更進一步的認識。

1.為什么需要協變和逆變,協變與逆變有什么效果?

2.為什么有了協變與逆變就可以類型安全的進行轉換,不加out和in就不可以轉換?

3.使用協變和逆變需要注意什么?

4.協變與逆變為什么只能用于接口和委托?

下面第一段代碼解決了第一個問題。對于第二個問題請看第二段代碼,里面對無out、in的泛型為什么不安全講得很清楚,從中我們要注意到如果要當進行協變時Function2是完全ok的,當進行逆變時Function1又是完全ok的。所以加out只是讓開發者在代碼里無法使用in的功能,加in則是讓開發者無法使用out的功能。讀者可以自己動手試試,在out T的情況下作為輸入參數將會報錯,同樣將in T作為返回參數也會報錯,且VS報錯時會直接告訴你這樣只能在協變或逆變情況下使用。也就是說加了out后,只有Function2能夠編譯通過,這樣o=str將不會受Function1的影響而不安全;加了in后,只有Function1能夠編譯通過,這樣str=o將不會受Function2的影響而不安全。使用out和in要注意它們只能用于接口和委托,且不能作用于值類型。out用于屬性時只能用于只讀屬性,in則是只寫屬性,進行協變和逆變時這2個類型參數必須要有繼承關系,現在為什么不能用于值類型你應該懂了吧。對于第四個疑惑我沒有找到一個完全正確的答案,只是發現了我認同的想法。接口和委托,有什么共同點?顯然就是方法,在接口或委托中聲明的T都將用于方法且只能用于方法,由上面的討論可知協變和逆變這種情況正是適用于方法這樣的成員。對于在抽象類中不可以使用的原因,或許微軟是覺得在抽象類中再搞一個僅限于方法的限制太麻煩了吧。

public interface INone<T> { }  public interface IOut<out T> { }  public interface IIn<in T> { }  public class MyClass<T> : INone<T>, IOut<T>, IIn<T> { }  void hh()  {   INone<object> oBase1 = new MyClass<object>();   INone<string> o1 = new MyClass<string>();   //下面兩句都無法編譯通過   //o1 = oBase1;   //oBase1 = o1;   //為了能夠進行轉換,于是出現了協變與逆變   IOut<object> oBase2 = new MyClass<object>();   IOut<string> o2 = new MyClass<string>();   //o2 = oBase2; 編譯不通過   //有了out關鍵字的話,就可以實現從IOut<string>到IOut<object>的轉換-往父類轉換   oBase2 = o2;   IIn<object> oBase3 = new MyClass<object>();   IIn<string> o3 = new MyClass<string>();   //oBase3 = o3; 編譯不通過   //有了in關鍵字的話,就可以實現從IIn<object>到IOut<string>的轉換-往子類轉換   o3 = oBase3;  }
public interface INone<T> {  void Function1(T tParam);  T Function2(); } class MyClass<T> : INone<T> {  public void Function1(T tParam)  {   Console.WriteLine(tParam.ToString());  }  public T Function2()  {   T t = default(T);   return t;  } } class hhh {  void fangyz()  {   INone<object> o = new MyClass<object>();   INone<string> str = new MyClass<string>();   //假設str能夠轉換為o   //o = str;   object o1=new object();   //這樣的話就是object類型向string類型轉換了,類型不安全   o.Function1(o1);   //這樣則是string類型向object類型轉換了,注意這樣是ok的,沒什么問題   object o2=o.Function2();   //假設str能夠轉換為o   //str=o;   //string對象將轉變為object,這樣沒問題   str.Function1("haha");   //這樣將是object向string類型的轉換,類型不安全。   string o3=str.Function2();  } }

以上所述是小編給大家介紹的C#基礎之泛型,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品电影观看| 国模gogo一区二区大胆私拍| 日韩av在线看| 亚洲精品一区二区三区婷婷月| 96精品久久久久中文字幕| 国产精品入口免费视| 在线播放国产一区中文字幕剧情欧美| 欧美一区二区三区免费视| 欧美精品videofree1080p| 精品久久国产精品| 欧美日韩在线第一页| 国产一区二区激情| 国产精品久久精品| 国产视频福利一区| 欧美美女18p| 亚洲成人激情视频| 国产91网红主播在线观看| 欧美性猛交丰臀xxxxx网站| 久久久久久高潮国产精品视| 日韩欧美视频一区二区三区| 成人免费视频在线观看超级碰| 成人亚洲激情网| 懂色av一区二区三区| 欧美另类99xxxxx| 久久久久成人网| 91精品在线看| 亚洲国产精品va在线| 欧美日韩在线视频首页| 高跟丝袜欧美一区| 国产精品手机播放| 在线亚洲国产精品网| 黄色一区二区在线观看| 伊人青青综合网站| 午夜精品一区二区三区在线视| 亚洲欧洲在线视频| 俺去啦;欧美日韩| 青青草国产精品一区二区| 这里只有精品在线观看| 亚洲美女动态图120秒| 精品视频偷偷看在线观看| 国产精品h在线观看| 伊人伊人伊人久久| 国产婷婷97碰碰久久人人蜜臀| 怡红院精品视频| 色偷偷88888欧美精品久久久| 成人免费黄色网| 国产成人啪精品视频免费网| 国产视频福利一区| 国产日本欧美一区二区三区在线| 国产精品久久97| 亚洲国产精品久久久久| 久久精品欧美视频| 亚洲网站在线观看| 97精品视频在线观看| 九色91av视频| 欧美日韩黄色大片| 成人激情免费在线| 久久综合久中文字幕青草| 久久久999精品| 久久国产精品亚洲| 亚州av一区二区| 国产日韩精品在线观看| 日韩高清欧美高清| 国产日韩欧美日韩大片| 欧美有码在线观看视频| 91在线视频导航| 国产精品午夜一区二区欲梦| 在线播放国产一区中文字幕剧情欧美| 国产精品一区电影| 成人美女免费网站视频| 亚洲一区www| 91在线观看免费| 欧美成年人视频网站欧美| 欧美大胆在线视频| 一区二区国产精品视频| 精品成人国产在线观看男人呻吟| 国产精品综合久久久| 欧美孕妇与黑人孕交| 亚洲丝袜在线视频| 欧美激情视频播放| 欧美精品少妇videofree| 亚洲国产精品yw在线观看| 日韩精品视频免费在线观看| 国产欧美日韩中文字幕在线| 日韩免费av片在线观看| 亚洲午夜精品久久久久久久久久久久| 中文字幕亚洲欧美日韩在线不卡| 欧美亚洲国产日本| 亚洲精品大尺度| 亚洲另类欧美自拍| www.欧美免费| 国产色综合天天综合网| 国产精品嫩草视频| 久久久久久久999精品视频| 国产精品1区2区在线观看| 色在人av网站天堂精品| 国产精品中文字幕久久久| 亚洲片国产一区一级在线观看| 国产一区二区久久精品| 欧美xxxx18国产| 久久久久这里只有精品| 亚洲国产天堂久久国产91| 欧美www在线| 欧美午夜精品伦理| 欧美激情视频免费观看| 国产精品美腿一区在线看| 午夜精品一区二区三区视频免费看| 成人情趣片在线观看免费| 91高清视频在线免费观看| 久久在线精品视频| 69久久夜色精品国产69| 国产成人在线亚洲欧美| 欧美激情在线一区| 热久久99这里有精品| 日韩亚洲欧美成人| 国产+人+亚洲| 亚洲韩国日本中文字幕| 98精品国产高清在线xxxx天堂| 亚洲淫片在线视频| 久久久精品2019中文字幕神马| 亚洲欧美日韩天堂一区二区| 欧美国产日韩一区二区三区| 精品福利视频导航| 中文字幕欧美精品在线| 国产精品自拍视频| 亚州欧美日韩中文视频| 日韩精品高清视频| 九九热最新视频//这里只有精品| 色999日韩欧美国产| 国产视频久久久| 欧美日韩第一页| 久久亚洲成人精品| 亚洲伦理中文字幕| 波霸ol色综合久久| 亚洲精品av在线播放| 97成人精品区在线播放| www日韩欧美| 亚洲视频电影图片偷拍一区| 中文字幕在线国产精品| 最近中文字幕mv在线一区二区三区四区| 久久久精品视频成人| 91成人天堂久久成人| 亚洲自拍小视频免费观看| 日韩成人激情视频| 成人午夜黄色影院| 亚洲影院污污.| 国产在线精品一区免费香蕉| 中文字幕日韩精品在线| 国产成人97精品免费看片| 国产不卡一区二区在线播放| 久久精品久久久久电影| 欧美精品videossex性护士| 亚洲自拍另类欧美丝袜| 亚洲男人第一网站| 欧美亚洲激情视频| 欧美三级免费观看| 成人午夜在线视频一区| 韩国精品久久久999| 韩曰欧美视频免费观看| 久久精品国产v日韩v亚洲| 国产精品91一区| 久久久国产精彩视频美女艺术照福利| 亚洲人成五月天| 欧美xxxx18性欧美|