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]图 ...
随机推荐
- SharePoint的安装配置
安装环境 1. Window server 2008 r2(sp2) OS.2. MS SQL Server 2008 r2.3. Office2010.4. IIS7以上.5. 确认服务器已经加入域 ...
- tkprof 解释
使用 tkprof 工具 tkprof orcl_ora_3048_安庆怀宁.trc 安徽安庆怀宁.txt sys=no aggregate=yes sys=no waits=yes sort=fc ...
- 解决jQuery对表单serialize后出现的乱码问题
通过看jQuery源码可以知道,serialize方法是通过encodeURIComponent编码的,所以解决乱码的最笨方法: 1.重新分解序列化后的值 2.把分解的值重新decodeURICo ...
- FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法。。
[原创] RFC3984是H.264的baseline码流在RTP方式下传输的规范,这里只讨论FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法. 1.单个NAL包单元 12字节 ...
- 【转】开始iOS 7中自动布局教程(一)
原文网址:http://www.cocoachina.com/industry/20131203/7462.html 原文:Beginning Auto Layout Tutorial in iOS ...
- handler.post 为什么要将thread对象post到handler中执行呢?
转载网址:http://www.cnblogs.com/crazypebble/archive/2011/03/23/1991829.html在Android中使用Handler和Thread线程执行 ...
- Please ensure that adb is correctly located at……问题解决方案
启动android模拟器时.有时会报The connection to adb is down, and a severe error has occured.的错误.在网友说在任务管理器上把所有ad ...
- iOS AFNetworking的使用
转:http://www.cnblogs.com/lookenwu/p/3927897.html AFNetworking几乎是iOS上最常用的HTTP库了,AFNetworking也确实用起来简单, ...
- css3制作优惠券
<div class="demo-container demo"><style> .demo{width:410px;} .stamp *{padding: ...
- 请不要用SECONDS_BEHIND_MASTER来衡量MYSQL主备的延迟时间
链接:http://www.woqutech.com/?p=1116 MySQL 本身通过 show slave status 提供了 Seconds_Behind_Master ,用于衡量主备之间的 ...