最稳的pk10计划iphone 北京pk10计划手机软件 北京pk10数字的规律 超神手机版pk10软件 pk10北京赛车9码技巧 pk10四期倍投计划表 pk10极速赛车论坛 北京赛车冠军怎样选5码 北京赛车系统下载安装 pk10教程视频 北京pk10选号公式 北京赛车pk10赚钱技巧 北京赛车怎么提升概率 pk10技巧北京快三 北京pk10大小计划
VB.net 2010 視頻教程 VB.net 2010 視頻教程 VB.net 2010 視頻教程
SQL Server 2008 視頻教程 c#入門經典教程 Visual Basic從門到精通視頻教程
當前位置:
首頁 > 編程開發 > .net教程 >
  • ASP.net教程之第一課《.net之--泛型》

  • 2019-06-18 21:31 來源:未知

  今天我來學習泛型,泛型是編程入門學習的基礎類型,從.net誕生2.0開始就出現了泛型,今天我們開始學習泛型的語法和使用。

  什么是泛型?

  泛型(generic)是C#語言2.0和通用語言運行時(CLR)的一個新特性。泛型為.NET框架引入了類型參數(type parameters)的概念。類型參數使得設計類和方法時,不必確定一個或多個具體參數,其的具體參數可延遲到客戶代碼中聲明、實現。這意味著使用泛型的類型參數T,寫一個類MyList<T>,客戶代碼可以這樣調用:MyList<int>, MyList<string>或 MyList<MyClass>。這避免了運行時類型轉換或裝箱操作的代價和風險。

  上面是官方腔調,我說人話:泛型就是為了滿足不同類型,相同代碼的重用!

 

  為什么要有泛型?

  下面我們舉例子來講解為什么會要泛型,以下我列舉了三個例子來講解:

   我們列舉了ShowInt,ShowString,ShowDatatime三個方法,如果我們在程序中每封裝一個方法有不同參數,就要像下面這樣寫一個函數的話,代碼會變得很累贅,在調用的時候必須傳遞吻合的參數,所以不得不寫出了3個方法,那有沒有好的方法來解決這樣的問題呢?答案是當然有,微軟是很聰明的,傾聽小滿哥慢慢來講。

復制代碼
        /// <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>
        /// 打印個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);
        }
     
     定義一些測試變量

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

      普通方式調用演示
      ShowInt(iValue);
      ShowInt(sValue);//這樣不行類型必須吻合
      ShowString(sValue);
      ShowDateTime(dtValue);

復制代碼

  在.net 1.0的時候微軟出現了object這個概念,下面有一個方法ShowObject,你們就會發現不管參數是int srtring datetime 我們都可以調用ShowObject來操作實現,那為什么會這樣呢?

  1:Object類型是一切類型的父類。

  2:任何父類出現的地方,都可以用子類來代替。

出現這個基本滿足了開發人員的一些需求,在.net1.0和1.1的時候,這個時候還沒有泛型就用object代替。

復制代碼
        /// <summary>
        /// 打印個object值
        /// 1 object類型是一切類型的父類
        /// 2 任何父類出現的地方,都可以用子類來代替
        /// .Net Framework 1.0 1.1
        /// </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);
        } 
     定義一些測試變量

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

     object方式調用演示

      ShowObject(oValue);
      ShowObject(iValue);
      ShowObject(sValue);
      ShowObject(dtValue);


	
復制代碼

 

  接著吹牛比,勞動人民的智慧還是很屌的,經過之前的經歷在.net2.0的時候,微軟讓主角登場了"泛型",當然同時出現的還有“線程池”這個我們先不講,回到正軌什么是泛型?你們在開發的時候有沒有用過List<T>這個集合?這個就是泛型,深化下直接舉個栗子吧,下面我寫一個例子Show<T>(T tParameter)看下泛型的寫法:

  有個毛用?下面這個方法也可以向上面ShowObject一樣,你們會發現不管參數是int srtring datetime 我們也可以調用Show<T>(T tParameter)來操作實現,替換了ShowObject這個方法的實現,具備了滿足了不同參數類型的方法實現,更適用性,泛型方法聲明的時候,沒有指定類型,而是調用的時候指定,具有延遲聲明和延遲思想,這個思想對我們在開發框架的時候灰常有用,不得不說老外這點還是很幾把厲害(還是我們被洗腦了?也許吧,我相信等中文編程語言出來估計會更屌,中華文化博大精深嘛),現在小伙伴們是不是大概了解泛型的基礎作用了?

復制代碼
        /// <summary>
        /// 泛型方法聲明的時候,沒有指定類型,而是調用的時候指定
        /// 延遲聲明:把參數類型的聲明推遲到調用
        /// 延遲思想:推遲一切可以推遲的
        /// 
        /// 2.0泛型不是語法糖,而是由框架升級提供的功能
        /// 泛型方法性能上和普通方法差不多的/// </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());
        }
     調用演示

      Show<object>(oValue);
      Show<int>(iValue);
      Show(iValue);//可以去掉,自動推算
      Show<string>(iValue);//必須匹配
      Show<string>(sValue);
      Show<DateTime>(dtValue);

復制代碼

    那問題來了,既然都可以實現為什么要用這個呢?我們做事凡事都要帶著疑問去看待,有些事別人說好,但真的好不好我們要自己親自試試才知道,我們最關注的的效率問題,下面是測試代碼:

復制代碼
     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);
            }
        }
復制代碼

  結果如下:commonSecond=508,objectSecond=916,genericSecond=452   你會發現普通方法508ms,object方法916ms,泛型是452ms,其中object最慢,為什么最慢呢?因為object會經過一個裝箱拆箱的過程,所以性能上會損失一些,但是在我看來這樣上億次這點損耗,算不了什么,但是可以證明泛型和普通類型速度是差不多的,這一點可以認可泛型還是性能挺好的,這個可以推薦使用泛型的理由之一。

  但泛型僅僅表現在這個層面嗎?遠遠不止,我們用泛型遠遠不是為了提升剛剛那點性能,為什么要用泛型?答案來了,我們要用泛型就是為了滿足不同類型,相同代碼的重用,下面我繼續舉栗子:

   泛型的一些用法,泛型只有四種用途,泛型類,泛型接口,泛型委托,泛型方法,如下:

1
2
3
4
<strong>    public class GenericClass<T>
    {
        public T Property { getset; }
    }</strong>

   public interface IGenericInterface<T>
     {

     }

   public delegate void DoNothing<T>();

    調用演示

    List<int> intList = new List<int>();//原生態List類
    List<string> stringList = new List<string>();

    GenericClass<int> iGenericClass = new GenericClass<int>();
    iGenericClass.Property = 1;

    GenericClass<string> sGenericClass = new GenericClass<string>();
    sGenericClass.Property = "1233";

    DoNothing<int> method = new DoNothing<int>(() => { });

    還有一種,別被嚇到:T,S,Xiaomange這些語法,只是占位符別怕,你可以自己定義的,在你調用的時候確定類型就OK了,好了差不多能理解泛型了吧?再說一次泛型就是為了滿足不同類型,相同代碼的重用。

    public class ChildClassGeneric<T, S, XiaoManGe> : GenericClass<T>, IGenericInterface<S>
    {

    }

  接下來我們來聊一聊拓展的一部分,好像泛型很吊的樣子感覺什么都能用泛型類型代替,但是天下哪有那么好的事情,雙刃劍的道理都懂,所以出現了泛型的約束這個緊箍咒。

  泛型的約束

  直接來代碼:

  很簡單的一個例子,接口ISports,IWork,People類,Japanese類等簡單繼承了一下,目前準備的一些代碼。

復制代碼
  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()
        { }
    }

    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 id)
        {
        }
        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 Pingpang()
        {
            Console.WriteLine("打乒乓球...");
        }
        public void Hi()
        { }
    }
復制代碼

  再來個調用類Constraint

復制代碼
 public class Constraint
    {
        /// <summary>
        /// 代碼編譯沒問題,執行的時候才報錯
        /// 代碼安全問題
        /// </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);

            People people = (People)oParameter;

            Console.WriteLine(people.Id);//這里就不行了 代碼安全問題,調用不到,但編譯不會報錯。
            Console.WriteLine(people.Name);
        }

        /// <summary>
        /// 基類約束:
        /// 1 帶來權力,可以使用基類里面的屬性和方法
        /// 2 帶來義務,類型參數必須是基類或者其子類
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tParameter"></param>
        public static void Show<T>(T tParameter)
            where T : People, ISports, new()//都是and 關系
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
                typeof(GenericMethod), tParameter.GetType().Name, tParameter.ToString());

            Console.WriteLine(tParameter.Id);
            Console.WriteLine(tParameter.Name);
            tParameter.Hi();
            tParameter.Pingpang();
            T t = new T();
        }

        public static void ShowPeople(People tParameter)
        {
            Console.WriteLine("This is {0},parameter={1},type={2}",
                typeof(GenericMethod), tParameter.GetType().Name, tParameter.ToString());

            Console.WriteLine(tParameter.Id);
            Console.WriteLine(tParameter.Name);
            tParameter.Hi();
            //tParameter.Pingpang();
        }

        public static T DoNothing<T>(T tParameter)
            //where T : ISports//接口約束
            //where T : class//引用類型約束
            //where T : struct//值類型約束
            where T : new()//無參數構造函數約束
        {
            //tParameter.Pingpang();
            //return null;
            T t = new T();
            return default(T);//會根據T的類型,去產生一個默認值
        }
    }
復制代碼

  有興趣的可以測試下,用ShowObject的方法和泛型Show<T>(T tParameter)調用來看差異,如果不加入約束,想調用參數的屬性和方法,代碼安全問題是調用不了的,會報錯,但是加入基類約束之后是可以調用到的,所以泛型約束帶來了權利,可以使用基類的屬性和方法,但也帶來義務,參數只能是基類和子類,又想馬兒跑,又想馬兒不吃草的事情是沒有的,權利和義務是相對的,在享受權利的同時也會帶來義務。

  其次,約束可以多重約束,然后即可作為參數約束也可以作為返回值約束,例如default(T)會根據泛型類型返回一個默認值,如果是無參數構造約束就可以類似這樣寫返回值T t=new T()。

  總之,我覺得泛型約束為了更靈活的滿足不同條件的需求而產生的,就是我們在寫一些固定的需求,約束疊加來完成我們的功能,同時不讓泛型肆無忌憚。

        泛型約束范圍如下:

約束

描述

where T: struct

類型參數必須為值類型。

where T : class

類型參數必須為引用類型。

where T : new()

類型參數必須有一個公有、無參的構造函數。當于其它約束聯合使用時,new()約束必須放在最后。

where T : <base class name>

類型參數必須是指定的基類型或是派生自指定的基類型。

where T : <interface name>

類型參數必須是指定的接口或是指定接口的實現。可以指定多個接口約束。接口約束也可以是泛型的。

  好了泛型的約束的先說到這,繼續套底子,了解到的都倒出來。

 

  逆變和協變

  逆變和協變不知道有沒有小伙伴熟悉的,我開始是不知道這個的,第一次看到也是一臉懵逼,到現在也有點迷糊,能不能講清楚看造化了,哈哈

   繼續舉栗子:

//協變
public interface IEnumerable<out T> : IEnumerable
//逆變
public delegate void Action<in T>(T obj);

  這段代碼是不是很熟悉?里面有個Out T 還有 in T,這里的Out 不是我們熟悉的參數返回Out,ref的作用,是協變專用的,逆變和協變指出現在接口或者委托泛型前面,

  In只能作為參數傳入,Out只能作為參數傳出。

  下面來個代碼  

  

復制代碼
    public class Bird
    {
        public int Id { get; set; }
    }
    public class Sparrow : Bird
    {
        public string Name { get; set; }
    }

      調用實例
      IEnumerable<int> intList = new List<int>();
      Action<int> iAction = null;
               
       Bird bird1 = new Bird();
       Bird bird2 = new Sparrow();//左邊是父類  右邊是子類
       Sparrow sparrow1 = new Sparrow();
        //Sparrow sparrow2 = new Bird();//不是所有的鳥,都是麻雀
        

        List<Bird> birdList1 = new List<Bird>();//一群鳥 是一群鳥
        //List<Bird> birdList2 = new List<Sparrow>();//一群麻雀難道不是一群鳥  ? 不是的,沒有父子關系
        List<Bird> birdList3 = new List<Sparrow>().Select(c => (Bird)c).ToList();//這里使用別扭了,明明知道但就是不能這樣寫
復制代碼

  以上代碼發現問題了嗎?很明顯出現了一些不和諧的地方,我們換個方式如下: 

復制代碼
      
      
復制代碼
   /// <summary>
    /// 逆變:只能修飾傳入參數
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ICustomerListIn<in T>
    {
        //T Get();

        void Show(T t);
    }

    public class CustomerListIn<T> : ICustomerListIn<T>
    {
        //public T Get()
        //{
        //    return default(T);
        //}

        public void Show(T t)
        {
        }
    }

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

        //void Show(T t);//不能做參數
    }

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

        //public void Show(T t)
        //{

        //}
    }
復制代碼

 

          
        {//協變:接口泛型參數加了個out,就是為了解決剛才的不和諧
                IEnumerable<Bird> birdList1 = new List<Bird>();
                IEnumerable<Bird> birdList2 = new List<Sparrow>();

                //Func<Bird> func = new Func<Sparrow>(() => null);

                ICustomerListOut<Bird> customerList1 = new CustomerListOut<Bird>();
                ICustomerListOut<Bird> customerList2 = new CustomerListOut<Sparrow>();
            }

            {//逆變
                ICustomerListIn<Sparrow> customerList2 = new CustomerListIn<Sparrow>();
                ICustomerListIn<Sparrow> customerList1 = new CustomerListIn<Bird>();

                //customerList1.Show()

                ICustomerListIn<Bird> birdList1 = new CustomerListIn<Bird>();
                birdList1.Show(new Sparrow());
                birdList1.Show(new Bird());

                Action<Sparrow> act = new Action<Bird>((Bird i) => { });
            }
復制代碼

  這樣可以了,協變IEnumerable加入協變Out 左邊是個父類,右邊可以是子類,逆變In 左邊是個字類,右邊也可以是父類,下面這段就更暈了,稍微看下吧。

       {
                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>();//協變+逆變
            }

  總結下原理:泛型在JIT編譯時指定具體類型,同一個泛型類,不同的類型參數,其實會變成不用的類型。

  我走的很慢,但從不后退!

pk10赛车冠军技巧
最稳的pk10计划iphone 北京pk10计划手机软件 北京pk10数字的规律 超神手机版pk10软件 pk10北京赛车9码技巧 pk10四期倍投计划表 pk10极速赛车论坛 北京赛车冠军怎样选5码 北京赛车系统下载安装 pk10教程视频 北京pk10选号公式 北京赛车pk10赚钱技巧 北京赛车怎么提升概率 pk10技巧北京快三 北京pk10大小计划
twiter是怎么赚钱的 小型幼儿园怎么赚钱 河南快赢481开奖视直播 广东快乐10分软件下载 双色球机选 期货跨期套利稳赚不赔吗 青海11选5开奖直播 妙巴黎如何赚钱 十一运夺金今日预测杀码号