泛型
作者:追风剑情 发布于:2016-6-20 15:56 分类:C#
示例一
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace GenericityTest { class Program { static void Main(string[] args) { Calculator<int, string> cal = new Calculator<int, string>(120, "aaa"); cal.SetCallBack(CallBack); Console.Read(); } static void CallBack(int i, string s) { Console.WriteLine("i={0}, s={1}", i, s); } } public class Calculator<T, V> { public delegate void CallBack(T t, V v); T t; V v; public Calculator(T t, V v) { this.t = t; this.v = v; } public void SetCallBack(CallBack callback) { callback(t, v); } } }
运行测试
示例二: 利用where限制泛型类型
using System; namespace TTest { class Program { static void Main(string[] args) { Fun<IClass, IClass>(new ClassA(), new ClassB()); Console.Read(); } public static void Fun<T, V>(T t, V v) where T : IClass where V : IClass { Console.WriteLine("Fun"); } } class IClass { } class ClassA : IClass { } class ClassB : IClass { } }
示例三: 用new()限定泛型必须要有public构造方法
public static void Fun<T, V>(T t, V v) where T : IClass, new() where V : IClass, new() { Console.WriteLine("Fun"); }
集合向上转型(协变)
IEnumerable<int> collection1 = new List<int>(); IEnumerable<object> collection2 = collection1; //由于声明了out关键字,所以可实现集合向上转型(协变) public interface IEnumerable<out T> : IEnumerable { IEnumerator<T> GetEnumerator(); }
泛型(generic)是CLR和编程语言提供的一种特殊机制,它支持另一种形式的代码重用,即“算法重用”。
CLR允许创建泛型引用类型和泛型值类型,但不允许创建泛型枚举类型。此外,CLR还允许创建泛型接口和泛型委托。方法偶尔也封装有用的算法,所以CLR允许在引用类型、值类型或接口中定义泛型方法。
简化语法
using DateTimeList = System.Collections.Generic.List<System.DateTime>; Boolean sameType = (typeof(List<DateTime>) == typeof(DateTimeList));//true
实现泛型接口
// 实现泛型接口,并指定类型实参 internal sealed class Triangle : IEnumerator<Point> { private Point[] m_vertices; // IEnumerator<Point>的Current属性是Point类型 public Point Current { get { ... } } ... } // 实现泛型接口,未指定实参 internal sealed class Triangle : IEnumerator<T< { private T[] m_vertices; // IEnumerator<T>的Current属性是T类型 public T Current { get { ... } } ... }
泛型委托
//定义泛型委托 public delegate TReturn CallMe<TReturn, TKey, TValue>(TKey key, TValue value); //编译器会将CallMe转换成如下所示的类: public sealed class CallMe<TReturn, TKey, TValue> : MulticastDelegate { public CallMe(Object object, IntPtr method); public virtual TReturn Invoke(TKey key, TValue value); public virtual IAsyncResult BeginInvoke(TKey key, TValue value, AsyncCallback callback, Object object); public virtual TReturn EndInvoke(IAsyncResult result); }
注意 建议尽量使用FCL预定义的泛型Action和Func委托。
委托和接口的逆变和协变泛型类型实参
委托的每个泛型类型参数都可标记为协变量或逆变量。利用这个功能,可将泛型委托类型的变量转换为相同的委托类型(但泛型参数类型不同)。泛型类型参数可以是以下任何一种形式。
● 不变量(invariant)意味着泛型类型参数不能更改。
● 逆变量(contravariant)意味着泛型类型参数可以从一个类更改为它的某个派生类。在C#是用in关键字标记逆变量形式的泛型类型参数。逆变量泛型类型参数只出现在输入位置,比如作为方法的参数。
● 协变量(covariant)意味着泛型类型参数可以从一个类更改为它的某个基类。C#是用out关键字标记协变量形式的泛型类型参数。协变量泛型类型参数只能出现在输出位置,比如作为方法的返回类型。
例如,假定存在以下委托类型定义(顺便说一下,它真的存在):
public delegate TResult Func<in T, out TResult>(T arg);
其中,泛型类型参数T用in关键字标记,这使它成为逆变量;泛型类型参数TResult用out关键字标记,这使它成为协变量。
所以,如果像下面这样声明一个变量:
Func<Object, ArgumentException> fn1 = null;
就可将它转型为另一个泛型类型参数不同的 Func 类型:
Func<String, Exception> fn2 = fn1; // 不需要显式转型
Exception e = fn2("");
上述代码的意思是说:fn1变量引用一个方法,获取一个 Object,返回一个ArgumentfException。而 fn2 变量引用另一个方法,获取一个 String,返回一个 Exception,由于可将一个 String 传给期待 Object 的方法(因为 String 从 Object 派生),而且由于可以获取返回ArgumentException的一个方法的结果,并将这个结果当成一个Exception(因为Exception是ArgumentException的基类),所以上述代码能正常编译,而且编译时能维持类型安全性。
void ProcessCollection(IEnumerable<Object> collection){...}
我不能在调用它时传递一个List<DateTime>对象引用,因为DateTime值类型和Object之间不存在引用转换——虽然DateTime是从Object派生的。为了解决这个问题,可像下面这样声明 ProcessCollection:
void ProcessCollection<T>(IEnumerable<T> collection){...}
另外,ProcessCollection(IEnumerable<Object> collection)最大的好处是JIT编译得到的代码只有一个版本。但如果使用ProcessCollection<T>(IEnumerable<T> collection),那么只有在 T 是引用类型的前提下,才可共享同一个版本的 JIT 编译代码。对于T是值类型的情况,每个值类型都有一份不同的 JIT 编译代码;不过,起码能在调用方法时传递一个值类型集合了。
另外,对于泛型类型参数,如果要将该类型的实参传给使用out或ref关键字的方法,便不允许可变性。例如,以下代码会造成编译器报告错误消息:无效的可变性:类型参数"T"在"SomeDelegate<T>.Invoke(refT)"中必须是不变量。现在的"T"是逆变量。
delegate void SomeDelegate<in T>(ref T t);
使用要获取泛型参数和返回值的委托时,建议尽量为逆变性和协变性指定in和out关键字。这样做不会有不良反应,并使你的委托能在更多的情形中使用。
和委托相似,具有泛型类型参数的接口也可将类型参数标记为逆变量和协变量。下面的示例接口有一个逆变量泛型类型参数:
public interface IEnumerator<in T> : IEnumerator { Boolean MoveNext(); T Current { get; } }
由于T是逆变量,所以以下代码可顺利编译和运行:
// 这个方法接受任意引用类型的一个IEnumerable Int32 Count(IEnumerable<Object> collection) { ... } ... // 以下调用向Count传递一个IEnumerableInt32 c = Count(new [] { "Grant" });
泛型方法
internal sealed class GenericType<T> { private T m_value; public GenericType(T value) {m_value = value;} public TOutput Converter<TOutput>() { TOutput result = (TOutput) Convert.ChangeType(m_value, typeof(TOutput)); return result; } }
public static class Interlocked { public static T Exchange<T>(ref T location1, T value) where T: class; public static T CompareExchange<T>( ref T location1, T value, T comparand) where T: class; }
可验证性和约束
public static T Min<T>(T o1, T o2) where T : IComparable<T> { if (o1.CompareTo(o2) < 0) return o1; return o2; }
泛型只能基于元数(类型参数个数)对类型或方法进行重载
// 可以定义以下类型 internal sealed class AType {} internal sealed class AType<T> {} internal sealed class AType<T1, T2> {} // 错误:与没有约束的AType<T>冲突 internal sealed class AType<T> {} where T : IComparable<T> {} // 错误:与AType<T1, T2>冲突 internal sealed class AType<T3, T4> {} internal sealed class AnotherType { // 可以定义以下方法,参数个数不同: private static void M() {} private static void M<T>() {} private static void M<T1, T2>() {} // 错误:与没有约束的M<T>冲突 private static void M<T>() where T : IComparable<T> {} // 错误:与M冲突 private static void M<T3, T4>() }
重写虚泛型方法时,重写的方法必须指定相同数量的类型参数,而且这些类型参数会继承在基类方法上指定的约束。事实上,根本不允许为重写方法的类型参数指定任何约束。但类型参数的名称是可以改变的。类似地,实现接口方法时,方法必须指定与接口方法等量的类型参数,这些类型参数将继承由接口方法指定的约束。下例使用虚方法演示了这一规则:
internal class Base { public virtual void M<T1, T2>() where T1 : struct where T2 : class { } } internal sealed class Derived : Base { //类型参数名称可以改变 //重写方法已经从基类继承了约束,无法重新指定约束 public override void M<T3, T4>() where T3 : EventArgs //错误 where T4 : class //错误 {} }
主要约束
类型参数可以指定零个或者一个主要约束。主要约束可以是代表非密封类的一个引用类型。不能指定以下特殊引用类型:System.Object,System.Array,System.Delegate,System.MulticastDelegate,System.ValueType,System.Enum或者System.Void。
有两个特殊的主要约束:class 和 struct。class表示引用类型约束(任何类类型、接口类型、委托类型、数组等),struct表示值类型约束(数字、枚举等)。CLR将任何System.Nullable<T>值类型视为特殊类型,不满足这个struct约束。原因是Nullable<T>类型将它的类型参数约束为struct,而CLR希望禁止像Nullable<Nullable<T>>这样的递归类型。
所有值类型都隐式地有一个无参构造器,当T被约束为struct时,new T()是合法的。
// 公共无参构造器约束 new() internal sealed class ContructorConstraint<T> where T : new() { public static T Factory() { // 允许,因为所有值类型都隐式有一个公共无参构造器 // 而如果指定的是引用类型,约束也要求它提供公共无参构造器 return new T(); } }
目前,CLR(以及C#编译器)只支持无参构造器约束。Microsoft认为这已经能满足几乎所有情况。
用default关键字设置默认值,例如 T temp = default(T) 如果T是引用类型将返回null,如果T是值类型将返回0。
未做任何约束的泛型类型也可以使用==或!=操作符与null进行比较。
不能将基元类型(Byte, Int16, Int32, Int64, Decimal等)操作符(比如+,-,*和/)应用于泛型类型变量。因为编译器在编译时确定不了类型。
标签: C#
日历
最新文章
随机文章
热门文章
分类
存档
- 2024年11月(3)
- 2024年10月(5)
- 2024年9月(3)
- 2024年8月(3)
- 2024年7月(11)
- 2024年6月(3)
- 2024年5月(9)
- 2024年4月(10)
- 2024年3月(11)
- 2024年2月(24)
- 2024年1月(12)
- 2023年12月(3)
- 2023年11月(9)
- 2023年10月(7)
- 2023年9月(2)
- 2023年8月(7)
- 2023年7月(9)
- 2023年6月(6)
- 2023年5月(7)
- 2023年4月(11)
- 2023年3月(6)
- 2023年2月(11)
- 2023年1月(8)
- 2022年12月(2)
- 2022年11月(4)
- 2022年10月(10)
- 2022年9月(2)
- 2022年8月(13)
- 2022年7月(7)
- 2022年6月(11)
- 2022年5月(18)
- 2022年4月(29)
- 2022年3月(5)
- 2022年2月(6)
- 2022年1月(8)
- 2021年12月(5)
- 2021年11月(3)
- 2021年10月(4)
- 2021年9月(9)
- 2021年8月(14)
- 2021年7月(8)
- 2021年6月(5)
- 2021年5月(2)
- 2021年4月(3)
- 2021年3月(7)
- 2021年2月(2)
- 2021年1月(8)
- 2020年12月(7)
- 2020年11月(2)
- 2020年10月(6)
- 2020年9月(9)
- 2020年8月(10)
- 2020年7月(9)
- 2020年6月(18)
- 2020年5月(4)
- 2020年4月(25)
- 2020年3月(38)
- 2020年1月(21)
- 2019年12月(13)
- 2019年11月(29)
- 2019年10月(44)
- 2019年9月(17)
- 2019年8月(18)
- 2019年7月(25)
- 2019年6月(25)
- 2019年5月(17)
- 2019年4月(10)
- 2019年3月(36)
- 2019年2月(35)
- 2019年1月(28)
- 2018年12月(30)
- 2018年11月(22)
- 2018年10月(4)
- 2018年9月(7)
- 2018年8月(13)
- 2018年7月(13)
- 2018年6月(6)
- 2018年5月(5)
- 2018年4月(13)
- 2018年3月(5)
- 2018年2月(3)
- 2018年1月(8)
- 2017年12月(35)
- 2017年11月(17)
- 2017年10月(16)
- 2017年9月(17)
- 2017年8月(20)
- 2017年7月(34)
- 2017年6月(17)
- 2017年5月(15)
- 2017年4月(32)
- 2017年3月(8)
- 2017年2月(2)
- 2017年1月(5)
- 2016年12月(14)
- 2016年11月(26)
- 2016年10月(12)
- 2016年9月(25)
- 2016年8月(32)
- 2016年7月(14)
- 2016年6月(21)
- 2016年5月(17)
- 2016年4月(13)
- 2016年3月(8)
- 2016年2月(8)
- 2016年1月(18)
- 2015年12月(13)
- 2015年11月(15)
- 2015年10月(12)
- 2015年9月(18)
- 2015年8月(21)
- 2015年7月(35)
- 2015年6月(13)
- 2015年5月(9)
- 2015年4月(4)
- 2015年3月(5)
- 2015年2月(4)
- 2015年1月(13)
- 2014年12月(7)
- 2014年11月(5)
- 2014年10月(4)
- 2014年9月(8)
- 2014年8月(16)
- 2014年7月(26)
- 2014年6月(22)
- 2014年5月(28)
- 2014年4月(15)
友情链接
- Unity官网
- Unity圣典
- Unity在线手册
- Unity中文手册(圣典)
- Unity官方中文论坛
- Unity游戏蛮牛用户文档
- Unity下载存档
- Unity引擎源码下载
- Unity服务
- Unity Ads
- wiki.unity3d
- Visual Studio Code官网
- SenseAR开发文档
- MSDN
- C# 参考
- C# 编程指南
- .NET Framework类库
- .NET 文档
- .NET 开发
- WPF官方文档
- uLua
- xLua
- SharpZipLib
- Protobuf-net
- Protobuf.js
- OpenSSL
- OPEN CASCADE
- JSON
- MessagePack
- C在线工具
- 游戏蛮牛
- GreenVPN
- 聚合数据
- 热云
- 融云
- 腾讯云
- 腾讯开放平台
- 腾讯游戏服务
- 腾讯游戏开发者平台
- 腾讯课堂
- 微信开放平台
- 腾讯实时音视频
- 腾讯即时通信IM
- 微信公众平台技术文档
- 白鹭引擎官网
- 白鹭引擎开放平台
- 白鹭引擎开发文档
- FairyGUI编辑器
- PureMVC-TypeScript
- 讯飞开放平台
- 亲加通讯云
- Cygwin
- Mono开发者联盟
- Scut游戏服务器引擎
- KBEngine游戏服务器引擎
- Photon游戏服务器引擎
- 码云
- SharpSvn
- 腾讯bugly
- 4399原创平台
- 开源中国
- Firebase
- Firebase-Admob-Unity
- google-services-unity
- Firebase SDK for Unity
- Google-Firebase-SDK
- AppsFlyer SDK
- android-repository
- CQASO
- Facebook开发者平台
- gradle下载
- GradleBuildTool下载
- Android Developers
- Google中国开发者
- AndroidDevTools
- Android社区
- Android开发工具
- Google Play Games Services
- Google商店
- Google APIs for Android
- 金钱豹VPN
- TouchSense SDK
- MakeHuman
- Online RSA Key Converter
- Windows UWP应用
- Visual Studio For Unity
- Open CASCADE Technology
- 慕课网
- 阿里云服务器ECS
- 在线免费文字转语音系统
- AI Studio
- 网云穿
- 百度网盘开放平台
- 迅捷画图
- 菜鸟工具
- [CSDN] 程序员研修院
- 华为人脸识别
- 百度AR导航导览SDK
- 海康威视官网
- 海康开放平台
- 海康SDK下载
- git download
交流QQ群
-
Flash游戏设计: 86184192
Unity游戏设计: 171855449
游戏设计订阅号