zoukankan      html  css  js  c++  java
  • 泛型

    感谢腾讯课堂软谋教育的Eleven老师对于泛型的详细讲解。

    PS:如有不足之处,还望大家多多指教,万分感谢。

    泛型概念  

    泛型(generic)是C#语言2.0和通用语言运行时(CLR)的一个新特性。泛型为.NET框架引入了类型参数(type parameters)的概念。类型参数使得设计类和方法时,不必确定一个或多个具体参数,其的具体参数可延迟到客户代码中声明、实现

    泛型类和泛型方法兼复用性、类型安全和高效率于一身,是与之对应的非泛型的类和方法所不及。泛型广泛用于容器(collections)和对容器操作的方法中。.NET框架2.0的类库提供一个新的命名空间System.Collections.Generic,其中包含了一些新的基于泛型的容器类。要查找新的泛型容器类(collection classes)的示例代码,请参见基础类库中的泛型。当然,你也可以创建自己的泛型类和方法,以提供你自己的泛化的方案和设计模式,这是类型安全且高效的。

    泛型的声明和使用

    泛型类的定义

       访问修饰符 class ClassName<T>{}

       示例:public class GenericClass<T>{}

    泛型方法的定义

       访问修饰符 返回值类型 methodName(参数类型/参数类型列表){}

       示例: 

        publice T GenericMethod<T,M>(){}//声明一个带返回值(泛型)的方法,有N个泛型参数

        调用时需要注意:必须指定类型参数(泛型方法可以不知道是什么类型,但是调用者必须知道是什么类型)

        .GenericMethod<int,string>(tempInt,tempString);//必须和声明时的个数保持一致,<>里的类型和()的参数的类型必须是一致。

    泛型接口的定义

        申明接口的关键字  interfaceName<T.....>{ 返回值类型 methodName(参数类型 参数名称);}

        interface interfaceName<T>{ void method(T s1)}//这里举例用泛型

       继承

        类:接口名称<这里需要指定具体类型>//例如string.....

        Class :interfaceName<string>//快捷实现接口的method方法

        publice void method<T>(T s1){//方法体}

       调用

       实例化Class.method<string>("嘿嘿");//调用时必须指定类型和具体值.

    泛型委托的定义

       访问修饰符 delegate 返回类型 delegateName<T>(T t1......);//这里用返回值(泛型)、带参数(泛型)举例

       public delegate T DelegateName<T>(T t1);//声明委托,这里用返回值(泛型)、带参数(泛型)举例

       //定义委托方法

        publice T tempMethod<T>(T t1){ return  t1;}//这里省略,只是简单返回值

      //在方法内部实例化委托

       {

        //语法糖,可以把原来的=new DelegateNmae<string>(tempMethod<string>)省略

        DelegateName<string> tempName=tempMethod<string>;//实例化委托的时候必须给定具体类型

         tempName.Invoke("测试");//传递具体参数    

    }

    类型参数的约束

        为什么要有约束?  因为有约束才有权利   

    约束使得泛型类能够使用其他实例的属性,因为所有为类型T的元素,都是一个对象或是一个继承自Employee的对象。

    public class Employee
    {
     public class Employee
        {
            private string name;
            private int id;
            public Employee(string s, int i)
            {
                name = s;
                id = i;
            }
     
            public string Name
            {
                get { return name; }
                set { name = value; }
            }
            public int ID
            {
                get { return id; }
                set { id = value; }
            }
     
        }
    }
    class MyList<T> where T: Employee
    {
     //Rest of class as before.
      public T FindFirstOccurrence(string s)
      {
       T t = null;
       Reset();
       while (HasItems())
       {
          if (current != null)
          {
    //The constraint enables this:
             if (current.Data.Name == s)
             {
                t = current.Data;
                break;
             }
             else
             {
                current = current.Next;
             }
          } //end if
       } // end while
      return t;
      }
    }
     

    泛型约束的几种常见类型:

    约束

    描述

    where T: struct

    类型参数必须为值类型。

    where T : class

    类型参数必须为引用类型。

    where T : new()

    类型参数必须有一个公有、无参的构造函数。当于其它约束联合使用时,new()约束必须放在最后。

    where T : <base class name>

    类型参数必须是指定的基类型或是派生自指定的基类型。

    where T : <interface name>

    类型参数必须是指定的接口或是指定接口的实现。可以指定多个接口约束。接口约束也可以是泛型的。

    针对早期版本的通用语言运行时和C#语言的局限,泛型提供了一个解决方案。以前类型的泛化(generalization)是靠类型与全局基类System.Object的相互转换来实现。.NET框架基础类库的ArrayList容器类,就是这种局限的一个例子。ArrayList是一个很方便的容器类,使用中无需更改就可以存储任何引用类型或值类型。

    //The .NET Framework 1.1 way of creating a list
    ArrayList list1 = new ArrayList(); 
    list1.Add(3);
    list1.Add(105);
    //...
    ArrayList list2 = new ArrayList();
    list2.Add(“It is raining in Redmond.”);
    list2.Add("It is snowing in the mountains.");
    //...

    但是这种便利是有代价的,这需要把任何一个加入ArrayList的引用类型或值类型都隐式地向上转换成System.Object。如果这些元素是值类型,那么当加入到列表中时,它们必须被装箱;当重新取回它们时,要拆箱。类型转换和装箱、拆箱的操作都降低了性能;在必须迭代(iterate)大容器的情况下,装箱和拆箱的影响可能十分显著。

    另一个局限是缺乏编译时的类型检查,当一个ArrayList把任何类型都转换为Object,就无法在编译时预防客户代码类似这样的操作:

    ArrayList list = new ArrayList(); 
    //Okay.  
    list.Add(3); 
    //Okay, but did you really want to do this?
    list.Add(.“It is raining in Redmond.”);
     
    int t = 0;
    //This causes an InvalidCastException to be returned.
        foreach(int x in list)
    {
      t += x;
    }

    下面的示例代码以一个简单的泛型链表类作为示范。(多数情况下,推荐使用由.NET框架类库提供的List<T>类,而不是创建自己的表。)类型参数T在多处使用,具体类型通常在这些地方来指明表中元素的类型。类型参数T有以下几种用法:

            AddHead方法中,作为方法参数的类型。

            在公共方法GetNext中,以及嵌套类NodeData属性中作为返回值的类型。

            在嵌套类中,作为私有成员data的类型。

    注意一点,T对嵌套的类Node也是有效的。当用一个具体类来实现MyList<T>——MyList<int>——每个出现过的T都要用int代替。

    using System;
    using System.Collections.Generic;
     
    public class MyList<T> //type parameter T in angle brackets
        {
            private Node head;
    // The nested type is also generic on T.
            private class Node          
            {
                private Node next;
    //T as private member data type:
                private T data;         
    //T used in non-generic constructor:
                public Node(T t)        
                {
                    next = null;
                    data = t;
                }
                public Node Next
                {
                    get { return next; }
                    set { next = value; }
                }
            //T as return type of property:
                public T Data           
                {
                    get { return data; }
                    set { data = value; }
                }
            }
            public MyList()
            {
                head = null;
            }
         //T as method parameter type:
            public void AddHead(T t)    
            {
                Node n = new Node(t);
                n.Next = head;
                head = n;
            }
            public IEnumerator<T> GetEnumerator()
            {
                Node current = head;
     
                while (current != null)
                {
                    yield return current.Data;
                    current = current.Next;
                }
            }
        }

    下面的示例代码演示了客户代码如何使用泛型类MyList<T>,来创建一个整数表。通过简单地改变参数的类型,很容易改写下面的代码,以创建字符串或其他自定义类型的表。

    class Program
        {
            static void Main(string[] args)
            {
    //int is the type argument.
               MyList<int> list = new MyList<int>();
                for (int x = 0; x < 10; x++)
                    list.AddHead(x);
     
                foreach (int i in list)
                {
                    Console.WriteLine(i);
                }
                Console.WriteLine("Done");
            }
        }


    泛型补充: 完整代码分享

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using static MyGeneric.Model;
    
    namespace MyGeneric
    {
    
        //1 引入泛型:延迟声明
        //2 如何声明和使用泛型
        //3 泛型的好处和原理
        //4 泛型类、泛型方法、泛型接口、泛型委托
        //5 泛型约束
    
        class Program
        {
           public static void Main(string[] args)
            {
                //出现时间: 泛型的引入是从.Net FrameWork 2.0开始
                //出现作用: 
                //泛型为.Net框架引入了类型参数的概念,类型参数的概念使得实际类或方法时,不必确定一个或多个参数
                //其具体参数类型可以在调用时去指定
    
                //在早期我们需要为不同的参数类型去写不同的方法
                CommonMenthod cmd = new CommonMenthod();
                //cmd.ShowInt(123);
                //cmd.ShowString("Hello");
    
                //又或者使用Object,但是使用Object就会涉及到拆箱装箱的问题,会影响到效率
                //cmd.ShowObject(123);//所有父类出现的地方都能用子类代替,Object是一切类型的父类
                //cmd.ShowObject("Hello");
    
                //泛型之后我们可以用一个泛型方法满足不同的需求
                //cmd.ShowGeneric(123);//如果可以根据参数推算出类型,可以省略类型
                //cmd.ShowGeneric("Hello");
    
    
                //哪里用泛型? 泛型到底是干嘛的?
                //泛型方法:为了一个方法满足不同的类型的需求
                //泛型类:一个类,满足不同类型的需求  如:List Dictionary
                //泛型接口:一个接口,满足不同类型的需求
                //泛型委托:一个委托,满足不同类型的需求
    
    
    
                //为什么要有泛型约束:
                //泛型参数有哪些:
    
                //where T: struct 类型参数必须为值类型。
                //where T : class 类型参数必须为引用类型。
                //where T : new() 类型参数必须有一个公有、无参的构造函数。当于其它约束联合使用时,new() 约束必须放在最后。
                //where T : <base class name>    类型参数必须是指定的基类型或是派生自指定的基类型。
                //where T : <interface name>    类型参数必须是指定的接口或是指定接口的实现。可以指定多个接口约束。接口约束也可以是泛型的。
    
                People people = new People()
                {
                    Id = 123,
                    Name="张三"
                };
    
                Chinese chinese = new Chinese()
                {
                    Id=345,
                    Name="李小龙"
                };
    
                Hubei hubei = new Hubei()
                {
                    Id=456,
                    Name="老王"
                };
    
                Japanese japanese = new Japanese()
                {
                    Id=567,
                    Name="矢野浩二"
                };
    
                //没有约束,任何类型都能传递进来,所以可能不安全,也不够灵活
                // GenericConstraint.ShowObject(people);
                //这里传入japanese在运行时候会报错,因为japanese并没有继承People
                // GenericConstraint.ShowObject(japanese);
    
    
                //GenericConstraint.Show(people);
                GenericConstraint.Show(chinese);
    
    
                //WebServices WCF 都不能用泛型,为什么?
                //跨语言的,别的语言也能用,不支持泛型。。
                //服务在发布的时候是必须确定的,泛型在编译时确定不了
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MyGeneric
    {
        public class CommonMenthod
        {
    
    
            public void ShowInt(int iParameter)
            {
                Console.WriteLine(iParameter);
            }
    
            public void ShowString(string sParameter)
            {
                Console.WriteLine(sParameter);
            }
    
            public void ShowDatetime(DateTime dtParameter)
            {
                Console.WriteLine(dtParameter);
            }
    
    
            public void ShowObject(object oParameter)
            {
                Console.WriteLine("This is {0},parameter={1},type={2}",
               typeof(CommonMenthod), oParameter.GetType().Name, oParameter);
            }
    
            /// <summary>
            /// 泛型为什么可以支持任何类型
            ///   因为T不知道是什么类型,在使用的时候才能确定
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="tParameter"></param>
            public void ShowGeneric<T>(T tParameter)
            {
                Console.WriteLine(tParameter);
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using static MyGeneric.Model;
    
    namespace MyGeneric
    {
       public  class  GenericConstraint
        {
    
            /// <summary>
            /// 泛型约束
            /// </summary>
            /// <param name="oParameter"></param>
            public static void ShowObject(object oParameter)
            {
                Console.WriteLine("This is {0},parameter={1},type={2}",
                    typeof(GenericConstraint), oParameter.GetType().Name, oParameter);
                People people = (People)oParameter;
                Console.WriteLine($"{people.Id}  {people.Name}");
            }
    
    
    
            public static void Show<T>(T tParameter)
                //where  T: People //约束,只要是继承了People的就可以
                   where T : People, ISports, IWork, new()//这里的是要同时满足几种需求,具体灵活使用
            {
    
                Console.WriteLine("This is {0},parameter={1},type={2}",
                    typeof(GenericConstraint), tParameter.GetType().Name, tParameter);
      
                Console.WriteLine($"{tParameter.Id}  {tParameter.Name}");
              
            }
        }
    }
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace MyGeneric
    {
        public class Model
        {
    
    
            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 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("打乒乓球...");
                }
            }
    
        }
    }
  • 相关阅读:
    TensorFlow Executor解析
    面试复习
    [洛谷]P1880 石子合并问题
    [西建大ACM协会]OJ平台如何使用
    [ACM] 相关OJ及在线运行代码网站
    [MySQL] Win10 安装MySQL5.7.27
    [PTA] PAT(A) 1012 The Best Rank (25 分)
    [PTA] PAT(A) 1011 World Cup Betting (20 分)
    [PTA] PAT(A) 1010 Radix (25 分)
    [PTA] PAT(A) 1009 Product of Polynomials (25 分)
  • 原文地址:https://www.cnblogs.com/JohnTang/p/10906974.html
Copyright © 2011-2022 走看看