C#— —泛型

本文主要介绍C#中的泛型。

1. 泛型介绍

我们可以用int a变量来代表-2147483648—2147483647之间的任意一个整数,那么我们也可以用T来代表StringintObject、自定义类等数据类型,这就是泛型。

对于一些操作,如果需要处理多种数据类型,但处理逻辑是一样的,那么通过使用泛型,可以大大减少代码量。

1.1 泛型示例-集合

在C#中,已经预定义了许多泛型类和泛型接口,最常用的就是System.Collections.Generic命名空间中的集合类,例如:

List<String> stringList = new List<string>();
stringList.Add("hello");
stringList.Add("你好");

在实例化List时,需要指定类型参数为String,这也表示列表中只能存储字符串。如果我们尝试向列表中存储其他类型的数据,则会报错:

在这里插入图片描述

1.2 泛型示例-可空类型

System命名空间中,存在结构体Nullable<T>,他的作用是可以将值类型赋值为null。用法如下:

Nullable<int> a = null;
Console.WriteLine("a = " + a);

程序输出如下:

a = 

表示整数类型的变量a被赋值为null了,并没有报错。

我们也可以简写:

int? b = null;
int? c = 5;
Console.WriteLine("b = " + b);
Console.WriteLine("c = " + c);

结果为:

b =
c = 5

2. 自定义泛型

除了使用预定义的泛型类和泛型接口,我们也可以自己定义泛型类、泛型方法、泛型接口和泛型委托。

2.1 泛型类

语法为:

class ClassName<T1,T2,[T3,T4...]> [: 类, 接口] {
    ...
}

例如List<T>的定义如下:

public class List<T> : ICollection<T>, IEnumerable<T>, IEnumerable, IList<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection, IList
{
    ...
}

对于一个泛型中的继承和接口实现,需要满足以下任一种情况即可:

  • 泛型类继承中,父类的类型参数已被实例化,这种情况下子类不一定必须是泛型类,如下例的NodeClosed<T>Node1
  • 父类的类型参数没有被实例化(或者可以说没有被完全实例化),但来源于子类,也就是说父类和子类都是泛型类,并且二者有相同的类型参数,如NodeClosed<T>
class BaseNode { }
class BaseNodeGeneric<T> { }

// concrete type
class NodeConcrete<T> : BaseNode { }

//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }

//open constructed type
class NodeOpen<T> : BaseNodeGeneric<T> { }

//No error
class Node1 : BaseNodeGeneric<int> { }

//Generates an error
//class Node2 : BaseNodeGeneric<T> {}

//Generates an error
//class Node3 : T {}

//如果这样写的话,显然会报找不到类型T,S的错误  
//public class TestChild : Test< T, S> { }  

2.2 泛型方法

自定义泛型方法的语法为:

void methodName<T1,[T2,T3...]>([T1 t1, T2 t2, T3 t3]){
    ...
}

不论是静态方法还是实例方法,我们都可以定义为泛型方法;

不论是泛型类还是非泛型类,我们都可以在其中定义泛型方法。

对于泛型类,在非泛型方法中也可以使用类型参数,如下:

class SampleClass<T>
{
    void Swap(ref T lhs, ref T rhs) { }
}

如果在一个泛型类中定义泛型方法,并且泛型方法和泛型类的类型参数同名,那么泛型方法中的类型参数会掩盖泛型类的类型参数,导致CS0693警告:

在这里插入图片描述

我们可以通过改名的方式解决这类型警告,如将泛型方法的类型参数改名为U:

在这里插入图片描述

泛型方法也可以通过类型参数被重载:

void DoWork() { }
void DoWork<T>() { }
void DoWork<T, U>() { }

2.3 泛型接口

泛型接口与泛型类的定义相似,如下:

interface interfaceNmae<T1,[T2,T3...]>{
    ...
}

在C#的System.Collections.Generic命名空间中,预定义了一些泛型接口,例如IComparerIcomparable

public interface IComparer<in T>
{
    int Compare([AllowNull] T x, [AllowNull] T y);
}

public interface IComparable<in T>
{
    int CompareTo([AllowNull] T other);
}

泛型接口的继承与实现,与泛型类的类似,不再赘述。

2.4 泛型委托

泛型委托的声明语法如下:

delegate T[或者具体的类型参数] MyDelegate<T>(T value);

泛型委托支持在返回值和参数上应用类型参数.

这样如果有两个方法,使用同一个泛型委托就可以了,案例如下:

// 定义泛型委托
public delegate T MyDelegate<T>(T value1, T value2);

// 定义方法
public static int add(int i, int j)
{
    return i + j;
}

public static String concate(String str1, String str2)
{
    return str1 + str2;
}

// Main方法中
MyDelegate<int> myDelegateInt = Program.add;
MyDelegate<String> myDelegateString = Program.concate;
Console.WriteLine(myDelegateInt(1, 10));
Console.WriteLine(myDelegateString("hello ", "world"));

结果为:

11
hello world

3. 类型参数约束

对于一个类型参数,如果我们不加约束,那么这个类型参数可以为任意的数据类型,显然,毫无约束的类型参数使得程序出错的概率大大增加,所以有了类型参数约束。

我们使用where来指明类型参数约束。

常见的类型参数约束如下:

  • where T : struct:表示类型参数T必须是非空的值类型;
  • where T : class:表示类型参数T必须是引用类型;
  • where T : <base class name>:表示类型参数T必须是base class,或者是base class的子类;
  • where T : <interface name>:表示类型参数T必须是接口interface name,或者实现了该接口;
  • where T : new():表示类型参数T必须具有公共无参构造函数,当和其他约束一起时,该约束必须放在最后,并且new()约束不能和structunmanaged约束放在一起;
  • where T : U:表示类型参数T必须是类型参数U,或者T是U的子类;

更多类型参数约束,请参看Constraints on type parameters - C# Programming Guide | Microsoft Docs

我们可以同时对多个类型参数添加约束,每个类型参数分别使用一个单独的where关键字;也可以对一个类型参数添加多个约束,但要注意不要矛盾冲突,如:

class Base { }
class Test<T, U>
    where U : struct
    where T : Base, new()
{ }

如果一个类型参数没有任何约束,则称之为未绑定的类型参数,未绑定的类型参数有如下规则:

  • 不能使用==!=运算符;
  • 可以与System.Object相互转换,也可以转换为任意接口;
  • 可以与null比较;如果类型参数是值类型,则与null的比较结果始终为false

4. 参考资料

[1] Generic Type Parameters - C# Programming Guide | Microsoft Docs

[2] 浅谈C#泛型的定义、继承、方法和约束 - Jara - 博客园 (cnblogs.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值