• (八)CSharp-泛型类和参数约束(1)


    一、C# 中的泛型

    泛型(generic)特性可以让多个类型共享一组代码。

    泛型类型不是类型,而是类型的模板。

    请添加图片描述

    C# 提供了5种类型:类、结构、接口、委托和方法。

    请添加图片描述

    泛型类

    请添加图片描述

    泛型的主要优点:

    • 性能
      类型转换时,非泛型的类型进行装箱和拆箱时,会使得性能损失比较大。(需要转换运算符以及拷贝内存)

    • 类型安全
      如果在泛型类中,定义了具体类型,编译器就不会编译代码。

    • 二进制代码重用
      泛型可以定义一次,就可以用于不同类型的实例化。

    • 代码的扩展
      1)泛型类的定义会放在程序集中,所以用特定类型实例化泛型类不会在 IL 代码中复制这些类。(即类型实例化的代码在使用泛型时,不会进行拷贝这些实例化的代码)。
      2)在 JIT 编译器把泛型类编译为本地代码时,会给每个值类型创建一个新类引用类型共享同一个本地类的所有相同的实现代码。(比如 List《int》会对其类型进行复制,而 List《MyClass》会对其类型进行引用。)

    • 命名约定

    命名规则:

    泛型类型的名称用字母 T 作为前缀。
    泛型类型允许用任意类替代,且只使用了一个泛型类型。

     public class List { }
        public class LinkedList { }
    
    • 1
    • 2
    • 如果泛型类型有特定的要求(例如,它必须实现一个接口或派生自基类),或者使用了两个或多个泛型类型,就应该泛型类型使用描述性的名称:
        //泛型委托
        public delegate void EventHandler(object sender, TEventArgs e);
        public delegate TOutput Converter(TInput from);
        //泛型类
        public class SortedList { }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二、声明泛型类

    泛型声明定义的代码:

        //T1,T2 为类型参数
        class SomeClass
        {
            public T1 SomeVar;
            public T2 OtherVar;
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                //构造的类型
                var first = new SomeClass();
                var second = new SomeClass();
    
                Console.ReadKey();
            }
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    执行测试代码的作用:

       //SomeClass()
        class SomeClass
        {
            public short SomeVar;
            public int OtherVar;
        }
    
       //SomeClass()
        class SomeClass
        {
            public int SomeVar;
            public short OtherVar;
        }
        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    三、比较泛型和非泛型栈

    实现泛型栈的代码例子:

        class MyStack
        {
            T[] StackArray;
            int StackPointer = 0;
    
            public void Push(T x)
            {
                if (!IsStackFull)
                    StackArray[StackPointer++] = x;
            }
    
            public T Pop()
            {
                return (!IsStackEmpty) ? StackArray[--StackPointer] : StackArray[0];
            }
    
    
            const int MaxStack = 10;
            bool IsStackFull { get { return StackPointer >= MaxStack; } }
            bool IsStackEmpty { get { return StackPointer <= 0; } }
    
            public MyStack()
            {
                StackArray = new T[MaxStack];
            }
    
            public void Print()
            {
                for (int i = StackPointer - 1; i >= 0; i--)
                    Console.WriteLine($"  Value: { StackArray[i] }");
    
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                MyStack StackInt = new MyStack();
                MyStack StackString = new MyStack();
    
                StackInt.Push(3);
                StackInt.Push(5);
                StackInt.Push(7);
                StackInt.Push(9);
                StackInt.Print();
    
                StackString.Push("This is fun");
                StackString.Push("Hi there!  ");
                StackString.Print();
                Console.ReadKey();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    表-非泛型栈和泛型栈之间的区别

    非泛型泛型
    源代码大小更大:需要为每一种类型编写一个新的实现(有重复的代码逻辑)更小:不管构造类型的数量有多少,只需要一个实现
    可执行文件大小无论每一个版本的栈是否会被使用,都会在编译的版本中出现可执行文件中只会出现有构造类型的类型 (疑问:设计泛型类型的代码逻辑不在可执行文件里吗?似乎有些明白了,泛型只会放在程序集中,而不会拷贝这些实例化的代码,所以编译器不会编译设计泛型的代码。)
    写的难易度易于书写,因为它更具体比较难写,因为它更抽象 (我觉得因为想要通用任何可支持的类型,需要考虑点多,越是抽象(类型特征越多),设计的代码结构就越复杂。)
    维护的难易度更容易出问题,因为所有修改需要应用到每一个可用的类型上 (因为类型已固定了,如果更改类型或者代码结构,就会把所有应用到的地方都修改一遍,容易出错。)易于维护,因为只需要修改一个地方

    四、类型参数的约束

    1、类型参数的约束

    由于泛型不知道它们保存的项的类型是什么,也就不会知道这些类型实现的成员,就不会对这些成员进行运算符的事情。

    所以,如果泛型类需要调用泛型类型中的方法,就必须添加约束。

    未绑定的类型参数: 符合约束的未绑定的类型参数。如果泛型里有 Object 成员,那么这个Object 成员是已知的类型,可以对它做一些如 ToSting、Equals 以及 GetType 方法的处理。而如果其他未知类型的成员不能像 Object 成员那样可以直接处理的,是未绑定的类型参数。

    class Simple
    {
    static public bool LessThan(T i1,T i2)
    {
    //错误,因为i1 和 i2 属于未绑定的类型参数,
    //不能使用 < 运算符进行处理
    retrun i1 < i2;
    }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2、Where 子句

    约束使用 where 子句列出。

    • 每一个有约束的类型参数都有自己的 where 子句。
    • 如果形参有多个约束,它们在 where 子句中使用逗号分隔。

    where 子句的语法如下:

    where TypeParam : constraint,constraint,...
    //where:关键字
    //TypeParam:类型参数
    //constraint,constraint,...:约束列表
    
    • 1
    • 2
    • 3
    • 4

    关于 where 子句的要点:

    • 它们在类型参数列表的关闭尖括号之后列出。
    • 它们不使用逗号或其他符号分隔。
    • 它们可以以任何次序列出。
    • where 是上下文关键字,所以可以在其他上下文中使用。
    //T1 未绑定,T2 和 T3 具有约束
    class MyClass < T1,T2,T3 >
    		where T2:Customer //T2的约束
    		where T3:IComparable //T3的约束
    {
    ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、约束类型和次序

    公有5种类型的约束:

    约束类型描述
    类名只有这个类型或从它派生的类才能用作类型实参
    class任何引用类型,包括类、数组、委托和接口都可以用作类型实参
    struct任何值类型都可以用作类型实参(包括枚举? 好吧,所有值类型,那就是包括枚举)
    接口名只有这个接口或实现这个接口的类型才能用作类型实参
    new()任何带有无参公共构造函数的类型都可以用作类型实参。这叫作构造函数约束

    where 子句可以以任何次序列出。但是,where 子句中的约束必须有特定的顺序

    • 最多只能有一个主约束,而且必须放在第一位。
    • 可以有任意多的接口名称约束
    • 如果存在构造函数约束,则必须放在最后
    约束约束类型个数
    主约束ClassName class struct0或1个
    次约束InterfaceName0或多个
    构造函数约束new()0或1个
    class SortedList
    		where S:IComparable{...}
    
    calss LinkedList
    		where M : IComparable
    		where M : ICloneable{...}
    
    class MyDictonary
    		where KeyType:IEnumerable,
    		new()               {...}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    Oracle数据库面试题-10
    【异常检测】【PaDiM】论文简单梳理与代码实现
    ASP.NET教学科研成果管理系统VS开发sqlserver数据库web结构c#编程计算机网页项目
    青岛大学数据结构与算法——第8章
    天翼物联网平台(AIoT)无感迁移能力
    使用Vue脚手架
    bat写的git命令大全(适合初学者)掌握命令行下的Git操作!
    Android开发超详细介绍
    JVM学习笔记——垃圾回收篇
    【ELM预测】基于matlab探路者算法优化极限学习机预测(含前后对比)【含Matlab源码 2204期】
  • 原文地址:https://blog.csdn.net/chen1083376511/article/details/131153272