《CLR Via C#(第三版)》 第十二章

第12章 泛型

CLR允许创建泛型引用类型和泛型值类型,但不允许创建泛型枚举类型。

泛型的主要好处是减少代码量,减少类型转换次数,提高代码性能。

12.3 泛型基础结构

所有引用类型的实参或变量实际只是指向堆上的对象的指针,在32位windows系统上,全部为32位指针;在64位windows系统上,全部为64位指针。

12.6 委托和接口的逆变和协变泛型类型实参

泛型类型中只有委托跟接口有协变与逆变。

不变量(invariant) 意味着泛型类型参数不能更改。

逆变量(contravariant) 意味着泛型类型参数可以从一个基类更改为该类的派生类,在C#中,用in关键字标记你变量形式的泛型类型参数。逆变量泛型参数只出现在输入位置。

协变量(covariant)意味着泛型类型参数可以从一个派生类更改为它的基类。在C#中,是用out关键字标记协变量形式的泛型类型参数。协变量类型参数只能出现在输出位置。

namespace GenericTypeApp
{
    class Program
    {
        static void Main(string[] args)
        {
            GenericClass<Class2, Class2> cal1 = new TheClass<Class2, Class2>();
            //out 协变是从基类转到派生类  in 逆变是从派生类到基类
            GenericClass<Class1, Class3> cal2 = cal1;          
            cal2.NoneGenericMethod(new Class3());
        }
    }

    interface GenericClass<out T1, in T2> where T2 : T1
    {
        //协变参数必须是输出值,抗变类型必须是输入参数
        T1 NoneGenericMethod(T2 p2);
    }

    class TheClass<T1, T2> : GenericClass<T1, T2> where T2 : T1
    {
        public T1 NoneGenericMethod(T2 p2)
        {
            return p2;
        }
    }

    class Class1 { }
    class Class2 : Class1 { }
    class Class3 : Class2 { }
}

12.7 泛型方法

C#编译器支持在调用一个泛型方法时进行类型推断(type inference)。

执行类型推断时,C#使用变量的数据类型,而不是由变量引用的对象的实际类型。

using System;

namespace GenericTypeApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Print(100.0); //Print<T>(T p)
            Print(100); //Print(int p)
        }

        public static void Print<T>(T p)
        {
            Console.WriteLine("Print<T>(T p)");
        }

        public static void Print(int p)
        {
            Console.WriteLine("Print(int p)");
        }
    }
}

12.9 可验证性和约束

    interface Interface1<T>
    {
        void Method2<T2>(T2 t) where T2 : IEnumerable<T2>;
    }

    class Class1<T>
    {
        public virtual int Method1<T1>(T1 t1, T1 t2) where T1:IComparable<T1>
        {
            return t1.CompareTo(t2);
        }
    }

    class Class2<T> : Class1<T>, Interface1<T>
    {
        public override int Method1<T1>(T1 t1, T1 t2)//不用声明约束,会直接从基类方法继承约束
        {
            return t2.CompareTo(t1);
        }

        public void Method2<T2>(T2 t) where T2 : IEnumerable<T2>
        {
            foreach (var i in t)
            {
                Console.WriteLine(i.ToString());
            }
        }
    }

重写一个虚泛型方法时,重写的方法必须指定相同数量的类型参数,而且这些类型参数会继承在基类方法上指定的约束。事实上,根本不允许重写方法的类型参数指定任何约束。但是类型参数的名字是可以改变的。实现接口方法时,方法必须指定约束。

12.9.1 主要约束

    class Class3
    {
        public void Method1<T>(T t) where T : Stream
        {
            t.Close();
        }

        public void Method2<T>(T t) where T : class
        {
            t = null;
        }

        public void Method3<T>(T t) where T : struct
        {
            t = new T();
        }
    }

12.9.2 次要约束

一个类型参数可以指定零个或者多个次要约束,次要约束代表的是一个接口类型。指定一个接口类型约束时,是向编译器承诺指定的类型必须实现了所有接口约束。

    interface Interface1 { }

    interface Interface2 { }

    class BaseClass { }

    class MyClass<T> where T : BaseClass, Interface1, Interface2 { }

12.9.3 构造器约束

一个类型参数可以指定零个或者一个构造器约束。指定构造器约束相当月向编译器承诺一个指定的类型实参是实现了公共无参构造器的一个非抽象类型。如果同时制定了构造器约束和struct约束,C#编译器会认为这是一个错误。

    internal class Class4<T> where T : new() { }
目前CLR以及C#编译器只支持无参构造器。

12.9.4其他可验证性问题

1. 泛型类型变量的转型

        public void Method3<T>(T t) 
        {
            string s = t as string;
            int y = (int)(object)t;
            string z = (string)(object)t;
        }
2. 将一个泛型类型变量设为默认值

        public void Method2<T>(T t) 
        {
            t = default(T);
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值