认识泛型

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

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

我们先看看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. 服务--web服务

    .面向对象和面向组件 .什么是Web服务 Web Service "Stack" .Web服务的应用分类 Web服务都是对象/组件技术在Internet中的延伸 面向对象和面向组件 ...

  2. bzoj4025

    首先我们要知道,怎么去维护一个是否是二分图 二分图的充要条件:点数>=2且无奇环 重点就是不存在奇环,怎么做呢 考虑随便维护一个图的生成树,不难发现,如果一条边加入后,形成奇环的话就不是二分图 ...

  3. 用Java Servlets代替CGI

    从网络的早期阶段开始,人们就希望他们的网络服务器不仅能够显示静态文档,通用网关接口(CGI) 扩展了服务器的能力,但其代价是增加了额外的处理进程.FastCGI则把外部CGI 处理进程同网络服务器更紧 ...

  4. hdu 4674 Trip Advisor(缩点+倍增lca)

    花了一天半的时间,才把这道题ac= = 确实是道好题,好久没敲这么长的code了,尤其是最后的判定,各种销魂啊~ 题目中给出的条件最值得关注的就是:每个点最多只能在一个环内->原图是由一个个边连 ...

  5. zoj 3659 Conquer a New Region

    // 给你一颗树 选一个点,从这个点出发到其它所有点的权值和最大// i 到 j的最大权值为 i到j所经历的树边容量的最小值// 第一感觉是树上的dp// 后面发现不可以// 看了题解说是并查集// ...

  6. 安装--SambaServce

    参考地址:快跑蚂蚁的linux之旅--redhat安装配置samba实验win共享linux主机目录 1.使用rpm -qa|grep "samba",查看samba安装包是否安装 ...

  7. Android SDK Manager 更新代理配置 ,蛋碎了

    启动 Android SDK Manager ,打开主界面,依次选择「Tools」.「Options...」,弹出『Android SDK Manager - Settings』窗口: 在『Andro ...

  8. C# C/S 结构操作Ini系统文件

    Winfrom 开发时,有时会将一些系统某个设置保存到Ini 类型的文件中.下面提供操作Ini 文件的代码: public static class IniFiles { [DllImport(&qu ...

  9. C# new用法总结

    有一道这样的题:写出c#中new关键字的三种用法,思前想后挖空心思也只想出了两种用法,回来查了下msdn,还真是有第三种用法: 用于在泛型声明中约束可能用作类型参数的参数的类型,这是在Framewor ...

  10. Java中的10颗语法糖

    语法糖(Syntactic Sugar):也称糖衣语法,指在计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用.通常来说,使用语法糖能够增加程序的可读性,减少程序代码出错的 ...