泛型

感谢腾讯课堂软谋教育的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("打乒乓球...");
            }
        }

    }
}
原文地址:https://www.cnblogs.com/JohnTang/p/10906974.html