C#学习笔记(五):泛型
认识泛型
泛型使类型参数化,从而实现了算法上的代码重用。
同时由于去掉了转换中装箱和拆箱的操作,使用泛型还可以提高程序的运行速度。
我们先看看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
{}
}
注意事项
- 只有接口和委托支持协变和逆变,类或方法都不支持协变和逆变;
- 协变和逆变只支持引用类型,值类型不支持协变和逆变;
- 必须显示的使用out或in来标记协变和逆变;
- 委托的协变和逆变不要在多播委托中使用;
- 协变和逆变不能同时使用,只能选择一种;
C#学习笔记(五):泛型的更多相关文章
- Typescript 学习笔记五:类
中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...
- C#可扩展编程之MEF学习笔记(五):MEF高级进阶
好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...
- (转)Qt Model/View 学习笔记 (五)——View 类
Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...
- java之jvm学习笔记五(实践写自己的类装载器)
java之jvm学习笔记五(实践写自己的类装载器) 课程源码:http://download.csdn.net/detail/yfqnihao/4866501 前面第三和第四节我们一直在强调一句话,类 ...
- Learning ROS for Robotics Programming Second Edition学习笔记(五) indigo computer vision
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...
- ES6学习笔记<五> Module的操作——import、export、as
import export 这两个家伙对应的就是es6自己的 module功能. 我们之前写的Javascript一直都没有模块化的体系,无法将一个庞大的js工程拆分成一个个功能相对独立但相互依赖的小 ...
- muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor
目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...
- python3.4学习笔记(五) IDLE显示行号问题,插件安装和其他开发工具介绍
python3.4学习笔记(五) IDLE显示行号问题,插件安装和其他开发工具介绍 IDLE默认不能显示行号,使用ALT+G 跳到对应行号,在右下角有显示光标所在行.列.pycharm免费社区版.Su ...
- Go语言学习笔记五: 条件语句
Go语言学习笔记五: 条件语句 if语句 if 布尔表达式 { /* 在布尔表达式为 true 时执行 */ } 竟然没有括号,和python很像.但是有大括号,与python又不一样. 例子: pa ...
- 【opencv学习笔记五】一个简单程序:图像读取与显示
今天我们来学习一个最简单的程序,即从文件读取图像并且创建窗口显示该图像. 目录 [imread]图像读取 [namedWindow]创建window窗口 [imshow]图像显示 [imwrite]图 ...
随机推荐
- UVa 11526 H(n)
题意: long long H(int n){ long long res = 0; for( int i = 1; i <= n; i=i+1 ){ res = (res + n/i); } ...
- 解决编译报错:Unable to copy file, because it is being used by another process.
Error 63 Unable to copy file "D:\DEV\XXX Website\trunk\4 Source Code\Common\WebControls\b ...
- HNOI2004宠物收养所(平衡树)
treap! var i,n,x,y,ans,a,b,root,tot,ft:longint; l,r,s,v,hr:..] of longint; procedure r_rotate(var x: ...
- Darwin Streaming Server Relay Setting
安装完Darwin Streaming Server,就可以使用VLC通过RTSP协议播放流媒体文件了.但是我现在有一个需求,需要将一台DSS(假设为A机)上的媒体文件发送到另一台DSS(假设为B机) ...
- H264 TS/ES
ES流(Elementary Stream): 也叫基本码流,包含视频.音频或数据的连续码流. PES流(Packet Elementary Stream): 也叫打包的基本码流, 是将基 ...
- java/python中获取当前系统时间,并与字符串相互转换格式,或者转化成秒数,天数等整数
java转换成秒数 Date类有一个getTime()可以换回秒数,例如: public class DateToSecond { public static void main(String[] a ...
- 【转】Intel HEX介绍
记录格式 Intel HEX由任意数量的十六进制记录组成.每个记录包含5个域,它们按以下格式排列: :llaaaatt[dd...]cc 每一组字母对应一个不同的域,每一个字母对应一个十六进制编码的数 ...
- java web 学习一
一.基本概念 1.1.WEB开发的相关知识 WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源. Internet上供外界访问的Web资源分为: 静态web资源( ...
- Redis源码分析系列
0.前言 Redis目前热门NoSQL内存数据库,代码量不是很大,本系列是本人阅读Redis源码时记录的笔记,由于时间仓促和水平有限,文中难免会有错误之处,欢迎读者指出,共同学习进步,本文使用的Red ...
- 翻译【ElasticSearch Server】第一章:开始使用ElasticSearch集群(1)
我们要做的第一件事是安装ElasticSearch.对于多数应用程序,您开始安装和配置,通常忘记这些步骤的重要性,直到发生了糟糕的事情.这章我们将广泛关注ElasticSearch的这部分.请注意本章 ...