C#/.NET-泛型


什么是泛型 Generic

  • 简单的来讲,泛型相当于模板,它允许您编写一个可以与任何数据类型一起工作的类或方法(说白了就是允许你根据模板制定自己需要的数据类型方法、类、接口)

  • 使用泛型是一种增强程序功能的技术,有助于您最大限度地重用代码、保护类型的安全以及提高性能

  • 标志就是以<T>为占位符的占位委托

注意:泛型类型要避免使用object类型,避免反复装箱拆箱的操作,会带来性能损耗

泛型的优点和原理

  • 最大的特点就是“通用”,C#在编译器下【这里还没确定泛型的确定类型,但是有T(机器码内以 ` 反单引号)作为一个占位符】生成/运行的时候会生成一个DLL/EXE文件,然后通过CLR环境【这一步确定泛型的具体类型 】进行二次编译,生成机器码(假如直接生成机器码的话在跨环境的情况下,机器内部硬件不同就可能不能执行,这样的话跨平台能力就会比较差)

泛型的约束

  • 规定泛型数据的类型,防止什么类型的数据都输入进来,写法:where
    约束的类型:
    new() 约束–表示只接受带有一个无参数的构造函数
    struct 值类型约束:(值类型:结构体struct/intdoublebool枚举。引用类型:接口委托object字符串
    class 引用类型约束
    – 自定义类型约束 where T : student
    – 多类型约束 where T : class,new()new()约束一定要写在多类型约束的最后面】,但是参数约束不能有冲突,比如值类型和引用类型不能同时存在。
public static void Show<T>(T t1) where T:new()  //只接收可以实例化的类,比如接口就不行

下面来写一个泛型的小demo,来更好的观测不同数据类型作为类型约束时,数据输出的差异,记得在测试时打上断点,方便调试

using System;

namespace GenericTestProject
{
    public class GenericTestClass<T> {
        //定义私有域数组变量,并且泛型定义数组类型
        private T[] genericArray;
        //使用构造方法对类中的属性进行初始化
        public GenericTestClass(int size){
            //初始化创建出泛型对应的数组,包括类型和长度
            //例如 T 为int 时,下面语句就是:genericArray = new int[size + 1];
            genericArray = new T[size + 1];
        }
        //设置数组的值
        public void setItem(int index, T value) {
            genericArray[index] = value;
        }
        //获取数组的值
        public T getItem(int index) {
            return genericArray[index];
        }
    }

    class Program
    {
        //程序入口
        public static void Main(string[] args){
            //测试当泛型数据为int类型时的情况
            GenericTestClass<int> genTest = new GenericTestClass<int>(5);

            for (int i = 0; i < 5; i++) {
                genTest.setItem(i, i + 1);
            }

            for (int j = 0; j < 5; j++) {
                Console.Write(genTest.getItem(j) + " ");
            }

            //下面测试使用char作为泛型类型数据时的情况
            Console.WriteLine("\n----------------------------");
            GenericTestClass<char> genTest2 = new GenericTestClass<char>(5);

            for (int a = 0; a < 5; a++) {
                genTest2.setItem(a, (char)(a + 96));
            }

            for (int b = 0; b < 5; b++) {
                Console.Write(genTest2.getItem(b) + " ");
            }
        }
    }
}

泛型的其它使用

泛型字典Dictionary<K,V> ,键/值对,是成对存在的

  • 泛型字典内部有自带的方法(Add)
Dictionary<int, string> dic = new Dictionary<int,string>();
//
dic.Add(1,"C#");
dic.Add(1,"C++");
dic.Add(1,"C");

foreach(var item in dic){
	Console.WriteLine(item.Key.ToString()+" " + item.Value);
}
  • ContainsValue,数据匹配;Remove,根据键位进行删除
bool isContains = dic.ContainsValue("C#"); 

dic.Remove(1); //根据键位匹配,删除了字典内对应的数据值

泛型自定义

  • 举例:操作数据库,读取数据时,每个表返回数据格式都不同。使用泛型自定义的话可以封装这样用不同的格式进行接收!通用的函数方法可以使代码的复用率提高,减少冗余代码
/// <summary>
    /// 自定义泛型类,这样可以提高代码复用
    /// 假如是SQL helper类提取数据库内不同数据时,可以使用
    /// 一段代码块适配不同的数据然后调取配置
	/// 使用占位符T表示待定数据类型,通过实例化传递类型数据定义泛型类的数据类型,泛型定义符号可以定义多个
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class myGeneric<T>
    {
        private T dataTest;
        //构造函数
        public myGeneric(T dataTest)
        {
            this.dataTest = dataTest;
        }
        public void getDataTest() {
            Console.WriteLine(dataTest);
        }
    }
    
........
        public static void Main(string[] args){
            myGeneric<int> mg = new myGeneric<int>(111);
            mg.getDataTest();
  			myGeneric<string> mg2 = new myGeneric<string>("abc");
            mg2.getDataTest();
        }

泛型方法

  • 使用泛型之后,数据库的crud就不再需要写这么多个方法就能通用
public void Show<T>(T showSomething){
	Console.WriteLine(showSomething.ToString());
}
......
 	show("展示泛型方法");
    show(101010);

泛型约束

public static void Main(string[] args){
	//实例化两个对象实例
    ChineseHuman ch = new ChineseHuman() { HumanName = "小明" };
    BirdClass bc = new BirdClass() {AnimalType = "鹦鹉" };
   	//调用函数,对泛型类型进行定义,并且将实例作为实参传入
    //可以发现的是,泛型定义类型已经被方法体内的泛型约束给限制了,
  	//无法定义为其它类型的泛型定义
    //泛型定义必须要与展示方法show的where约束类型相同,并且传参也要与泛型类型相同
    FunClass.ShowAnimal<AnimalClass>(bc);
    FunClass.ShowHuman<ChineseHuman>(ch);
}

//展示方法
public class FunClass {
  		//泛型约束where 
       public static void ShowHuman<T>(T Typedef) where T : HumanClass {
           Console.WriteLine($"这个人名字是{Typedef.HumanName}");
       }
       public static void ShowAnimal<T>(T Typedef) where T : AnimalClass{
           Console.WriteLine($"这只鸟的种类是{Typedef.AnimalType}");
       }
}

... 继承
public class BirdClass : AnimalClass{
      public string BirdName { get; set; }
}
...	继承	
public class ChineseHuman : HumanClass{
      public string Cname { get; set; }
}
...	父类
public class HumanClass{
      public string HumanName { get; set; }
}
...	父类
public class AnimalClass{
      public string AnimalType { get; set; }
}

结尾

那么话说回来,使用泛型对于我这个菜鸡来说有什么好处呢?好处就是,从前刚接触代码都是有一个需求就写一个,现在使用泛型我可以把一些重复的、近似度高的代码复用起来,通过泛型这个写法最大程度地复用代码,同时泛型可以在一些特殊场景让我的代码具备一定的安全性。
回到我的专业领域,在嵌入式中,开发芯片的内存可谓是寸土寸金,如果不将可复用的代码最大程度地利用起来,转而为每个功能都写上一个功能函数,那ROM岂不是被大量的浪费掉了,这只是个人比较片面的看法,希望对你有所帮助


文章作者: Viktor Chen
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Viktor Chen !
评论
  目录