.NET泛型解析(下)

上一篇对.NET中的泛型进行了详细的介绍以及使用泛型的好处是什么,这篇将更加深入的去了解泛型的其他的知识点,重头戏.

【1】泛型方法

上一篇我们也说过了,泛型可以是类,结构,接口,在这些泛型类型中定义的方法都可以叫做泛型方法,都可以引用由泛型类型本身指定的一个类型参数例如:

1 public class GenericType<T>
2 {
3
4      private T G_Value;
5
6      public T Convert<T> { T res = (T)Convert.ChangeType(G_value,typeof(T)); return res; }
7
8 }

泛型方法的存在为我们提供了极大的灵活性。

泛型方法顾名思义,首先它是一个方法,只不过这个方法的返回值,参数可能是泛型的,

类型推断

类型推断就是根据泛型方法中定义的形参类型T,在我们调用的使用,那么所要传的也应该是一致的。例如:

1 static void Test<T >(ref T t1,ref T t2)
2 {
3        T temp = t1;
4        t1 = t2;
5        t2 = temp;
6 }

这是我们定义的一个泛型方法,第一个参数类型为 T,第二个也为 T,说明这两个类型是一致的。那么我们在调用的时候应该这样 :

1   int i = 8, j = 0 ;
2
3   Test( ref i, ref j);

而如果我们两个实参的类型不统一的话,那么就会引发编译异常例如 :

开放类型和封闭类型

具有泛型类型参数的类型对象让就还是一个类型, CLR内部依旧和其他的对象类型一样创建一个内部类型对象, 那么这种具有泛型类型参数的类型称之为开放类型,开发类型不能进行构造任何的实例.

当泛型类型参数是一个实参时,那么所有的类型实参传递的都是实际的数据类型了,这种类型称之为 封闭类型,封闭类型是允许构造实例对象的

 1  class DictionaryStringKey <T> : IEnumerable<T>
 2  {
 3        public IEnumerator<T> GetEnumerator()
 4        {
 5            throw new NotImplementedException();
 6        }
 7
 8        System .Collections. IEnumerator System. Collections.IEnumerable .GetEnumerator()
 9        {
10            throw new NotImplementedException();
11        }
12  }

在上述代码中,有一个DictionaryStringKey的类,继承了一个IEnumable<T>的泛型,那么根据我们的开放类型它在调用的时候是不只是具体的实参,并且不能被构造实例的.

 1          static void Main( string[] args)
 2         {
 3             object o = null;
 4             Type t = typeof( DictionaryStringKey <>);
 5            o =   CreateInstance(t);
 6            Console .ReadLine();
 7
 8         }
 9
10         private static object CreateInstance( Type t)
11         {
12             object o = null;
13             try
14             {
15                 o = Activator . CreateInstance(t);
16                 Console .WriteLine( @" 创建了 " + t + "的实例 " , t .ToString());
17             }
18             catch (Exception ex)
19             {
20                 o = Activator . CreateInstance(t);
21                 Console .WriteLine( @" 创建 " + t + "的实例失败 " , t .ToString());
22             }
23
24             return o;
25         }

上述代码中我尝试着对开放类型进行实例化,结果报出异常 :

当我们传入实参之后,那么它就是一个封闭类型,这样我们就可以进行构造实例了.

1   static void Main( string[] args)
2         {
3             object o = null;
4             Type t = typeof( DictionaryStringKey <string > );
5            o =   CreateInstance(t);
6            Console .ReadLine();
7
8         }

 【2】泛型接口

泛型的主要作用就是定义泛型的引用类型和值类型,在CLR中,对于泛型接口的支持也是很重要的,因为这样更有益与程序的扩展性, 另外一点如果没有泛型接口我们每次使用非泛型接口时都会进行装箱操作(例如:  IComparable),并且会失去编译时的安全性(因为它是非泛型的,较之泛型的好处之一).

那么CLR提供了对于泛型接口的支持,对于引用类型和值类型可以通过制定实参的方式来实现泛型接口,同时也可以保持未指定状态来实现一个泛型接口( 接受者也可以是泛型 , 可以在后期进行指定类型)

实例 1 : 声明一个泛型接口

1      interface IPair <T>
2     {
3         T First { get; set ; }
4         T Second { get; set ; }
5     }

IPair这个接口实现了一对相关的对象,两个数据项具有相同的类型.

实现接口时,语法与非泛型类的语法是相同的,如果在实现泛型接口时,同时不指定实参,则默认为是一个泛型类.

实例 2 : 泛型接口的实现

 1      class Pair <T> : IPiar< T>
 2     {
 3         public T First
 4         {
 5             get
 6             {
 7                 throw new NotImplementedException();
 8             }
 9             set
10             {
11                 throw new NotImplementedException();
12             }
13         }
14
15         public T Second
16         {
17             get
18             {
19                 throw new NotImplementedException();
20             }
21             set
22             {
23                 throw new NotImplementedException();
24             }
25         }
26     }

CLR泛型接口的支持对于集合类型来讲尤其重要,同时泛型在集合类型中较为常用,如果没有泛型那么我们所使用List的时候都要依赖与System.Collections,我们在每次访问的时候都需要执行一次转换,如果使用泛型接口,就可以避免执行转型,因为参数化的接口能实现更强的编译时绑定.

实例 3 : 在一个类中多次实现相同的接口

 1     interface IContainer <T>
 2    {
 3        ICollection<T > Items { get ; set; }
 4    }
 5
 6     public class Person : IContainer<A >,IContainer <B>
 7     {
 8
 9         public ICollection <A> Items
10         {
11             get
12             {
13                 throw new NotImplementedException();
14             }
15             set
16             {
17                 throw new NotImplementedException();
18             }
19         }
20
21         ICollection<B > IContainer <B>. Items
22         {
23             get
24             {
25                 throw new NotImplementedException();
26             }
27             set
28             {
29                 throw new NotImplementedException();
30             }
31         }
32     }

Items属性使用一个显式接口实现多次出现,每一次,类型参数都有所不同,如果没有泛型,这是做不到的,在非泛型的情况下,CLR只允许一个显式的IContainer.Items属性.

【4】泛型约束(主要约束,次要约束,构造器约束)

约束也可以理解为是一种规则,意思就是我们所编写的所有泛型,你在调用的时候,应该给我传输那些类型的实参。

在上一篇中,我们也在一个DataTableToList中使用到了约束,但是并没有进行讲解,在这里我们详细的说明一下:

在.NET 中有4中约束方式,它们的常规语法是相同的,约束要放在泛型方法或者泛型类型声明的末尾,并由上下文关键字 ”where :“  来引入 。

主要约束(引用类型约束):

主要约束表示的是 : 我们在指定一个引用类型约束时,那么一个指定的类型实参要么是与约束类型相同的类型,要么是从约束类型派生的一个类型

实例 4 : 引用类型约束

1 struct Generic<T>(T t1) where : class

这里我定义了一个泛型方法,Generic,后来写了一个where : class,这就告诉编译器,在使用者调用这个方法时,必须为这个方法的传入一个引用类型的实参,同时,我们也可以注意到这个方法的返回类型为struct值类型。

主要约束(值类型约束)

很明显,值类型约束的话,就是将 约束条件指定为 Struct

实例 5 : 值类型约束

1  class ConstraintOfStruct <T> where T : struct
2  {
3      public T result()
4      {
5          return new T();
6      }
7  }

在代码中,我们最后返回的是new T(),这个是没有问题的,因为我们已经可以肯定T就是值类型,所有的值类型都有一个隐式的构造函数,那么如果我们将 约束条件改为 class的话,那么就会出异常,因为不是所有的引用类型都可以实例化.

实例 6 : 反面教材 - 约束为引用类型时进行实例化.

构造函数类型约束

构造函数类型约束表示为 T : new(),这个约束必须为所有的类型参数的最后一个约束,它用于检查类型实参是否有一个可用于创建类型实例的无参构造函数,这一点比较适用于所有的值类型,所有的没有显式声明构造函数的非静态、非抽象类,所有显式声明了一个公共无参构造函数的非抽象类。

实例 7 : 构造函数类型约束

1   public T CreateInatance<T >() where T : new()
2   {
3       return new T();
4   }

在方法createInstance中,要求我们必须传入一个无参的构造函数,例如:我们可以传入 object,struct,stream等,但是不能传入 string,Directory等.

接口约束:

接口约束的一个好处就是我们可以指定多个接口,但是只能指定一个类

1     class ConstraintOfStruct <T> where T : class,IEquatable <T> ,IComparable<T>
2     {
3     }

组合约束 :

组合约束就是指定多个约束条件在上一个约束中我们已经看到了

【5】泛型类型转型

1  public static T Desercialies <T > (Stream stream, IFormatter formatter)
2 {
3       return (T)formatter. Deserialize(stream);
4 }

formatter 负责将流转换为 Object,返回一个Object类型,我们在使用泛型调用的时候应该这样来 :

1   string result = Desercialies <string > (stream, formatter);

上述的代码看着是一种强制转换,但是仍然执行了隐式的转换,就和使用非泛型 string result = (string)Desercialies(stream, formatter); 一样

【6】泛型和属性

属性也可以应用到泛型类型中,使用的方式和非泛型类型相同.自定义的属性只允许引用开发泛型类型

1     class MyClass<T,S> where T :class
2           where S : struct
3     {
4         public T MyName { get; set; }
5         public S MyCode { get; set; }
6         public List<T> MyCourse { get; set; }
7         public List<S> MyStudyHistory { get; set; }
8     }

上述我们定义了一个MyClass 类,这个类有两个参数一个是引用类型一个是值类型.在属性MyName,MyCourse的类型都是引用类型,MyCode的类型都为值类型

泛型类型构造器:

 1      class Pair < T> : IPiar< T >
 2     {
 3         private T _first;
 4
 5         public T First
 6         {
 7             get { return _first; }
 8             set { _first = value; }
 9         }
10         private T _second;
11
12         public T Second
13         {
14             get { return _second; }
15             set { _second = value; }
16         }
17         public Pair(T first,T second)
18         {
19
20         }
21     }
22     interface IPiar < T> { }

泛型的构造器不需要添加类型参数来与类的声明一致.

先到这里, 下一篇着重分享协变和逆变,否则这一篇符太长,不利于以后的查看.

泛型委托和泛型反射留在深入总结委托和反射的时候进行总结。

同时,希望给在阅读的你带来一些帮助。

时间: 2024-10-13 03:35:12

.NET泛型解析(下)的相关文章

Java泛型解析(02):通配符限定

Java泛型解析(02):通配符限定 考虑一个这样的场景,计算数组中的最大元素. [code01] public class ArrayUtil { public static <T> T max(T[] array) { if (array == null || 0 == array.length) { return null ;} T max = array[0]; for (int i = 1; i < array.length; i++) { if (max.compareTo(

Java泛型解析(01):认识泛型

What Java从1.0版本到现在的8,中间Java5中发生了一个很重要的变化,那就是泛型机制的引入.Java5引入了泛型,主要还是为了满足在1999年指定的最早Java规范之一.经过了5年左右的时间,专家组定义了一套泛型规范,实现后通过测试投入到使用.所以说泛型是Java5以后才有的,欲知详情,继续往下看. Why      换个角度想,Java5引入泛型,必定是它能带来好处,否则牛气的Java专家工程师就要遭到吐槽了.我们来吐槽一下没有泛型的程序是怎么写的. [code01] ArrayL

Android View系统解析(下)

转载请注明出处:http://blog.csdn.net/singwhatiwanna/article/details/38426471(来自singwhatiwanna的csdn博客) Android View系统解析系列: Android View系统解析(上) 介绍View的基础知识.View的滑动.弹性滑动.滑动冲突解决方式.事件分发等 Android View系统解析(下) 介绍View的Framework层原理.View的measure / layout / draw三大流程和一些高

使用GSON和泛型解析约定格式的JSON串(转)

时间紧张,先记一笔,后续优化与完善. 解决的问题: 使用GSON和泛型解析约定格式的JSON串. 背景介绍: 1.使用GSON来进行JSON串与java代码的互相转换. 2.JSON的格式如下三种: 写道 #第一种: {"success":true,"data":{"averageStarLevel":4.7,"remarkCount":10}} #第二种: {"success":true,"da

Java泛型解析(04):约束和局限性

Java泛型解析(04):约束和局限性 前两节,认识和学习了泛型的限定以及通配符,初学者可能需要一些时间去体会到泛型程序设计的好处和力量,特别是想成为库程序员的同学就需要下去体会通配符的运用了,应用程序员则需要掌握怎么使用泛型,这里针对泛型的使用中的约束和局限性做一个介绍性的讲解. 不能用基本类型实例化类型参数 这个很好理解,实例化泛型类时泛型参数不能使用基本类型,如List<int>这是不合法的,要存储基本类型则可以利用基本类型的包装类如List<Integer> .List&l

Java泛型解析(03):虚拟机执行泛型代码

Java泛型解析(03):虚拟机执行泛型代码 Java虚拟机是不存在泛型类型对象的,所有的对象都属于普通类,甚至在泛型实现的早起版本中,可以将使用泛型的程序编译为在1.0虚拟机上能够运行的class文件,这个向后兼容性后期被抛弃了,所以后来如果用Sun公司的编译器编译的泛型代码,是不能运行在Java5.0之前的虚拟机的,这样就导致了一些实际生产的问题,如一些遗留代码如何跟新的系统进行衔接,要弄明白这个问题,需要先了解一下虚拟机是怎么执行泛型代码的. 虚拟机的一种机制:擦除类型参数,并将其替换成特

Android开发之Tween(补间动画)完全解析(下)

欢迎转载,转载请注明出处:http://blog.csdn.net/dmk877/article/details/51980734 在上一篇文章中,我们详细讨论了Tween动画的xml的实现以及interpolator的使用,相信通过上篇文章大家对Tween动画的xml属性的配置会有一个详细的理解,当然这篇文章也是承接上篇文章,所以强烈建议先阅读上篇文章:Android开发之Tween(补间动画)完全解析(上),这篇文章将从代码的角度实现上篇文章的效果.如有疑问请留言,如有谬误欢迎批评指正. T

.NET泛型解析(上)

[1]:泛型介绍 泛型是C#2.0中一个重要的新特性,泛型是CLR和编程语言提供的一种特殊机制,它支持另一种形式的代码重用.泛型通常用与集合以及作用于集合的方法一起使用,当然也可以单独使用. C#是一种强类型的语言,在泛型没有被提出之前,我们在使用集合的代码的时候,每次对其进行转换都需要隐式的强制转换,我们都知道所有对象的最终基类是object,我们在每次使用object的时候,无论是变换什么类型都要对其进行强制转换. 那么有了泛型之后,使用泛型我们就无需进行转换,因为泛型根据接收者传入的参数类

Android属性动画完全解析(下),Interpolator和ViewPropertyAnimator的用法

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/44171115 大家好,欢迎继续回到Android属性动画完全解析.在上一篇文章当中我们学习了属性动画的一些进阶技巧,包括ValueAnimator和ObjectAnimator的高级用法,那么除了这些之外,当然还有一些其它的高级技巧在等着我们学习,因此本篇文章就对整个属性动画完全解析系列收个尾,来学习一下剩下的非常重要的高级技巧. 另外,本篇文章中使用的代码是建立在上篇文章基础之