摘要:本文介绍了如何定义一个C#泛型类,以及实现泛型类的继承、方法和约束。

C#泛型参数化了类型,把类型作为参数抽象出来,从而使我们在实际的运用当中能够更好的实现代码的重复利用,同时它提供了更强的类型安全,更高的效率,不过在约束方面,它只支持显示的约束,这样在灵活性方面就显得不是那么好了。我觉得它之所以能够提供更高的效率是因为泛型在实例化的时候采用了"on-demand"的模式,即按需实例化,发生在JIT(Just In Time)编译时。

下面来看如何定义一个C#泛型类,很简单,你只需要意识到一点,在这里,类型已经被参数化了:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4.  
  5. namespace GenericTest
  6. {
  7. class Program
  8. {
  9. static void Main(string[] args)
  10. {
  11. //使用string,int来实例化Test< T,S>类
  12. Test<string, int> t = new Test<string, int>("SHY520", );
  13.  
  14. //调用泛型类中的方法
  15. t.SetValue();
  16. }
  17. }
  18.  
  19. /**/
  20. /// < summary>
  21. /// 定义一个泛型类,该类有两个类型参数,分别是T,S
  22. /// http://www.cnblogs.com/jara
  23. /// < /summary>
  24. /// < typeparam name="T">类型参数< /typeparam>
  25. /// < typeparam name="S">类型参数< /typeparam>
  26. public class Test<T, S>
  27. {
  28. //泛型类的类型参数可用于类成员
  29. private T name;
  30. private S age;
  31.  
  32. public Test(T Name, S Age)
  33. {
  34. this.name = Name;
  35. this.age = Age;
  36. }
  37.  
  38. public void SetValue()
  39. {
  40. Console.WriteLine(name.ToString());
  41. Console.WriteLine(age.ToString());
  42. }
  43. }
  44. }

上面的例子不是很恰当,目的是让初学泛型的你了解一下泛型的定义及实例化方法,如上,我们定义了一个泛型类,那么如何实现C#泛型类的继承呢?这里需要满足下面两点中的任何一点即可:

1、泛型类继承中,父类的类型参数已被实例化,这种情况下子类不一定必须是泛型类;

2、父类的类型参数没有被实例化,但来源于子类,也就是说父类和子类都是泛型类,并且二者有相同的类型参数;

  1. //如果这样写的话,显然会报找不到类型T,S的错误
  2. public class TestChild : Test< T, S> { }
  3.  
  4. //正确的写法应该是
  5. public class TestChild : Test< string, int>{ }
  6. public class TestChild< T, S> : Test< T, S> { }
  7. public class TestChild< T, S> : Test< String, int> { }

接着我们来看看泛型接口,其创建以及继承规则和上面说的泛型类是一样的,看下面的代码:

  1. public interface IList<T>
  2. {
  3. T[] GetElements();
  4. }
  5. public interface IDictionary<K, V>
  6. {
  7. void Add(K key, V value);
  8. }
  9.  
  10. // 泛型接口的类型参数要么已实例化
  11. // 要么来源于实现类声明的类型参数
  12. class List<T> : IList<T>, IDictionary<int, T>
  13. {
  14. public T[] GetElements() { return null; }
  15. public void Add(int index, T value)
  16. { }
  17. }

在来看一下C#泛型委托,首先我们定义一个类型参数为T的委托,然后在类中利用委托调用方法:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4.  
  5. namespace GenericTest
  6. {
  7. //定义一个委托,类型参数为T,返回值类型T
  8. //泛型委托支持在返回值和参数上应用类型参数
  9. delegate string GenericDelete<T>(T value);
  10.  
  11. class test
  12. {
  13. static string F(int i) { return "SHY520"; }
  14. static string G(string s) { return "SHY520"; }
  15.  
  16. static void Main(string[] args)
  17. {
  18. GenericDelete<string> G1 = G;
  19. GenericDelete<int> G2 = new GenericDelete<int>(F);
  20. }
  21. }
  22. }

我们再来看C#泛型方法,C#的泛型机制只支持在方法申明上包含类型参数,也即是泛型方法。特别注意的是,泛型不支持在除了方法以外的其他类/接口成员上使用类型参数,但这些成员可以被包含在泛型类型中,并且可以使用泛型类型的类型参数。还有一点需要说的就是,泛型方法可以在泛型类型中,也可以存在于非泛型类型中。下面我们分别看一下泛型类型的申明,调用,重载和覆盖。

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4.  
  5. namespace GenericTest
  6. {
  7. class GenericClass
  8. {
  9. //申明一个泛型方法
  10. public T getvalue<T>(T t)
  11. {
  12. return t;
  13. }
  14.  
  15. //调用泛型方法
  16. //注意:在调用泛型方法时,对泛型方法的类型参数实例化
  17. public int useMethod()
  18. {
  19. return this.getvalue<int>();
  20. }
  21.  
  22. //重载getvalue方法
  23. public int getvalue(int i)
  24. {
  25. return i;
  26. }
  27. }
  28.  
  29. //下面演示覆盖
  30. //要注意的是,泛型方法被覆盖时,约束被默认继承,不需要重新指定约束关系
  31. abstract class Parent
  32. {
  33. public abstract K TEST<K, V>(K k, V v) where K : V;
  34. }
  35.  
  36. class Child : Parent
  37. {
  38. public override T TEST<T, S>(T t, S s)
  39. {
  40. return t;
  41. }
  42. }
  43. }

最后我们来看一下C#泛型中的约束:

C#中的泛型只支持显示的约束,因为这样才能保证C#所要求的类型安全,但显示的约束并非时必须的,如果不加约束,泛型类型参数将只能访问System.Object类型中的公有方法。“显式约束”由where子句表达,可以指定“基类约束”,“接口约束”,“构造器约束”,“值类型/引用类型约束”共四种约束。下面的例子来源于李建忠老师的讲座PPT。

1、基类约束:

  1. class A { public void F1() {} }
  2. class B { public void F2() {} }
  3. class C< S,T>
  4. where S: A // S继承自A
  5. where T: B // T继承自B
  6. {
  7.  // 可以在类型为S的变量上调用F1,
  8.  // 可以在类型为T的变量上调用F2
  9. }

2、接口约束

  1. interface IPrintable { void Print(); }
  2. interface IComparable< T> { int CompareTo(T v);}
  3. interface IKeyProvider< T> { T GetKey(); }
  4. class Dictionary< K,V>
  5. where K: IComparable< K>
  6. where V: IPrintable, IKeyProvider< K>
  7. {
  8.  // 可以在类型为K的变量上调用CompareTo,
  9.  // 可以在类型为V的变量上调用Print和GetKey
  10. }

3、构造器约束

  1. class A { public A() { } }
  2. class B { public B(int i) { } }
  3. class C< T>
  4. where T : new()
  5. {
  6.  //可以在其中使用T t=new T();
  7. }
  8. C< A> c=new C< A>(); //可以,A有无参构造器
  9. C< B> c=new C< B>(); //错误,B没有无参构造器

4、值/引用类型约束

  1. public struct A { }
  2. public class B { }
  3. class C< T>
  4. where T : struct
  5. {
  6.  // T在这里面是一个值类型
  7. }
  8. C< A> c=new C< A>(); //可以,A是一个值类型
  9. C< B> c=new C< B>(); //错误,B是一个引用类型

浅谈C#泛型的定义、继承、方法和约束的更多相关文章

  1. 【ASP.NET MVC系列】浅谈表单和HTML辅助方法

    [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作篇)(下) [04]浅谈ASP. ...

  2. 浅谈Java——泛型DAO

    首先解释一下为什么要学习泛型DAO.平时在写DAO的时候是一个接口对应一个实现类,实现类里面要写很多的操作数据库的方法.当我们有很多的javaben的时候我们会写很多的接口和实现类,并且里面的代码都是 ...

  3. 浅谈Vue响应式(数组变异方法)

    很多初使用Vue的同学会发现,在改变数组的值的时候,值确实是改变了,但是视图却无动于衷,果然是因为数组太高冷了吗? 查看官方文档才发现,不是女神太高冷,而是你没用对方法. 看来想让女神自己动,关键得用 ...

  4. 浅谈js分页的几种方法

    一个项目中必然会遇到分页这种需求的,分页可以使数据加载更合理,也让页面显示更美观,更有层次感!那么js分页到底如何实现呢?下面我就来讲一下三种循序渐进的方法 1.自己纯手写分页 更深入的去理解分页的意 ...

  5. 浅谈C#泛型

    一.为什么要提出泛型的概念 我们在声明对象或者方法中,对象中成员变量的定义或者函数参数都传递都要指定具体的对象类型,但是有的时候参数的类型是变化的,但是实现的功能却又差不多,这个时候我们就想,是否存在 ...

  6. 浅谈Java泛型中的extends和super关键字(转)

    通配符 在本文的前面的部分里已经说过了泛型类型的子类型的不相关性.但有些时候,我们希望能够像使用普通类型那样使用泛型类型: 向上造型一个泛型对象的引用 向下造型一个泛型对象的引用 向上造型一个泛型对象 ...

  7. 浅谈Java泛型中的extends和super关键字

    泛型是在Java 1.5中被加入了,这里不讨论泛型的细节问题,这个在Thinking in Java第四版中讲的非常清楚,这里要讲的是super和extends关键字,以及在使用这两个关键字的时候为什 ...

  8. 转载--浅谈spring4泛型依赖注入

    转载自某SDN-4O4NotFound Spring 4.0版本中更新了很多新功能,其中比较重要的一个就是对带泛型的Bean进行依赖注入的支持.Spring4的这个改动使得代码可以利用泛型进行进一步的 ...

  9. 浅谈Java泛型之<? extends T>和<? super T>的区别

    关于Java泛型,这里我不想总结它是什么,这个百度一下一大堆解释,各种java的书籍中也有明确的定义,只要稍微看一下就能很快清楚.从泛型的英文名字Generic type也能看出,Generic普通. ...

随机推荐

  1. 剑指Offer——数值的整数次方

    题目描述: 给定一个double类型的浮点数base和int类型的整数exponent.求base的exponent次方. 分析: 快速幂,简单解释下, 当e的二进制为1001011, b^e=b^( ...

  2. King's Game---hdu5643(约瑟夫环)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5643    约瑟夫环问题的原来描述为,设有编号为1,2,……,n的n(n>0)个人围成一个圈,从 ...

  3. redis哨兵集群、docker入门

    redis-sentinel主从复制高可用 Redis-Sentinel Redis-Sentinel是redis官方推荐的高可用性解决方案,当用redis作master-slave的高可用时,如果m ...

  4. ruamel.yaml 将字典写入yaml文件

    #安装 pip install ruamel.yaml import os from ruamel import yaml # 将字典写入到yaml my_dic = { 'name': '张三', ...

  5. vs开发nodejs系列之 修改新建js文件的模板

    文件位置 C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\Extensions\Microsoft ...

  6. linux下dubbo调试 ---telnet命令

    linux下启动dubbo服务端, 怎么调试? 方法有二: 1. 自己写简单消费者功能,进行各种情况测试.(这确实是有必要的) 2. 使用telnet直接连接上dubbo,使用命令调用,然后调试.(这 ...

  7. win下如何解决在chrome的同源访问问题

    引子:本来是想验证如果在网页中包含多个框架,那么就会存在两个以上的不同全局环境,如果从一个框架引用另一个框架的数据比如数组a,那么用 instanceof 判断这个数组a是不是另个框架Array的实例 ...

  8. 解决Centos关闭You have new mail in /var/spool/mail/root提示(转)

    今天查看内存的时候 出现一天奇怪的提示 You have new mail in /var/spool/mail/root 有的时候每敲一下回车,就出来You have new mail in /va ...

  9. jQery实现插入删除信息

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...

  10. python 随机分类

    #encoding:utf-8import pandas as pdimport numpy as npfrom sklearn import datasets,linear_modelfrom sk ...