认识泛型

泛型使类型参数化,从而实现了算法上的代码重用。

同时由于去掉了转换中装箱和拆箱的操作,使用泛型还可以提高程序的运行速度。

我们先看看C#自带的使用了泛型的类:

 using System.Collections.Generic;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
List<int> list1 = new List<int>();
list1.Add();
int i = list1[]; List<string> list2 = new List<string>();
list2.Add("Hello");
string s = list2[];
}
}
}

通过使用泛型,我们可以重复利用List提供的功能,而不用每个类型对应去写一个List的类。

泛型在类上的实现

下面我们自己使用泛型编写一个简单的类,如下:

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
Test<int> test1 = new Test<int>();
test1.myValue = ;
Console.WriteLine(test1.myValue); Test<string> test2 = new Test<string>();
test2.myValue = "Hello";
Console.WriteLine(test2.myValue);
}
} public class Test<T>
{
private T _myValue; public T myValue
{
set { _myValue = value; }
get { return _myValue; }
}
}
}

Test类中的尖括号里面的T即为泛型,其可以表示任意的类型。

泛型约束

我们上面示例中的T可以使用任意的类型,那么如果我们只希望T是某类型或某类型的子类该怎么办呢?

public class Test<T> where T : IComparable

如果这样写,则表示T必须是实现了IComparable接口的对象。

多个类型的情况

多个类型的写法如下:

public class Test<T, K> where T : IComparable where K : ICloneable

如上所示,一个类型如果要添加约束就需要写一个where进行对应,所以有多个就会有多个where关键字出现。

创建类型的情况

如果需要使用new创建一个类型,则需要在约束里添加new()的字符串,如下:

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
Test<Data> test1 = new Test<Data>();
Console.WriteLine(test1.myComparable.s); Console.Read();
}
} public class Test<T> where T : IComparable, new()
{
private T _myComparable; public T myComparable
{
set { _myComparable = value; }
get { return _myComparable; }
} public Test()
{
_myComparable = new T();
}
} public class Data : IComparable
{
public string s = "Hello World!"; public int CompareTo(object obj)
{
return ;
}
}
}

但是如果是值类型,则不需要这么写,但是要约束T为值类型,如下:

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
Test<int> test1 = new Test<int>();
Console.WriteLine(test1.myComparable); Console.Read();
}
} public class Test<T> where T : struct
{
private T _myComparable; public T myComparable
{
set { _myComparable = value; }
get { return _myComparable; }
} public Test()
{
_myComparable = new T();
}
}
}

default关键字

当我们需要对泛型T置空时不能直接写“xxx=null;”因为只有当 T 为引用类型时,语句 t = null 才有效;只有当 T 为数值类型而不是结构时,语句 t = 0 才能正常使用。所以我们使用default关键字就可以解决这个问题,如下:

_myComparable = default(T);

泛型继承

子类也有相同的泛型时:

 public class A<T>
{ } public class B<T> : A<T>
{ }

当然,你可以使用另外的名称,只要能对应上即可:

 public class A<T>
{ } public class B<K> : A<K>
{ }

子类指定好类型:

 public class A<T>
{ } public class B : A<string>
{ }

子类添加新类型:

 public class A<T>
{ } public class B<T, K> : A<T>
{ }

泛型在方法上的实现

如果要在方法上添加类上没有指定的类型,可以直接在方法上添加泛型:

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
Test<int> test = new Test<int>();
Console.WriteLine(test.Func<string>("Hello")); Console.Read();
}
} public class Test<T>
{
public K Func<K>(K k)
{
return k;
}
}
}

泛型在委托上的实现

委托上也可以使用泛型,定义方法和在方法上使用泛型一致,如下:

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
new Test(); Console.Read();
}
} public class Test
{
public delegate T add<T>(T a, T b); public Test()
{
add<int> func1 = AddInt;
Console.WriteLine(func1(, )); add<float> func2 = AddFloat;
Console.WriteLine(func2(1.2f, 0.03f));
} private int AddInt(int a, int b)
{
return a + b;
} private float AddFloat(float a, float b)
{
return a + b;
}
}
}

泛型接口

泛型接口的使用和泛型类一致,大家可以查看微软自己的文档:https://msdn.microsoft.com/zh-cn/library/kwtft8ak(VS.80).aspx

泛型和静态字段与方法

泛型同样可以使用在静态字段和方法中,由于静态字段和方法在内存中始终只存在一个,所以当我们使用了泛型的时候,编译器会帮我们自动生成对应的方法。

泛型静态的使用和动态一致就跳过不说了。

类型推断

我们在调用泛型方法时可以省略泛型类型的书写,完全交由编译器根据我们的类型来进行判断,这样可以减小代码量同时也更清晰:

 using System;

 namespace Study
{
class Program
{
static void Main(string[] args)
{
//没有类型推断
Console.WriteLine(CompareTo<int>(, )); //使用类型推断
Console.WriteLine(CompareTo(12.3f, 12.33f));
Console.WriteLine(CompareTo('a', 'a')); Console.Read();
} private static int CompareTo<T>(T a, T b) where T : IComparable
{
return a.CompareTo(b);
}
}
}

泛型的可变性

我们先看一个例子:

 using System;
using System.Collections.Generic; namespace Study
{
class Program
{
static void Main(string[] args)
{
List<B> listB = new List<B>(); //下面这句代码会报错
//Cannot convert source type 'System.Collections.Generic.List<Study.B>' to
//target type 'System.Collections.Generic.List<Study.A>'
List<A> listA = listB; Console.Read();
}
} public class A
{} public class B : A
{}
}

我们发现虽然B继承于A,但是List<A>和List<B>之间是不能相互转换的。

为了解决这个问题,微软在C#4.0中添加了对泛型的可变性的支持。

协变性

协变性指的是泛型类型参数可以从一个派生类隐式地转换为其基类。

out关键字

协变使用out关键字标识,如下:

public interface MyInterface<out T>

示例:

 using System;
using System.Collections.Generic; namespace Study
{
class Program
{
static void Main(string[] args)
{
MyInterface<B> myB = new MyClass<B>();
MyInterface<A> myA = myB; Console.Read();
}
} public interface MyInterface<out T>
{} public class MyClass<T> : MyInterface<T>
{} public class A
{} public class B : A
{}
}

逆变性

逆变性指的是泛型类型参数可以从一个基类隐式的转换为其派生类。

in关键字

public interface MyInterface<in T>

示例:

 using System;
using System.Collections.Generic; namespace Study
{
class Program
{
static void Main(string[] args)
{
MyInterface<A> myA = new MyClass<A>();
MyInterface<B> myB = myA; Console.Read();
}
} public interface MyInterface<in T>
{} public class MyClass<T> : MyInterface<T>
{} public class A
{} public class B : A
{}
}

注意事项

  1. 只有接口和委托支持协变和逆变,类或方法都不支持协变和逆变;
  2. 协变和逆变只支持引用类型,值类型不支持协变和逆变;
  3. 必须显示的使用out或in来标记协变和逆变;
  4. 委托的协变和逆变不要在多播委托中使用;
  5. 协变和逆变不能同时使用,只能选择一种;

C#学习笔记(五):泛型的更多相关文章

  1. Typescript 学习笔记五:类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  2. C#可扩展编程之MEF学习笔记(五):MEF高级进阶

    好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...

  3. (转)Qt Model/View 学习笔记 (五)——View 类

    Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...

  4. java之jvm学习笔记五(实践写自己的类装载器)

    java之jvm学习笔记五(实践写自己的类装载器) 课程源码:http://download.csdn.net/detail/yfqnihao/4866501 前面第三和第四节我们一直在强调一句话,类 ...

  5. Learning ROS for Robotics Programming Second Edition学习笔记(五) indigo computer vision

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

  6. ES6学习笔记<五> Module的操作——import、export、as

    import export 这两个家伙对应的就是es6自己的 module功能. 我们之前写的Javascript一直都没有模块化的体系,无法将一个庞大的js工程拆分成一个个功能相对独立但相互依赖的小 ...

  7. muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor

    目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...

  8. python3.4学习笔记(五) IDLE显示行号问题,插件安装和其他开发工具介绍

    python3.4学习笔记(五) IDLE显示行号问题,插件安装和其他开发工具介绍 IDLE默认不能显示行号,使用ALT+G 跳到对应行号,在右下角有显示光标所在行.列.pycharm免费社区版.Su ...

  9. Go语言学习笔记五: 条件语句

    Go语言学习笔记五: 条件语句 if语句 if 布尔表达式 { /* 在布尔表达式为 true 时执行 */ } 竟然没有括号,和python很像.但是有大括号,与python又不一样. 例子: pa ...

  10. 【opencv学习笔记五】一个简单程序:图像读取与显示

    今天我们来学习一个最简单的程序,即从文件读取图像并且创建窗口显示该图像. 目录 [imread]图像读取 [namedWindow]创建window窗口 [imshow]图像显示 [imwrite]图 ...

随机推荐

  1. iOS开发:插件记录

    进入沙盒的插件 https://github.com/TongeJie/ZLGotoSandboxPlugin 图片提示的插件 https://github.com/ksuther/KSImageNa ...

  2. ASP.NET Redis 开发(转载)

    Redis简介 Redis是一个开源的,使用C语言编写,面向“键/值”对类型数据的分布式NoSQL数据库系统,特点是高性能,持久存储,适应高并发的应用场景.Redis纯粹为应用而产生,它是一个高性能的 ...

  3. codevs 1197 Vigenère密码

    开始做NOIP啦.. #include<iostream> #include<cstdio> #include<cstring> #include<algor ...

  4. zoj 2723 Semi-Prime

    // 题意都不好理解 我以为是求 一个数被分成2个素数和 然后是求分成2个素数积// 坑爹 忘记写 !=EOF 然后一直超时 然后换了几种 还是超时 一看别人代码 速度明显比我慢// 然后发现被自己坑 ...

  5. Oracle 课程八之性能优化之Oracle SQL Trace

    一. SQL_TRACE 当SQL语句出现性能问题时,我们可以用SQL_TRACE来跟踪SQL的执行情况,通过跟踪,我们可以了解一条SQL或者PL/SQL包的运行情况,SQL_TRACE命令会将SQL ...

  6. 22个所见即所得在线 Web 编辑器

    前言: 关于编辑器,适合的才是最好的,接下来,我会写一些关于日志编辑器的文章,今天就写写,可能内容会比较多. --------------------------------------------- ...

  7. Linux iostat监测IO状态

    Linux iostat监测IO状态 http://www.orczhou.com/index.php/2010/03/iostat-detail/

  8. Selenium2Library系列 keywords 之 _SelectElementKeywords 之 page_should_not_contain_list(self, locator, message='', loglevel='INFO')

    def page_should_not_contain_list(self, locator, message='', loglevel='INFO'): """Veri ...

  9. OpenGL超级宝典第5版&&开发环境搭建

    参考:http://www.zyh1690.org/build-opengl-super-bible-fifth-edition-development-environment/ 环境搭建的测试环境为 ...

  10. 安装CPqD/ofdissector遭遇的错误

    为了安装支持openflow1.3的wireshark插件,在下载了ofdissector.git,并进入了其src目录后,执行scons install,出现如下错误: util/FieldMana ...