(.NET進階十三)泛型類/泛型方法/泛型委託/協變逆變

一、泛型類

public class GenericStack<T>
{
    private T[] stackArray;//泛型數組
    private int currentPosition;//當前位置
    private int count;//棧的數據容量

    public GenericStack(int count)
    {
        this.count = count;
        this.stackArray = new T[count];//初始化數組大小
        this.currentPosition = 0;/當前位置從0開始
    }


    //入棧
    public void Push(T item)
    {
        if(currentPosition >= count)
        {
            Console.WriteLine("當前棧空間已滿!");
        }
        else
        {
            this.stackArray[currentPosition] = item;
            currentPosition++;
        }
    }

    //出棧
    public T Pop()
    {
        T result = this.stackArray[currentPosition-1];
        currentPosition--;
        return result ;
    }    

}



//調用
static void Main(string[] args)
{
    GenericStack<int> stack1 = new GenericStack<int>(5);
    stack1.Push(1);
    stack1.Push(2);
    stack1.Push(3);
    stack1.Push(4);
    stack1.Push(5);
    
    stack1.Pop();
    stack1.Pop();
    stack1.Pop();
    stack1.Pop();
    stack1.Pop();

}
  1. 泛型好處:增加類型安全,編碼靈活性提高
  2. 常見泛型:泛型類 泛型方法
  3. 泛型類規範:
    1. public class 類型<T>{類的成員...}
    2. T僅爲佔位符,符合C#命名規則即可,表示一個通用的數據類型,在使用時用實際的類型替代
    3. 如果包含任意多個類型的參數,參數之間用逗號分隔,如GenericStack<T1,T2,T3>{...},所定義的各種類型參數,可以用做成員變量的類型,屬性,方法等返回值類型及方法參數...

二、泛型約束

//default關鍵字使用
public class GenericClass1<T1,T2>
{
    private T1 obj1;
    
    public GenericClass1()
    {
        //泛型使用的兩種錯誤
        //obj1 = null;
        //obj1 = new T1();//不能人爲假定某種類型,因爲這種類型也許沒有構造方法,也許是私有的
        
        //解決方法
        obj1 = default(T1);
    }
}


//添加約束類型的泛型類
public class GenericClass2<T1,T2,T3>
        where T2:class//類型必須爲引用類型
        where T3:new()//在這個類中,類型必須有一個無參構造,且必須把這個約束放在最後
                      //其他類型-->基類類型 where T2:T1{} 表示T2必須與T1類型相同或繼承自T1
{
    //產品列表
    public List<T2> ProductList{get;set;}

    //發行者
    public T3 Publisher{get;set;}

    public GenericClass2()
    {
        ProductList = new List<T2>();
        Publisher = new T3();
    }

    //購買第幾個產品
    public T2 BuyProduct(T1 num)
    {
        //return ProductList[num];//直接寫有錯誤,T1不一定爲int類型
        dynamic index = num;//轉化爲動態類型
        return ProductList[index];
    }
}

//根據泛型類要求設計參數(實際開發時自行設計)
class Course
{
    public string CourseName{get;set;}//課程名稱
    public int Period{get;set;}//課程學習週期
}

class Teacher
{
    public Teacher(){}
    public string Name{get;set;}
    public int Count{get;set;}//授課數量
    
}


static void Main(string[] args)
{
    GenericClass2<int,Course,Teacher> myclass2 = new GenericClass2<int,Course,Teacher>();

    myclass2.Publisher = new Teacher{Name = "",Count = 20};
    myclass2.ProductList = new List<Course>()
    {
        new Course(){CourseName = "",Period = 6},
        new Course(){CourseName = "",Period = 7}
    }

    Course myCourse = myclass2.BuyProduct(0);
    
    Console.WriteLine("輸出======");
}

三、泛型方法

//實現四則運算

static T Add1<T>(T a,T b)
{
    return a + b;//寫法錯誤
    dynamic a1 = a;//動態類型僅在編譯期間存在,運行期間會被object類型替代(編譯時不考慮具體類型)
    dynamic b1 = b;
    return a1+b1;
}

static T Add2<T>(T a,T b)where T:struct
{
    dynamic a1 = a;
    dynamic b1 = b;
    return a1+b1;
}

static T Sub<T>(T a,T b)where T:struct
{
    dynamic a1 = a;
    dynamic b1 = b;
    return a1-b1;
}

static T Multiply<T>(T a,T b)where T:struct
{
    dynamic a1 = a;
    dynamic b1 = b;
    return a1*b1;
}

static T Div<T>(T a,T b)where T:struct
{
    dynamic a1 = a;
    dynamic b1 = b;
    return a1/b1;
}

//調用
static void Main(string[] args)
{
    Add1(20,30);
    Add2(10,20);
    Sub(20.5,56);
    Add2(20.5,5);
}



//實現一個數的求和
private static T Sum<T>(T a)where T : struct
{
    int sum = default(T);
    for(dynamic i=0;i<=a;i++)
    {
        sum+=i;
    }
    return sum;
}

四、泛型委託

static void Main(string[] args)
{
    MyDeletegate<int> myDeletegate1 = Add;
    MyDeletegate<double> myDeletegate2 = Sub;
    
    //調用
    myDeletegate1(10,20);
    myDeletegate2(10.5,20.7);
}

//定義泛型委託
public deletegate T MyDeletegate(T a,T b);

static int Add(int a,int b)
{
    return a+b;
}

static int Sub(double a,double b)
{
    return a-b;
}

五、Action與Func

系統提供了沒有返回值和一個返回值的泛型委託

//最多可以有16個參數
public Action<int,int,int> myAction = (a1,a2,a3)=>
{
    int resule = (a1+a2)*a3
    Console.WriteLine(result);
}

///最後一個表示返回值類型
public Func<int,int,string> myFunc = (a1,a2)=>
{
    int result = a1+a2;
    return "返回值爲:"+result;
}

六、協變逆變

  • 協變 out
    • 某個返回值類型可以用它的子類類型替換,則這個類型支持協變,List不支持協變
    • 應用,如:IEnumerable<父類> list= new List<子類>();
  • 逆變 in
    • 逆變是說某個《輸入類型》可以用它的《父類類型替換》
    • 應用,如:Action<T>支持逆變,IEnumerable<子類> list= new List<父類>();

七、緩存

  • 總結:正常情況下,如果創建一個靜態成員和靜態構造方法,在第一次使用的時候會初始化,後面再次使用永遠不會初始化
  •  但是如果我們使用泛型類,當我們傳遞不同的類型時,靜態成員和靜態構造,還會在第一次執行
static void Main(string[] args)
{
    CacheBaseGeneric<Student>.SaveData(new Student{name = "000"});
    CacheBaseGeneric<Student>.GetData().Name;


    CacheBaseGeneric<Teacher>.SaveData(new Student{name = "000"});
    CacheBaseGeneric<Teacher>.GetData().Name;

    
    //使用靜態構造方法,不同類型進入時先進行初始化,當該類型再次進入時則不進入構造方法,直接更新緩存

    //總結:正常情況下,如果創建一個靜態成員和靜態構造方法,在第一次使用的時候會初始化,後面再次使用永遠不會初始化
    //但是如果我們使用泛型類,當我們傳遞不同的類型時,靜態成員和靜態構造,還會在第一次執行
    
}


//基於泛型實現緩存:泛型類+靜態字段+靜態方法
//結論:泛型本質就是根據不同的數據類型,編譯成不同的類,下面可以實現高性能不同數據類型的數據緩存
public class CacheBaseGeneric<T>where T:class,new()
{
    private static T tData = null;
    //靜態構造方法
    static CacheBaseGeneric()
    {
        Console.writeLine("靜態都在方法被調用,類型爲:"+typeof(T));
    }

    //保存數據
    public static void SaveData(T data)
    {
        tData = data;
    }

    //獲取緩存數據
    public static T GetData()
    {
        return tData;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章