BigBox777

爱学习、爱运动

  :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: :: 管理 ::

阅读目录

1、介绍

2、实现

  2.1、泛型类

  2.2、泛型方法

  2.3、泛型接口

  2.4、泛型委托

  2.5、泛型约束

3、扩展

  3.1、协变逆变

  3.2、缓存应用

 4、参考

返回系列文章目录 

 

开始阅读 

1、介绍

  在微软的官方文档中这样来描述泛型,泛型将类型参数的概念引入 .NET,这样就可设计具有以下特征的类和方法:在客户端代码声明并初始化这些类或方法之前,这些类或方法会延迟指定一个或多个类型。 我个人的理解是泛型就是在声明的时候泛指一些类型、方法、接口和委托,需要在调用的时候指明。实际应用中解决的问题是把一些不同参数类型参与的重复工作,使用泛型实现来减少代码量。

     

2、实现

  本节会分别介绍泛型类、泛型方法、泛型接口和泛型委托的声明和使用,还会介绍泛型的常用约束及参数out、in修饰符。

2.1、泛型类

1 //声明泛型类
2 public class GenericClass<T>
3 {
4     public void Show(T tPara)
5     {
6         Console.WriteLine($"Type:{typeof(T)},Value:{tPara}");
7     }
8 }
声明泛型类

 

 1 //调用泛型类
 2 static void Main(string[] args)
 3 {
 4     GenericClass<int> genericClass1 = new GenericClass<int>();
 5     genericClass1.Show(12345);
 6 
 7     GenericClass<string> genericClass2 = new GenericClass<string>();
 8     genericClass2.Show("我爱你中国");
 9 
10     Console.ReadKey();
11 }
调用泛型类

 

1 //泛型类继承泛型类
2 public class GenericStudent<T>: GenericClass<T>
3 {
4 }
5 
6 //非泛型类继承泛型类
7 public class Student:GenericClass<string>
8 {
9 }
继承泛型类

 

 1 //官方继承例子
 2 class BaseNode { }
 3 class BaseNodeGeneric<T> { }
 4 
 5 // concrete type
 6 class NodeConcrete<T> : BaseNode { }
 7 
 8 //closed constructed type
 9 class NodeClosed<T> : BaseNodeGeneric<int> { }
10 
11 //open constructed type
12 class NodeOpen<T> : BaseNodeGeneric<T> { }
13 
14 
15 //No error
16 class Node1 : BaseNodeGeneric<int> { }
17 
18 //Generates an error
19 //class Node2 : BaseNodeGeneric<T> {}
20 
21 //Generates an error
22 //class Node3 : T {}
23 
24 
25 class BaseNodeMultiple<T, U> { }
26 
27 //No error
28 class Node4<T> : BaseNodeMultiple<T, int> { }
29 
30 //No error
31 class Node5<T, U> : BaseNodeMultiple<T, U> { }
32 
33 //Generates an error
34 //class Node6<T> : BaseNodeMultiple<T, U> {}
官方继承例子

 

2.2、泛型方法

 1 //声明泛型方法
 2 public class GenericMothed
 3 {
 4     public void ShowInt(int iPara)
 5     {
 6         Console.WriteLine($"type:{typeof(int)},value:{iPara}");
 7     }
 8 
 9     public void ShowString(string sPara)
10     {
11         Console.WriteLine($"type:{typeof(string)},value:{sPara}");
12     }
13 
14     public void ShowPara<T>(T tPara)
15     {
16         Console.WriteLine($"type:{typeof(T)},value:{tPara}");
17     }
18 }
声明泛型方法

 

 1 //调用泛型方法
 2 static void Main(string[] args)
 3 {
 4     GenericMothed genericMothed = new GenericMothed();
 5     //调用普通方法
 6     genericMothed.ShowInt(123);
 7     genericMothed.ShowString("我爱你中国");
 8     //调用泛型方法
 9     genericMothed.ShowPara<int>(123);
10     genericMothed.ShowPara<string>("我爱你中国");
11     Console.ReadKey();
12 }
调用泛型方法

 

2.3、泛型接口

  官方文档建议泛型接口和泛型类结合使用,这样可以避免值类型的装箱和拆箱操作。.NET5框架里的泛型接口都定义在命名空间System.Collections.Generic。

1 //自定义声明泛型接口
2 public interface GenericInterface<T>
3 {
4 }
声明泛型接口

 

 1 //非泛型类继承自定义泛型接口
 2 public class Person: GenericInterface<string>
 3 {
 4 }
 5 
 6 //泛型类继承自定义泛型接口
 7 public class Person<T>: GenericInterface<T>
 8 {
 9 }
10 
11 //继承官方的接口
12 class Stack<T> : IEnumerable<T>
13 {
14     public IEnumerator<T> GetEnumerator()
15     {
16         throw new NotImplementedException();
17     }
18 
19     IEnumerator IEnumerable.GetEnumerator()
20     {
21         throw new NotImplementedException();
22     }
23 }
继承泛型接口

 

2.4、泛型委托

  官方解释委托可以定义它自己的类型参数。 引用泛型委托的代码可以指定类型参数以创建封闭式构造类型,就像实例化泛型类或调用泛型方法一样。 我个人的理解就是委托使用泛型参数就是泛型委托。  

 1 //声明调用泛型委托
 2 public class GenericDelegate
 3 {
 4     delegate void Show<T>(T tPara);
 5     public void Test()
 6     {
 7         Show<string> showT = Sing;
 8         showT.Invoke("我爱你中国");
 9     }
10     public void Sing<T>(T tPara)
11     {
12     }
13 }
声明调用泛型委托

 

2.5、泛型约束

  官方解释约束告知编译器类型参数必须具备的功能。 在没有任何约束的情况下,类型参数可以是任何类型。 编译器只能假定 System.Object 的成员,它是任何 .NET 类型的最终基类。 如果客户端代码使用不满足约束的类型,编译器将发出错误。 通过使用 where 上下文关键字指定约束。   

 1 //单个泛型约束
 2 public class GenericConstraints<T> where T : struct
 3 {
 4 }
 5 
 6 //多个泛型约束
 7 public class GenericConstraints<T, K>
 8     where T : struct
 9     where K : class
10 {
11 }
使用泛型约束

 

 

3、扩展

3.1、协变逆变

  先看看in修饰符官方解释对于泛型类型参数,in 关键字可指定类型参数是逆变的。逆变使你使用的类型可以比泛型参数指定的类型派生程度更小。 这样可以隐式转换实现协变接口的类以及隐式转换委托类型。 引用类型支持泛型类型参数中的协变和逆变,但值类型不支持它们。仅在类型定义方法参数的类型,而不是方法返回类型时,类型可以在泛型接口或委托中声明为逆变。   

 1 // 逆变接口
 2 interface IContravariant<in A> { }
 3 
 4 // 扩展逆变接口
 5 interface IExtContravariant<in A> : IContravariant<A> { }
 6 
 7 // 实现逆变接口
 8 class Sample<A> : IContravariant<A> { }
 9 
10 //使用逆变接口
11 class Program
12 {
13     static void Test()
14     {
15         IContravariant<Object> iobj = new Sample<Object>();
16         IContravariant<String> istr = new Sample<String>();
17 
18         // You can assign iobj to istr because
19         // the IContravariant interface is contravariant.
20         istr = iobj;
21     }
22 }
逆变泛型接口

 

 1 // 逆变委托
 2 public delegate void DContravariant<in A>(A argument);
 3 
 4 // 与委托签名匹配的方法
 5 public static void SampleControl(Control control)
 6 { }
 7 public static void SampleButton(Button button)
 8 { }
 9 
10 // 使用逆变委托
11 public void Test()
12 {
13 
14     // Instantiating the delegates with the methods.
15     DContravariant<Control> dControl = SampleControl;
16     DContravariant<Button> dButton = SampleButton;
17 
18     // You can assign dControl to dButton
19     // because the DContravariant delegate is contravariant.
20     dButton = dControl;
21 
22     // Invoke the delegate.
23     dButton(new Button());
24 }
逆变委托

 

  再来看看out修饰符官方解释对于泛型类型参数,out 关键字可指定类型参数是协变的。协变使你使用的类型可以比泛型参数指定的类型派生程度更大。 这样可以隐式转换实现协变接口的类以及隐式转换委托类型。 引用类型支持协变和逆变,但值类型不支持它们。具有协变类型参数的接口使其方法返回的类型可以比类型参数指定的类型派生程度更大。     

 1 // 协变泛型接口
 2 interface ICovariant<out R> { }
 3 
 4 // 扩展协变泛型接口
 5 interface IExtCovariant<out R> : ICovariant<R> { }
 6 
 7 // 实现协变泛型接口
 8 class Sample<R> : ICovariant<R> { }
 9 
10 // 使用协变泛型接口
11 class Program
12 {
13     static void Test()
14     {
15         ICovariant<Object> iobj = new Sample<Object>();
16         ICovariant<String> istr = new Sample<String>();
17 
18         // You can assign istr to iobj because
19         // the ICovariant interface is covariant.
20         iobj = istr;
21     }
22 }
协变泛型接口

 

 1 // 协变泛型委托
 2 public delegate R DCovariant<out R>();
 3 
 4 // 与委托签名匹配的方法
 5 public static Control SampleControl()
 6 { return new Control(); }
 7 
 8 public static Button SampleButton()
 9 { return new Button(); }
10 
11 // 使用协变泛型委托
12 public void Test()
13 {
14     // Instantiate the delegates with the methods.
15     DCovariant<Control> dControl = SampleControl;
16     DCovariant<Button> dButton = SampleButton;
17 
18     // You can assign dButton to dControl
19     // because the DCovariant delegate is covariant.
20     dControl = dButton;
21 
22     // Invoke the delegate.
23     dControl();
24 }
协变泛型委托

 

3.2、缓存应用

  由于泛型的不同泛型类型都会生成一个副本,我们就可以利用这个原理实现一个简单的内存缓存。为了使全局只有一个实例,可以把缓存的字段设置为静态的。

 1 //定义泛型缓存类
 2 public class GenericCache<T> where T:class
 3     {
 4         private static readonly List<T> _list = new List<T>();
 5         public static void Add(T tPara)
 6         {
 7             _list.Add(tPara);
 8         }
 9         public static void Remove(T tPara)
10         {
11             if(_list.Contains(tPara))
12             {
13                 _list.Remove(tPara);
14             }
15         }
16         public static List<T> GetAll()
17         {
18             return _list;
19         }
20     }
定义泛型缓存类

 

1 //定义泛型缓存用的模型
2 public class Demo
3 {
4     public int Id { get; set; }
5     public string Name { get; set; }
6 }
定义泛型缓存用的模型

 

 1 //使用泛型缓存类
 2 static void Main(string[] args)
 3 {
 4     GenericCache<string>.Add("我爱你中国");
 5     GenericCache<string>.Add("春眠不觉晓");
 6     GenericCache<string>.Add("处处闻啼鸟");
 7     GenericCache<string>.Add("我爱你中国");
 8 
 9     GenericCache<Demo>.Add(new Demo { Name = "C#" });
10     GenericCache<Demo>.Add(new Demo { Name = "JAVA" });
11     GenericCache<Demo>.Add(new Demo { Name = "Python" });
12     Console.WriteLine("*****打印字符串缓存数据*****");
13     foreach (var item in GenericCache<string>.GetAll())
14     {
15         Console.WriteLine(item);
16     }
17     Console.WriteLine("*****打印自定义类缓存数据*****");
18     foreach (var item in GenericCache<Demo>.GetAll())
19     {
20         Console.WriteLine(item.Name);
21     }
22 
23     Console.ReadKey();
24 }
使用泛型缓存类

 

 4、参考

泛型 - C# 编程指南 https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/

泛型类 - C# 编程指南 https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/generic-classes

泛型方法 - C# 编程指南 https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/generic-methods

泛型接口 - C# 编程指南 https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/generic-interfaces

泛型委托 - C# 编程指南 https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/generic-delegates

where(泛型类型约束)- C# 参考 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/where-generic-type-constraint

类型参数的约束(C# 编程指南) https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters

out 关键字(泛型修饰符) - C# 参考 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/out-generic-modifier

in(泛型修饰符) - C# 参考 https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/in-generic-modifier

泛型中的协变和逆变 https://docs.microsoft.com/zh-cn/dotnet/standard/generics/covariance-and-contravariance

 

posted on 2021-02-20 13:35  BigBox777  阅读(762)  评论(0编辑  收藏  举报