NET CLR via C#读书笔记 - 第十二章 泛型

1 泛型简介

  泛型是另一种形式的“代码重用”,或者说“算法重用”。开发者可以定义好算法,排序,搜索,交换等,但是这些算法的数据类型却不会事先指定,而是由使用者去决定,一个排序算法可以操作int和string等类型对象。
  CLR允许创建泛型引用类型和泛型值类型,不允许创建泛型枚举类型。CLR允许在引用类型,值类型或者接口中定义泛型方法。
  使用泛型的优点有一下几点:
  ① 源代码保护,使用泛型的开发人员不需要知道泛型的具体实现,只需要知道相关定义即可,C++模板的泛型技术必须公开源代码。
  ② 类型安全,试图使用不兼容类型的对象会造成编译时错误,或者运行时异常。
  ③ 清晰的代码逻辑,由于编译器的强制类型安全性,不需要在代码使用强制装换指明转换类型
  ④ 性能优势,泛型算法相比于常规化算法,用来值类型的实例时会进行装箱操作,在数据量较大时,会比较严重的损耗性能,使用泛型算法时,所有值类型以传值的方式进行传递,不会造成装箱操作。

2 开放类型和封闭类型

  开放类型:具有泛型类型参数的类型,CLR禁止创建开放类型的实例。当所有类型参数都传递了实际类型参数时就成了封闭类型,可以创建封闭类型的实例。
  为泛型类型的泛型类型参数指定具体类型时,CLR会在内部创建创建该具体类型对象的内部数据结构,且每个封闭类型都有自己独立的静态字段,举个例子,加入List中定义了任意一个静态字段,但是在List定义的静态字段不会在List和List中共享,他们是相互独立的,静态构造器也是同样的规则,针对List和List会分别执行它们自己的静态构造器。

3 泛型类型和继承

  泛型类型也是类型,所以也可以从其它类型派生。
  例如List从object派生,那么List和List也从object派生。
  假如直接定义以下这个链表节点类型:

internal sealed class NODE<T> 
{
     public T m_data;
     public NODE<T> m_next;
     
     public NODE(T data) : this(data, null) { }
     public NODE(T data, NODE<T> next)
     {
         this.m_data = data;
         this.m_next = next;
     }

     public override string ToString()
     {
         return m_data.ToString() + ((m_next != null) ? m_next.ToString():string.Empty);
     }

 }

  调用代码如下:

NODE<char> head = new NODE<char>('C');
head = new NODE<char>('B', head);
head = new NODE<char>('A', head);
Console.WriteLine(head.ToString());

  输出结果:

  在以上这个示例中,所有的数据节点的类型在使用时是确定的,不能同时存储int和char这两种类型。
  那如果需要同时存储多种数据类型的链表可以如何实现呢?
  方法一:可以使用非泛型方法NODE,不过这种方法会导致编译时的类型安全丢失,而且值类型会频繁装箱。
  方法二:定义非泛型NODE基类,在定义泛型GNODE类。具体实现如下:

internal class NODE
{
    protected NODE m_next;
    public NODE(NODE next)
    {
        m_next = next;
    }
}

internal sealed class GNODE<T> : NODE
{
    public T m_data;

    public GNODE(T data) : this(data, null) { }

    public GNODE(T data,NODE next) : base(next)
    {
        m_data = data;
    }

    public override string ToString()
    {
        return m_data.ToString() + ((m_next != null) ? m_next.ToString() : string.Empty);
    }
}

  调用如下:

NODE ghead = new GNODE<char>('.');
ghead = new GNODE<int>(20210926, ghead);
ghead = new GNODE<string>("Today is ", ghead);
Console.WriteLine(ghead.ToString());

  输出结果(ABC为第一种方式输出结果):
在这里插入图片描述

4 泛型类型同一性

  使用using简化写法而不要使用新增类定义来简化代码,新增类会导致同一性丢失,具体看如下代码:
  新增类定义:

internal sealed class IntList : List<int>{}
IntList iT = new IntList();
bool sameType = (typeof(List<int>) == typeof(IntList ))

  运行结果 sameType 为 false
  使用using语法:

using IntList = System.Collections.Generic.List<int>;
bool sameType = (typeof(List<int>) == typeof(IntList ))

  运行结果 sameType 为 true

5 可验证性和约束

5.1 主要约束

  泛型类型参数可以指定0或者多个主要约束,主要约束是代表非密封类的一个引用类型,不能指定为System.Object,System.Array,System.Delegate,System.MulticastDelegate,System.ValueType,System.Enum,System…Void.指定主要约束后,相当于向编译器承诺,后续指定的类型实参要么是约束类型,要么是约束类型的派生类型,语法如下:

internal sealed class Test<T> where T : Stream{
	public void M(T stream){
		stream.Close();
	}
}

class 和 struct 是两个特殊的主要约束,class表明实参必须是引用类型,struct表明泛型实参指定时要为引用类型。

5.2 次要约束

  次要约束可以有0个或者多个,次要类型有接口类型约束和类型参数约束两种,接口类型约束在第十三章中展开,类型参数约束是规定了泛型类型的指定实参,要么就是约束类型,要么就是约束类型的派生类型。代码如下:

private static List<TBase> ConvertList<T,TBase>(IList<T> list) where T : TBase{
	List<TBase> baseList = new List<TBase>(list.Count);
	for(int index = 0;index < list.Count; index++)
	{
		baseList.Add(List[index]);
	}
	return baseList;
}

  ConvertList指定了两个类型参数,其中T为TBase的类型参数约束,T指定的任何类型实参都必须兼容于TBase指定的类型实参。

5.3 构造器约束

  构造器约束可以有0个或者多个,泛型类型指定的类型实参实现了公共无参构造器的非抽象类型。

class TestCtor<T> where T : new(){
	public static T Factory() {
		return new T();
	}
}

5.4 泛型类型变量转型

  将泛型类型的变量转型为其它类型(与约束类型不兼容的)是非法的。

5.5 泛型类型变量设为默认值

  使用default 关键字

class TestCtor<T>(){
	T temp = default(T);
}

5.5 泛型类型变量与null比较

  无论泛型是否被约束,使用==或者!= 操作符与null作比较都是合法的。

5.6 两个泛型变量作比较

  如果两个泛型变量是引用类型,是合法的,如果是都为值类型,如果没有重载==或者!=操作符,则是非法的。

5.7 不要将泛型类型变量作为操作数使用

  编译器在编译时确定不了类型,不能向泛型类型的变量应用任何操作符。

  以上内容为对《NET CLR via C#(第四版)》第十二章内容的阅读笔记,只记录其中核心部分内容,书中对以上主题还进行详细的代码演示说明,书中还讨论了泛型接口和泛型委托,如有需要,请详细阅读本书第十二章内容,感谢您的阅读。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值