EKsumic's Blog

let today = new Beginning();

Click the left button to use the catalog.

OR

C#泛型约束

C#泛型约束

一般来讲,泛型的类型参数(parameter)可以是任意类型的。

如果只允许使用特定的类型参数(argument),就可以指定约束,

 

规定:

where T:base-class   //必须是某个父类的子类
where T:interface     //表示T必须是实现了某个接口
where T:class           //表示T必须是引用类型
where T:struct         //表示T必须是值类型(非空)
where T:new()         //表示必须有无参构造函数
where U:T               //表示U继承于T

 

使用例:

class SomeClass{ }
interface Interface1{ }

class GenericClass<T,U> where T:SomeClass,Interface1
                                        where U:new()
{ . . . }

▢ 泛型的约束可以作用于类型或方法的定义。

 

如何约束:

 public interface IComparable<T>
{
            int CompareTo(T other);
}

static T Max<T>(T a, T b) where T : IComparable<T>
 {
            return a.CompareTo(b) > 0 ? a : b;
}

int z = Max(5, 10);  //10
string last = Max("ant", "zoo"); //zoo

注意这里只是示意,你不能自己定义IComparable,
 

 

泛型类型的子类

▢ 泛型class可以有子类,在子类里,可以继续让父类的类型参数保持开放

class Stack<T>  { . . . }
class SpecialStack<T>:Stack<T>  { . . . }

▢ 在子类里,也可以使用具体的类型来关闭(封闭)父类的类型参数

class IntStack:Stack<int> { . . . }

▢ 子类型也可以引入新的类型参数

class List<T> { . . . }
class KeyedList<T,TKey>:List<T> { . . . }

▢ 技术上讲,所有子类的类型参数都是新鲜的。你可以认为子类先把父类的类型参数(argument)给关闭,然后又打开了。为这个先关闭后打开的类型参数(argument)带来新的名称或含义。

class List<T> { . . . }
class KeyedList<TElement,TKey>:List<TElement>{ . . . }

 

 

自引用的泛型声明

▢ 在封闭类型参数(argument)的时候,该类型可以把自己作为具体的类型。

public interface IEquatable<T> { bool Equals(T obj);}

public class Balloon:IEquatable<Balloon>
{
    public string Color{get;set;}
    public int CC{get;set;}

    public bool Equals(Balloon b)
   {
      if(b==null) return false;
      return b.Color==Color&&b.CC==CC;
   }
}

class Foo<T> where T:IComparable<T>{ . . . }
class Bar<T> where T:Bar<T>{ . . .}

 

 

静态数据

▢ 针对每一个封闭类型,静态数据是唯一的

class Bob<T>{public static int Count;}

class Test
{
    static void Main()
    {
        Console.WriteLine(++Bob<int>.Count);          // 1 
        Console.WriteLine(++Bob<int>.Count);          // 2
        Console.WriteLine(++Bob<string>.Count);     // 1
        Console.WriteLine(++Bob<object>.Count);    // 1
    }  
}

 

 

类型参数和转换

▢ C#的转换操作符支持下列转换:
  ▢ 数值转换
  ▢ 引用转换
  ▢ 装箱拆箱转换
  ▢ 自定义转换
▢ 决定采用的是那种转换,发生在编译时,根据已知类型的操作数来决定。

比如,这么写会产生歧义,编译器以为你是想自定义转换

StringBuilder Foo<T>(T arg)
{
    if(arg is StringBuilder)
       return (StringBuilder) arg;    //Will not compile
    . . .
}

最佳解决方案:

StringBuilder Foo<T>(T arg)
{
    StringBuilder sb=arg as StringBuilder;
    if(sb!=null) return sb;
    . . .
}

经典处理方案:

return (StringBuilder)(object) arg;

拆箱的时候也会引发歧义:

int Foo<T>(T x)=>(int) x;   //Compile-time error

上面这个就有可能是个 数值转换、拆箱操作、自定义转换,

所以应该确定:

int Foo<T>(T x)=>(int)(object) x;

This article was last edited at 2020-03-18 01:13:03

* *