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]图 ...
随机推荐
- overload和override
Overload是重载的意思,Override是覆盖的意思,也就是重写. 重载Overload表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同). 重写Ove ...
- C#路径/文件/目录/I/O常见操作汇总<转载>
文件操作是程序中非常基础和重要的内容,而路径.文件.目录以及I/O都是在进行文件操作时的常见主题,这里想把这些常见的问题作个总结,对于每个问题,尽量提供一些解决方案,即使没有你想要的答案,也希望能提供 ...
- Asp.Net IEnumerable,ICollection,IList,List区别
做C#的同学们,都知道,一类只能有一个继承类,但可以实现多个接口.这句话就告诉我们:IEnumerable,ICollection,IList,List区别了 首先我看看 IEnumerable: / ...
- HTMLayout界面CSSS样式解析笔记
HTMLayout学习笔记 by BBDXF 一.界面篇 学习界面需要有一定的HTML.CSS认知,如果你问为什么,那就当我白说. 由于界面库官方没有给一个完善的User guide,所有的学习都靠自 ...
- HDU 5303 Delicious Apples 美味苹果 (DP)
题意: 给一个长为L的环,起点在12点钟位置,其他位置上有一些苹果,每次带着一个能装k个苹果的篮子从起点出发去摘苹果,要将全部苹果运到起点需要走多少米? 思路: 无论哪处地方,只要苹果数超过k个,那么 ...
- 最简单的视音频播放示例4:Direct3D播放RGB(通过Texture)
本文接着上一篇文章继续记录Direct3D(简称D3D)播放视频的技术.上一篇文章中已经记录了使用Direct3D中的Surface渲染视频的技术.本文记录一种稍微复杂但是更加灵活的渲染视频的方式:使 ...
- sql给数据库加锁问题
加锁是在操作数据时进行了,不能事后加锁. 例: begin tran insert 表 with(TABLOCKX) --加锁 (字段列表) ...
- wifi详解(四)
1 IOCTL的调用逻辑 之所以要分析这个,是因为上层wpa_supplicant和WIFI驱动打交道的方式,多半是通过ioctl的方式进行的,所以看看它的调用逻辑(这里只列出其主要的调 ...
- oracle 11g在安装过程中出现监听程序未启动或数据库服务未注册到该监听程序
15511477451 原文 oracle 11g在安装过程中出现监听程序未启动或数据库服务未注册到该监听程序? 环境:win7 64位系统.oracle11g数据库 问题描述:在win7 64位系统 ...
- Reading or Writing to Another Processes Memory in C# z
http://www.jarloo.com/reading-and-writing-to-memory/ Declarations [Flags] public enum ProcessAccessF ...