欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#泛型詳解

 更新時間:2022年03月09日 15:20:14   作者:.NET開發(fā)菜鳥  
本文詳細講解了C#中的泛型,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

這篇文章主要講解C#中的泛型,泛型在C#中有很重要的地位,尤其是在搭建項目框架的時候。

一、什么是泛型

泛型是C#2.0推出的新語法,不是語法糖,而是2.0由框架升級提供的功能。

我們在編程程序時,經(jīng)常會遇到功能非常相似的模塊,只是它們處理的數(shù)據(jù)不一樣。但我們沒有辦法,只能分別寫多個方法來處理不同的數(shù)據(jù)類型。這個時候,那么問題來了,有沒有一種辦法,用同一個方法來處理傳入不同種類型參數(shù)的辦法呢?泛型的出現(xiàn)就是專門來解決這個問題的。

二、為什么使用泛型

先來看下面一個例子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    public class CommonMethod
    {
        /// <summary>
        /// 打印個int值
        /// 
        /// 因為方法聲明的時候,寫死了參數(shù)類型
        /// 已婚的男人 Eleven San
        /// </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>
        /// 打印個DateTime值
        /// </summary>
        /// <param name="oParameter"></param>
        public static void ShowDateTime(DateTime dtParameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
                typeof(CommonMethod).Name, dtParameter.GetType().Name, dtParameter);
        }
    }
}

結(jié)果:

從上面的結(jié)果中我們可以看出這三個方法,除了傳入的參數(shù)不同外,其里面實現(xiàn)的功能都是一樣的。在1.0版的時候,還沒有泛型這個概念,那么怎么辦呢。相信很多人會想到了OOP三大特性之一的繼承,我們知道,C#語言中,object是所有類型的基類,將上面的代碼進行以下優(yōu)化:

public static void ShowObject(object oParameter)
{
      Console.WriteLine("This is {0},parameter={1},type={2}",
         typeof(CommonMethod), oParameter.GetType().Name, oParameter);
}

結(jié)果:

從上面的結(jié)果中我們可以看出,使用Object類型達到了我們的要求,解決了代碼的可復(fù)用??赡苡腥藭柖x的是object類型的,為什么可以傳入int、string等類型呢?原因有二:

1、object類型是一切類型的父類。

2、通過繼承,子類擁有父類的一切屬性和行為,任何父類出現(xiàn)的地方,都可以用子類來代替。

但是上面object類型的方法又會帶來另外一個問題:裝箱和拆箱,會損耗程序的性能。

微軟在C#2.0的時候推出了泛型,可以很好的解決上面的問題。

三、泛型類型參數(shù)

在泛型類型或方法定義中,類型參數(shù)是在其實例化泛型類型的一個變量時,客戶端指定的特定類型的占位符。 泛型類(GenericList<T>)無法按原樣使用,因為它不是真正的類型;它更像是類型的藍圖。 若要使用GenericList<T>,客戶端代碼必須通過指定尖括號內(nèi)的類型參數(shù)來聲明并實例化構(gòu)造類型。 此特定類的類型參數(shù)可以是編譯器可識別的任何類型。 可創(chuàng)建任意數(shù)量的構(gòu)造類型實例,其中每個使用不同的類型參數(shù)。

上面例子中的代碼可以修改如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    public class GenericMethod
    {
        /// <summary>
        /// 泛型方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tParameter"></param>
        public static void Show<T>(T tParameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
                typeof(GenericMethod), tParameter.GetType().Name, tParameter.ToString());
        }
    }
}

調(diào)用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    class Program
    {
        static void Main(string[] args)
        {

            int iValue = 123;
            string sValue = "456";
            DateTime dtValue = DateTime.Now;

            Console.WriteLine("***********CommonMethod***************");
            CommonMethod.ShowInt(iValue);
            CommonMethod.ShowString(sValue);
            CommonMethod.ShowDateTime(dtValue);
            Console.WriteLine("***********Object***************");
            CommonMethod.ShowObject(iValue);
            CommonMethod.ShowObject(sValue);
            CommonMethod.ShowObject(dtValue);
            Console.WriteLine("***********Generic***************");
            GenericMethod.Show<int>(iValue);
            GenericMethod.Show<string>(sValue);
            GenericMethod.Show<DateTime>(dtValue);
            Console.ReadKey();
        }
    }
}

顯示結(jié)果:

為什么泛型可以解決上面的問題呢?

泛型是延遲聲明的:即定義的時候沒有指定具體的參數(shù)類型,把參數(shù)類型的聲明推遲到了調(diào)用的時候才指定參數(shù)類型。 延遲思想在程序架構(gòu)設(shè)計的時候很受歡迎。例如:分布式緩存隊列、EF的延遲加載等等。

泛型究竟是如何工作的呢?

控制臺程序最終會編譯成一個exe程序,exe被點擊的時候,會經(jīng)過JIT(即時編譯器)的編譯,最終生成二進制代碼,才能被計算機執(zhí)行。泛型加入到語法以后,VS自帶的編譯器又做了升級,升級之后編譯時遇到泛型,會做特殊的處理:生成占位符。再次經(jīng)過JIT編譯的時候,會把上面編譯生成的占位符替換成具體的數(shù)據(jù)類型。請看下面一個例子:

Console.WriteLine(typeof(List<>));
Console.WriteLine(typeof(Dictionary<,>));

結(jié)果:

從上面的截圖中可以看出:泛型在編譯之后會生成占位符。

注意:占位符需要在英文輸入法狀態(tài)下才能輸入,只需要按一次波浪線(數(shù)字1左邊的鍵位)的鍵位即可,不需要按Shift鍵。

1、泛型性能問題

請看一下的一個例子,比較普通方法、Object參數(shù)類型的方法、泛型方法的性能。

添加一個Monitor類,讓三種方法執(zhí)行同樣的操作,比較用時長短:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    public class Monitor
    {
        public static void Show()
        {
            Console.WriteLine("****************Monitor******************");
            {
                int iValue = 12345;
                long commonSecond = 0;
                long objectSecond = 0;
                long genericSecond = 0;

                {
                    Stopwatch watch = new Stopwatch();
                    watch.Start();
                    for (int i = 0; i < 100000000; i++)
                    {
                        ShowInt(iValue);
                    }
                    watch.Stop();
                    commonSecond = watch.ElapsedMilliseconds;
                }
                {
                    Stopwatch watch = new Stopwatch();
                    watch.Start();
                    for (int i = 0; i < 100000000; i++)
                    {
                        ShowObject(iValue);
                    }
                    watch.Stop();
                    objectSecond = watch.ElapsedMilliseconds;
                }
                {
                    Stopwatch watch = new Stopwatch();
                    watch.Start();
                    for (int i = 0; i < 100000000; i++)
                    {
                        Show<int>(iValue);
                    }
                    watch.Stop();
                    genericSecond = watch.ElapsedMilliseconds;
                }
                Console.WriteLine("commonSecond={0},objectSecond={1},genericSecond={2}"
                    , commonSecond, objectSecond, genericSecond);
            }
        }

        #region PrivateMethod
        private static void ShowInt(int iParameter)
        {
            //do nothing
        }
        private static void ShowObject(object oParameter)
        {
            //do nothing
        }
        private static void Show<T>(T tParameter)
        {
            //do nothing
        }
        #endregion

    }
}

Main()方法調(diào)用:

Monitor.Show();

結(jié)果:

從結(jié)果中可以看出:泛型方法的性能最高,其次是普通方法,object方法的性能最低。

四、泛型類

除了方法可以是泛型以外,類也可以是泛型的,例如:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    /// <summary>
    /// 泛型類
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GenericClass<T>
    {
        public T _T;
    }
}

Main()方法中調(diào)用:

// T是int類型
GenericClass<int> genericInt = new GenericClass<int>();
genericInt._T = 123;
// T是string類型
GenericClass<string> genericString = new GenericClass<string>();
genericString._T = "123";

除了可以有泛型類,也可以有泛型接口,例如:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    /// <summary>
    /// 泛型接口
    /// </summary>
    public interface IGenericInterface<T>
    {
        //泛型類型的返回值
        T GetT(T t);
    }
}

也可以有泛型委托:

public delegate void SayHi<T>(T t);//泛型委托

注意:

1、泛型在聲明的時候可以不指定具體的類型,但是在使用的時候必須指定具體類型,例如:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    /// <summary>
    /// 使用泛型的時候必須指定具體類型,
    /// 這里的具體類型是int
    /// </summary>
    public class CommonClass :GenericClass<int>
    {
    }
}

如果子類也是泛型的,那么繼承的時候可以不指定具體類型,例如:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    /// <summary>
    /// 使用泛型的時候必須指定具體類型,
    /// 這里的具體類型是int
    /// </summary>
    public class CommonClass :GenericClass<int>
    {
    }

    /// <summary>
    /// 子類也是泛型的,繼承的時候可以不指定具體類型
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CommonClassChild<T>:GenericClass<T>
    {

    }
}

2、類實現(xiàn)泛型接口也是這種情況,例如:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    /// <summary>
    /// 必須指定具體類型
    /// </summary>
    public class Common : IGenericInterface<string>
    {
        public string GetT(string t)
        {
            throw new NotImplementedException();
        }
    }

    /// <summary>
    /// 可以不知道具體類型,但是子類也必須是泛型的
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CommonChild<T> : IGenericInterface<T>
    {
        public T GetT(T t)
        {
            throw new NotImplementedException();
        }
    }
}

五、泛型約束

先來看看下面的一個例子:

定義一個People類,里面有屬性和方法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    public interface ISports
    {
        void Pingpang();
    }

    public interface IWork
    {
        void Work();
    }


    public class People
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public void Hi()
        {
            Console.WriteLine("Hi");
        }
    }

    public class Chinese : People, ISports, IWork
    {
        public void Tradition()
        {
            Console.WriteLine("仁義禮智信,溫良恭儉讓");
        }
        public void SayHi()
        {
            Console.WriteLine("吃了么?");
        }

        public void Pingpang()
        {
            Console.WriteLine("打乒乓球...");
        }

        public void Work()
        {
            throw new NotImplementedException();
        }
    }

    public class Hubei : Chinese
    {
        public Hubei(int version)
        { }

        public string Changjiang { get; set; }
        public void Majiang()
        {
            Console.WriteLine("打麻將啦。。");
        }
    }


    public class Japanese : ISports
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public void Hi()
        {
            Console.WriteLine("Hi");
        }
        public void Pingpang()
        {
            Console.WriteLine("打乒乓球...");
        }
    }
}

在Main()方法里面實例化:

People people = new People()
{
        Id = 123,
        Name = "走自己的路"
};
Chinese chinese = new Chinese()
{
        Id = 234,
        Name = "晴天"
};
Hubei hubei = new Hubei(123)
{
        Id = 345,
        Name = "流年"
};
Japanese japanese = new Japanese()
{
        Id = 7654,
        Name = "werwer"
};

這時有一個需求:需要打印出Id和Name屬性的值,將ShowObject()方法修改如下:

但是這樣修改報錯了:object類里面沒有Id和Name屬性,可能會有人說,強制類型轉(zhuǎn)換一下就行了?。?/p>

public static void ShowObject(object oParameter)
{
         Console.WriteLine("This is {0},parameter={1},type={2}",
         typeof(CommonMethod), oParameter.GetType().Name, oParameter);

         Console.WriteLine($"{((People)oParameter).Id}_{((People)oParameter).Name}");
}

這樣修改以后,代碼不會報錯了,這時我們在Main()方法里面調(diào)用:

CommonMethod.ShowObject(people);
CommonMethod.ShowObject(chinese);
CommonMethod.ShowObject(hubei);
CommonMethod.ShowObject(japanese);

結(jié)果:

可以看出程序報錯了,因為Japanese沒有繼承自People,這里類型轉(zhuǎn)換的時候失敗了。這樣會造成類型不安全的問題。那么怎么解決類型不安全的問題呢?那就是使用泛型約束。

所謂的泛型約束,實際上就是約束的類型T。使T必須遵循一定的規(guī)則。比如T必須繼承自某個類,或者T必須實現(xiàn)某個接口等等。那么怎么給泛型指定約束?其實也很簡單,只需要where關(guān)鍵字,加上約束的條件。

泛型約束總共有五種。

約束說明
T:結(jié)構(gòu)類型參數(shù)必須是值類型
T:類類型參數(shù)必須是引用類型;這一點也適用于任何類、接口、委托或數(shù)組類型。
T:new()類型參數(shù)必須具有無參數(shù)的公共構(gòu)造函數(shù)。 當(dāng)與其他約束一起使用時,new() 約束必須最后指定。
T:<基類名>類型參數(shù)必須是指定的基類或派生自指定的基類。
T:<接口名稱>類型參數(shù)必須是指定的接口或?qū)崿F(xiàn)指定的接口。 可以指定多個接口約束。 約束接口也可以是泛型的。

先看下面的示例代碼:

class MyList<T> 
{
    List<T> list = new List<T>();
    public T this[int i]
    {
        get { return list[i]; }
        set { this.list[i] = value; }
    }
}

class Person
{
    public string Name { get; set; }
}

1 指定泛型參數(shù)為值類型

class MyList<T> where T:struct
{
...代碼省略部分
}

看看加上約束后的效果怎么樣,按下面方式 實例化MyList 類 :

MyList<Person> list = new MyList<Person>();

你會發(fā)現(xiàn),將會產(chǎn)生下面的錯誤提示:

使用下面的方式實例化MyList類,將一切正常:

MyList<int> list = new MyList<int>();

如圖所示:

2指定泛型參數(shù)為引用類型

class MyList<T> where T:class 
{
...代碼省略部分 
}

修改泛型約束為引用類型后,前面的錯誤提示消失,因為Person類是引用類型,滿足泛型約束。

3指定泛型參數(shù)有無參的公共的構(gòu)造函數(shù)

class MyList<T> where T:new()
{
...代碼省略部分
}

為Person類增加私有的無參構(gòu)造函數(shù),代碼如下:

class Person
{
    public string Name { get; set; }
    private Person()
    {
        //do nothing
    }
}

實例化MyList<Person>類:

MyList<Person> list = new MyList<Person>();

出現(xiàn)編譯錯誤,提示:

把Person類里面私有的構(gòu)造函數(shù)去掉就可以了。

4指定泛型參數(shù)必須派生于指定基類

增加抽象類 SeniorAnimal

abstract class SeniorAnimal//高級動物
{
    public abstract void Speak();//會說話
    public abstract void UseTool();//會使用工具
}

指定泛型參數(shù) 必須派生于基類 SeniorAnimal

class MyList<T> where T : SeniorAnimal
{
     ...代碼省略部分
}

實例化 MyList < Person > 類(此時Person類還未繼承自 SeniorAnimal 類 ):

MyList<Person> list = new MyList<Person>();

出現(xiàn)編譯錯誤,提示:

修改代碼,使Person類繼承自SeniorAnimal類:

class Person : SeniorAnimal
{
    public string Name { get; set; }

    public override void Speak()
    {
        Console.WriteLine("我會說話!");
    }

    public override void UseTool()
    {
        Console.WriteLine("我會用水果刀!");
    }
}

再次編譯,一切正常。

5指定泛型參數(shù)必須實現(xiàn)指定接口

演示情況和第4點類似,就不提供演示代碼了。

1、基類約束

上面打印的方法約束T類型必須是People類型。

/// <summary>
/// 基類約束:約束T必須是People類型或者是People的子類
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tParameter"></param>
public static void Show<T>(T tParameter) where T : People
{
      Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
      tParameter.Hi();
}

注意:

基類約束時,基類不能是密封類,即不能是sealed類。sealed類表示該類不能被繼承,在這里用作約束就無任何意義,因為sealed類沒有子類。

2、接口約束

/// <summary>
/// 接口約束
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static T Get<T>(T t) where T : ISports
{
      t.Pingpang();
      return t;
}

3、引用類型約束 class

引用類型約束保證T一定是引用類型的。

/// <summary>
/// 引用類型約束
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static T Get<T>(T t) where T : class
{
      return t;
}

4、值類型約束 struct

值類型約束保證T一定是值類型的。

/// <summary>
/// 值類型類型約束
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static T Get<T>(T t) where T : struct
{
      return t;
}

5、無參數(shù)構(gòu)造函數(shù)約束 new()

/// <summary>
/// new()約束
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public static T Get<T>(T t) where T : new()
{
     return t;
}

泛型約束也可以同時約束多個,例如:

public static void Show<T>(T tParameter)
            where T : People, ISports, IWork, new()
{
      Console.WriteLine($"{tParameter.Id}_{tParameter.Name}");
      tParameter.Hi();
      tParameter.Pingpang();
      tParameter.Work();
}

注意:有多個泛型約束時,new()約束一定是在最后。

六、泛型的協(xié)變和逆變

協(xié)變和逆變是在.NET 4.0的時候出現(xiàn)的,只能放在接口或者委托的泛型參數(shù)前面,out 協(xié)變covariant,用來修飾返回值;in:逆變contravariant,用來修飾傳入?yún)?shù)。

先看下面的一個例子:

定義一個Animal類:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    public class Animal
    {
        public int Id { get; set; }
    }
}

然后在定義一個Cat類繼承自Animal類:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    public class Cat :Animal
    {
        public string Name { get; set; }
    }
}

在Main()方法可以這樣調(diào)用:

// 直接聲明Animal類
Animal animal = new Animal();
// 直接聲明Cat類
Cat cat = new Cat();
// 聲明子類對象指向父類
Animal animal2 = new Cat();
// 聲明Animal類的集合
List<Animal> listAnimal = new List<Animal>();
// 聲明Cat類的集合
List<Cat> listCat = new List<Cat>();

那么問題來了:下面的一句代碼是不是正確的呢?

List<Animal> list = new List<Cat>();

可能有人會認為是正確的:因為一只Cat屬于Animal,那么一群Cat也應(yīng)該屬于Animal啊。但是實際上這樣聲明是錯誤的:因為List<Cat>和List<Animal>之間沒有父子關(guān)系。

這時就可以用到協(xié)變和逆變了。

// 協(xié)變
IEnumerable<Animal> List1 = new List<Animal>();
IEnumerable<Animal> List2 = new List<Cat>();

F12查看定義:

可以看到,在泛型接口的T前面有一個out關(guān)鍵字修飾,而且T只能是返回值類型,不能作為參數(shù)類型,這就是協(xié)變。使用了協(xié)變以后,左邊聲明的是基類,右邊可以聲明基類或者基類的子類。

協(xié)變除了可以用在接口上面,也可以用在委托上面:

Func<Animal> func = new Func<Cat>(() => null);

除了使用.NET框架定義好的以為,我們還可以自定義協(xié)變,例如:

/// <summary>
/// out 協(xié)變 只能是返回結(jié)果
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICustomerListOut<out T>
{
     T Get();
}

public class CustomerListOut<T> : ICustomerListOut<T>
{
     public T Get()
     {
         return default(T);
     }
}

使用自定義的協(xié)變:

// 使用自定義協(xié)變
ICustomerListOut<Animal> customerList1 = new CustomerListOut<Animal>();
ICustomerListOut<Animal> customerList2 = new CustomerListOut<Cat>();

在來看看逆變。

在泛型接口的T前面有一個In關(guān)鍵字修飾,而且T只能方法參數(shù),不能作為返回值類型,這就是逆變。請看下面的自定義逆變:

/// <summary>
/// 逆變 只能是方法參數(shù)
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ICustomerListIn<in T>
{
     void Show(T t);
}

public class CustomerListIn<T> : ICustomerListIn<T>
{
     public void Show(T t)
     {
     }
}

使用自定義逆變:

// 使用自定義逆變
ICustomerListIn<Cat> customerListCat1 = new CustomerListIn<Cat>();
ICustomerListIn<Cat> customerListCat2 = new CustomerListIn<Animal>();

協(xié)變和逆變也可以同時使用,看看下面的例子:

/// <summary>
/// inT 逆變
/// outT 協(xié)變
/// </summary>
/// <typeparam name="inT"></typeparam>
/// <typeparam name="outT"></typeparam>
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<Cat, Animal> myList1 = new MyList<Cat, Animal>();
IMyList<Cat, Animal> myList2 = new MyList<Cat, Cat>();//協(xié)變
IMyList<Cat, Animal> myList3 = new MyList<Animal, Animal>();//逆變
IMyList<Cat, Animal> myList4 = new MyList<Animal, Cat>();//逆變+協(xié)變

七、泛型緩存

在前面我們學(xué)習(xí)過,類中的靜態(tài)類型無論實例化多少次,在內(nèi)存中只會有一個。靜態(tài)構(gòu)造函數(shù)只會執(zhí)行一次。在泛型類中,T類型不同,每個不同的T類型,都會產(chǎn)生一個不同的副本,所以會產(chǎn)生不同的靜態(tài)屬性、不同的靜態(tài)構(gòu)造函數(shù),請看下面的例子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyGeneric
{
    public class GenericCache<T>
    {
        static GenericCache()
        {
            Console.WriteLine("This is GenericCache 靜態(tài)構(gòu)造函數(shù)");
            _TypeTime = string.Format("{0}_{1}", typeof(T).FullName, DateTime.Now.ToString("yyyyMMddHHmmss.fff"));
        }

        private static string _TypeTime = "";

        public static string GetCache()
        {
            return _TypeTime;
        }
    }
}

然后新建一個測試類,用來測試GenericCache類的執(zhí)行順序:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MyGeneric
{
    public class GenericCacheTest
    {
        public static void Show()
        {
            for (int i = 0; i < 5; i++)
            {
                Console.WriteLine(GenericCache<int>.GetCache());
                Thread.Sleep(10);
                Console.WriteLine(GenericCache<long>.GetCache());
                Thread.Sleep(10);
                Console.WriteLine(GenericCache<DateTime>.GetCache());
                Thread.Sleep(10);
                Console.WriteLine(GenericCache<string>.GetCache());
                Thread.Sleep(10);
                Console.WriteLine(GenericCache<GenericCacheTest>.GetCache());
                Thread.Sleep(10);
            }
        }
    }
}

Main()方法里面調(diào)用:

GenericCacheTest.Show();

結(jié)果:

從上面的截圖中可以看出,泛型會為不同的類型都創(chuàng)建一個副本,所以靜態(tài)構(gòu)造函數(shù)會執(zhí)行5次。 而且每次靜態(tài)屬性的值都是一樣的。利用泛型的這一特性,可以實現(xiàn)緩存。

注意:只能為不同的類型緩存一次。泛型緩存比字典緩存效率高。泛型緩存不能主動釋放

到此這篇關(guān)于C#泛型詳解的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論