.NET編程01(泛型)
一:Object 類型:一切類型的父類,通過繼承,子類擁有父類一切屬性和行為;任何父類出現的地方,都可以用子類來代替;
用一個方法來完成多個方法做的事
/// <summary>
/// 普通方法類
/// </summary>
public class CommonMethod
{
/// <summary>
/// 打印個int值
/// </summary>
/// <param name="iParameter"></param>
public static void ShowInt(int iParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}",
typeof(CommonMethod).Name, iParameter.GetType().Name, iParameter);
}
/// <summary>
/// 打印個string值
/// </summary>
/// <param name="sParameter"></param>
public static void ShowString(string sParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}",
typeof(CommonMethod).Name, sParameter.GetType().Name, sParameter);
}
/// <summary>
/// 打印個object值
/// </summary>
/// <param name="oParameter"></param>
public static void ShowObject(object oParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}",
typeof(CommonMethod), oParameter.GetType().Name, oParameter);
}
Typeof():用來獲取某個類型
缺點:
第一:object是應用類型的,所以把值類型的參數傳入ShowObject()方法時,就會存在裝箱和拆箱,影響性能
第二:傳入參數裏面的屬性值無法被訪問到
第三:涉及到類型安全的問題
二:泛型(不屬於語法糖,是ClR升級後才支持的語法)
用途:對於不同類型的參數,具有相同行為的時候,希望代碼能重用起來,這時候使用泛型
原理: /// 延遲申明:申明方法的時候並沒有指定參數類型(實際存在兩個參數:T是類型參數,oParameter是真實參數),而是等到使用的時候在指定
/// 延遲思想:推遲一切可以推遲的
/// 編譯的時候,類型參數編譯為占位符,在程序真實運行的時候,JIT進行即時編譯替換為真實類型
public class GenericMethod
{
/// <summary>
/// 第一:泛型方法(方法名字後面帶上尖括號,類型參數),尖括號類可以是任何沒定義的任何字母或者類型,不要寫關鍵字,不要把定義好的類寫在裏面
/// </summary>
/// <param name="oParameter"></param>
public static void Show<T>(T oParameter)
{
Console.WriteLine("This is {0},parameter={2},type={1}",
typeof(CommonMethod), oParameter.GetType().Name, oParameter);
}
}
class Program
{
static void Main(string[] args)
{
int iValue = 123;
object ovalue=new object();
//第一:泛型方法調用
GenericMethod.Show<int>(ivalue);
GenericMethod.Show(ivalue);//不指定類型參數,編譯器自動推算
GenericMethod.Show<string>(ivalue);//此時報錯,<類型參數>和參數類型:(ivalue)的類型必須吻合
GenericMethod.Show<object>(ovalue);
}
}
三:泛型類/接口
//泛型類
public class GenericClass<M,T,S>
{
public void Show(M m){}//可以作為參數
public T get(){}//可以作為返回值
}
//泛型接口
publuc interInface IStudy<T>
{
T Study(T t);
}
//泛型委托
public delegate T GetHandler<T>();
//普通類
public class Child
:GenericClass<M,T,S>//報錯普通類不能直接繼承泛型類
:GenericClass<int,string,double>//指定類型參數後才可以繼承
:IStudy<T>//報錯普通類不能直接實現泛型接口
:IStudy<string>//指定類型後可以
{
public string Study(string t){}
}
//泛型類
public class GenericChils<M,T>//等於申明了兩個局部類型 M,T
:GenericClass<M,T,String>//泛型類可以直接繼承泛型類
:IStudy<T>//T和M都可以或者指定一個特定類型,除此之外不行,泛型類可以直接實現泛型接口
{
T IStudy<T>.Study(T t){}
}
四:泛型約束
public class People
{
public int Id { get; set; }
public string Name { get; set; }
public void Hi(){ }
}
public class Chinese :People:ISports
{
public void SayHi(){}
public void Pingpang(){}
}
public interface ISports
{
void Pingpang();
}
public class Constraint
{
///基類約束
/// 1: 基類約束,就可以訪問基類的屬性和方法
/// 2:被調用時, 參數必須是基類/子類
public static void Show<T>(T oParameter) where T:People{}
//接口約束
public static void ShowInterface<T>(T oParameter) where T:ISports{}
///接口+基類約束
public static void ShowBasic<T>(T oParameter) where T:People,ISports{}
public static T Get<T>()
//where T:Class//應用了類型約束
//where T:struce//值類型約束
//where T:new()//無參數構造函數約束
{
T t=new T();
return null;//應用類型默認值
return default(T):值類型默認值
}
}
泛型方法的約束的調用:
People people = new People()
Chinese chinese = new Chinese()
Constraint.Show<People>(People p)
Constraint.Show(people )
Constraint.Show<Chinese >(Chinese p)
Constraint.Show(chinese )
Constraint.ShowInterface(people)
五:泛型的協變/逆變(out/in):只能放在接口或者委托的參數前面,類沒有協變逆變
out 協變 convariant 修飾返回值
in 逆變 contravariant 修飾傳入參數 IEnumerable<int> Action<int>
第一:協變
定義兩個普通類
public class Bird{public int ID{get;set;}}
public class Sparrow:Bird{public string Name{get;set;}}
List<Bird> b1list=new List<bird>();------編譯正確
List<Bird> B2list=new List<Sparrow>();------編譯錯誤,因為list<Bird>和List<Sparrow>不是父子關系,沒有繼承關系
但是實際工作中會有這種需要(用父類集合來接受子類的集合)
一般的寫法:List<Bird> B3list=new List<Sparrow>().select(c=>(Bird)c).ToList();-----編譯正確,類型進行了強轉
簡單的寫法:IEnumerable<Bird> b1=new List<bird>();---------協變
IEnumerable<Bird> b2=new List<Sparrow>();---------協變(實際上也存在類型的的轉換,只不過是編譯器來做,減少了代碼量)
以上寫法采用協變使用系統提供的IEnumerable<out T>
public interface IEnumerable<out T> : IEnumerable ------ 系統對IEnumerable<out T>的定義
使用List來接受子類集合會報錯是因為,系統本身本沒有對list進行協變定義
public class List<T>:IList<T>,IEnumerable<T>..........----------- 系統對List<T>的定義 參數沒有Out
原因:List出現在.net2.0版本,而IEnumerable<out T>出現在C#4.0版本,兩個不是屬於同一個時期出現
自定義協變接口
Public interface ICustomerListOut<out T>
{
T Get();-----正確
void Show(T t)----報錯,T不能作為傳入參數,只能是返回結果;
}
//類沒有協變逆變
public class CustomerListOut<T>:ICustomerListOut<T>
{
public T Get(){return default(T);}
}
使用:
ICustomerListOut<Bird> b=new CustomerListOut<Bird>();
ICustomerListOut<Bird> b=new CustomerListOut<Sparrow>();
第二:逆變
自定義逆變接口
Public interface ICustomerListOut<in T>
{
//T Get();-----報錯,T只能作為傳入參數,不能是返回結果;
void Show(T t)----正確
}
public class CustomerListOut<T>:ICustomerListOut<T>
{
public void Show(T t){}
}
使用:
ICustomerListIn<Sparrow> customerList2 = new CustomerListIn<Sparrow>();
ICustomerListIn<Sparrow> customerList1 = new CustomerListIn<Bird>();
第三:逆變+協變(實際工作中使用很少,一般使用系統很少自己定義,系統自帶Func<in T,out Tresult>)
public interface IMyList<in inT, out outT>
{
void Show(inT t);
outT Get();
outT Do(inT t);
}
public class MyList<T1, T2> : IMyList<T1, T2>
{
public void Show(T1 t)
{
Console.WriteLine(t.GetType().Name);
}
public T2 Get()
{
Console.WriteLine(typeof(T2).Name);
return default(T2);
}
public T2 Do(T1 t)
{
Console.WriteLine(t.GetType().Name);
Console.WriteLine(typeof(T2).Name);
return default(T2);
}
}
使用:
IMyList<Sparrow, Bird> myList1 = new MyList<Sparrow, Bird>();
IMyList<Sparrow, Bird> myList2 = new MyList<Sparrow, Sparrow>();//協變
IMyList<Sparrow, Bird> myList3 = new MyList<Bird, Bird>();//逆變
IMyList<Sparrow, Bird> myList4 = new MyList<Bird, Sparrow>();//協變+逆變
第六:泛型的緩存
一般采用字典緩存:定義一個靜態屬性
/// <summary>
/// 字典緩存:靜態屬性常駐內存
/// </summary>
public class DictionaryCache
{
private static Dictionary<string, string> _TypeTimeDictionary = null;
static DictionaryCache()
{
Console.WriteLine("This is DictionaryCache 靜態構造函數");
_TypeTimeDictionary = new Dictionary<string, string>();
}
public static string GetCache<T>()
{
Type type = typeof(T);
if (!_TypeTimeDictionary.ContainsKey(type.Name))
{
_TypeTimeDictionary[type.Name] = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
}
return _TypeTimeDictionary[type.Name];
}
}
第二:泛型緩存
原理:利用泛型每次在運行時Jit即時編譯生成不同的副本
使用場景:用來保存固定數據,適合不同類型,需要緩存一份數據的場景,
優點:效率高
缺點:不能清除被回收
/// <summary>
/// 每個不同的T,在運行的時候JIT都會生成一份不同的副本,用於保存不同的T
/// </summary>
/// <typeparam name="T"></typeparam>
public class GenericCache<T>
{
static GenericCache()
{
Console.WriteLine("This is GenericCache 靜態構造函數");
_TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
}
private static string _TypeTime = "";
public static string GetCache()
{
return _TypeTime;
}
}
調用:
for (int i = 0; i < 5; i++)
{
Console.WriteLine(GenericCache<int>.GetCache());
Thread.Sleep(10);
Console.WriteLine(GenericCache<long>.GetCache());
}
.NET編程01(泛型)