1. 程式人生 > >.NET編程01(泛型)

.NET編程01(泛型)

基類 減少 inter st3 func basic line pro ping

一: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(泛型)