【Unity】Unity C#基础(十三)泛型(Generic)
文章目录泛型(Generic)泛型(Generic)的特性泛型类泛型方法泛型委托继承和泛型泛型方法虚拟方法泛型参数隐式强制转换泛型参数显示强制转换泛型参数强制转换到其他任何类型使用is和as运算符泛型约束派生约束构造函数约束值约束引用约束多个泛型参数C#泛型与Java泛型泛型(Generic)泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。
文章目录
泛型(Generic)
泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。
C#的泛型,无论是源码,IL语言,还是CLR中,都是完全不同的类型。也就是在泛型的基础上每多指定一个泛型,就会多出一个类,这个类是确实存在的,在IL中的名称为XXClass`|,这些多出来的类型有自己的虚方法表和类型数据,这种实现称为类型膨胀,所以C#的泛型为真实泛型。
泛型(Generic)的特性
使用泛型是一种增强程序功能的技术,具体表现在以下几个方面:
- 它有助于您最大限度地重用代码、保护类型的安全以及提高性能。
- 您可以创建泛型集合类。.NET 框架类库在 System.Collections.Generic 命名空间中包含了一些新的泛型集合类。您可以使用这些泛型集合类来替代 System.Collections 中的集合类。
- 您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
- 您可以对泛型类进行约束以访问特定数据类型的方法。
- 关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。
泛型是个好东西,能够有效的约束烂代码,还能更显式的提醒开发人员该如何使用这个类。使用泛型的情况有很多,但对于新人来说理解易应用难,需要经验的积累才能熟练应用。
泛型使得类与类的关联更加稳定,使代码更加严谨。
泛型约束
如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。这些限制称为约束。约束是使用 where 上下文关键字指定的。泛型约束是泛型的核心。
下表列出了五种类型的约束:
派生约束
1.常见的
public class MyClass5<T> where T :IComparable { }
2.约束放在类的实际派生之后
public class B { }
public class MyClass6<T> : B where T : IComparable { }
3.可以继承一个基类和多个接口,且基类在接口前面
public class B { }
public class MyClass7<T> where T : B, IComparable, ICloneable { }
构造函数约束
1.常见的
public class MyClass8<T> where T : new() { }
2.可以将构造函数约束和派生约束组合起来,前提是构造函数约束出现在约束列表的最后
public class MyClass8<T> where T : IComparable, new() { }
值约束
1.常见的
public class MyClass9<T> where T : struct { }
2.与接口约束同时使用,在最前面(不能与基类约束,构造函数约束一起使用)
public class MyClass11<T> where T : struct, IComparable { }
引用约束
public class MyClass10<T> where T : class { }
多个泛型参数
public class MyClass12<T, U> where T : IComparable where U : class { }
泛型类
最常见的泛型类,自定义Collection:
using System;
using System.Collections.Generic;
namespace GenericApplication
{
public class MyGenericArray<T>
{
private T[] array;
public MyGenericArray(int size)
{
array = new T[size + 1];
}
public T getItem(int index)
{
return array[index];
}
public void setItem(int index, T value)
{
array[index] = value;
}
}
class Tester
{
static void Main(string[] args)
{
// 声明一个整型数组
MyGenericArray<int> intArray = new MyGenericArray<int>(5);
// 设置值
for (int c = 0; c < 5; c++)
{
intArray.setItem(c, c*5);
}
// 获取值
for (int c = 0; c < 5; c++)
{
Console.Write(intArray.getItem(c) + " ");
}
Console.WriteLine();
// 声明一个字符数组
MyGenericArray<char> charArray = new MyGenericArray<char>(5);
// 设置值
for (int c = 0; c < 5; c++)
{
charArray.setItem(c, (char)(c+97));
}
// 获取值
for (int c = 0; c < 5; c++)
{
Console.Write(charArray.getItem(c) + " ");
}
Console.WriteLine();
Console.ReadKey();
}
}
}
继承泛型约束
先提供一个泛型基类B:
public class B<T>{ }
1.在从泛型基类派生时,可以提供类型实参,而不是基类泛型参数
public class SubClass11 : B<int>
{ }
2.如果子类是泛型,而非具体的类型实参,则可以使用子类泛型参数作为泛型基类的指定类型
public class SubClass12<R> : B<R>
{ }
3.在子类重复基类的约束(在使用子类泛型参数时,必须在子类级别重复在基类级别规定的任何约束)
public class B<T> where T : ISomeInterface { }
public class SubClass2<T> : B<T> where T : ISomeInterface { }
4.构造函数约束
public classB<T> where T : new()
{
public T SomeMethod()
{
return new T();
}
}
public class SubClass3<T> : B<T> where T : new(){ }
泛型方法
我们可以通过类型参数声明泛型方法。下面的程序说明了这个概念:
using System;
using System.Collections.Generic;
namespace GenericMethodAppl
{
class Program
{
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
static void Main(string[] args)
{
int a, b;
char c, d;
a = 10;
b = 20;
c = 'I';
d = 'V';
// 在交换之前显示值
Console.WriteLine("Int values before calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values before calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
// 调用 swap
Swap<int>(ref a, ref b);
Swap<char>(ref c, ref d);
// 在交换之后显示值
Console.WriteLine("Int values after calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values after calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
Int values before calling swap:
a = 10, b = 20
Char values before calling swap:
c = I, d = V
Int values after calling swap:
a = 20, b = 10
Char values after calling swap:
c = V, d = I
泛型方法的重载
//第一组重载
void MyMethod1<T>(T t, int i){ }
void MyMethod1<U>(U u, int i){ }
//第二组重载
void MyMethod2<T>(int i){ }
void MyMethod2(int i){ }
//第三组重载,假设有两个泛型参数
void MyMethod3<T>(T t) where T : A { }
void MyMethod3<T>(T t) where T : B { }
//第四组重载
public class MyClass8<T,U>
{
public T MyMothed(T a, U b)
{
return a;
}
public T MyMothed(U a, T b)
{
return b;
}
public int MyMothed(int a, int b)
{
return a + b;
}
}
泛型方法的重写
public class MyBaseClass1
{
public virtual void MyMothed<T>(T t) where T : new() { }
}
public class MySubClass1 : MyBaseClass1
{
public override void MyMothed<T>(T t) // 不能重复任何约束
{ }
}
public class MyBaseClass2
{
public virtual void MyMothed<T>(T t)
{ }
}
public class MySubClass2 : MyBaseClass2
{
public override void MyMothed<T>(T t) // 重新定义泛型参数T
{ }
}
虚方法泛型
- 使用实参继承的时候方法要使用实参的类型
- 使用泛型继承时,方法也是泛型
public class BaseClass4<T>
{
public virtual T SomeMethod()
{
return default(T);
}
}
public class SubClass4 : BaseClass4<int> // 使用实参继承的时候方法要使用实参的类型
{
public override int SomeMethod()
{
return 0;
}
}
public class SubClass5<T> : BaseClass4<T> // 使用泛型继承时,方法也是泛型
{
public override T SomeMethod()
{
return default(T);
}
}
泛型委托
委托级别的约束只在声明委托变量和实例化委托时使用,类似于在类型和方法的作用范围中实施的其他任何约束。
您可以通过类型参数定义泛型委托。例如:
delegate T NumberChanger<T>(T n);
下面的实例演示了委托的使用:
using System;
using System.Collections.Generic;
delegate T NumberChanger<T>(T n);
namespace GenericDelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
// 创建委托实例
NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);
NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);
// 使用委托对象调用方法
nc1(25);
Console.WriteLine("Value of Num: {0}", getNum());
nc2(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
Value of Num: 35
Value of Num: 175
通过这种方式,就能够实现多个类型共同使用同一个委托,你传给我什么类型,我就给你返回什么类型。
泛型强转
泛型参数隐式强制转换
编译器只允许将泛型参数隐式强制转换到 Object 或约束指定的类型。
class MyClass<T> where T : BaseClass, ISomeInterface
{
void SomeMethod(T t)
{
ISomeInterface obj1 = t;
BaseClass obj2 = t;
object obj3 = t;
}
}
变通方法:使用临时的 Object 变量,将泛型参数强制转换到其他任何类型
class MyClass2<T>
{
void SomeMethod(T t)
{
object temp = t;
BaseClass obj = (BaseClass)temp;
}
}
泛型参数显示强制转换
编译器允许您将泛型参数显式强制转换到其它任何接口,但不能将其转换到类。
class MyClass1<T>
{
void SomeMethod(T t)
{
ISomeInterface obj1 = (ISomeInterface)t; // 接口可以强转
//BaseClass obj2 = (BaseClass)t; // 普通类不能强转,不能通过编译
}
}
泛型参数强制转换到其他任何类型
使用临时的 Object 变量,将泛型参数强制转换到其他任何类型。
class MyClass2<T>
{
void SomeMethod(T t)
{
object temp = t;
BaseClass obj = (BaseClass)temp;
}
}
使用is和as运算符
public class MyClass3<T>
{
public void SomeMethod(T t)
{
if (t is int) { }
if (t is LinkedList<int>) { }
string str = t as string;
if (str != null) { }
LinkedList<int> list = t as LinkedList<int>;
if (list != null) { }
}
}
C#泛型与Java泛型
C#的泛型是真实泛型,而Java的泛型是伪泛型。C#的泛型,无论是源码,IL语言,还是CLR中,都是完全不同的类型,称为类型膨胀。
Java的泛型只在源码中存在,编译后就变成了原始类型RawType,并在相应地方插入了强转代码,因此对于Java来说,ArrayList<int>
和ArrayList<String>
是同一个类。Java中的泛型只是一个语法糖,这种实现方法叫做类型擦除,这种泛型也被叫做伪泛型。
C#的泛型是类型安全的,而Java的泛型缺可能在运行时出现类型错误。
本文部分内容转自:https://www.cnblogs.com/arxive/p/6179972.html
感谢分享
更多内容请查看总目录【Unity】Unity学习笔记目录整理
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)