C# 泛型 Generic

泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。

C# 允许您使用类型参数(type parameter)而不使用特定数据类型来定义泛型类、接口、抽象类、字段、方法、静态方法、属性、事件、委托和运算符。 类型参数是创建泛型类型实例时指定的特定类型的占位符。

泛型类型是通过在类型名称后的尖括号中指定类型参数来声明的,例如 TypeName 其中 T 是类型参数。

下面这个简单的实例将有助于您理解这个概念:

泛型类

泛型类是使用类名后尖括号中的类型参数定义的。 下面定义了一个泛型类。

class Program
    {
        static void Main(string[] args)
        {
            var my = new MyFloatList<int>(); 
            my[0] = 1;
            my[1] = 2;
            my[2] = 3;
            my[3] = 4;
            for (int i = 0; i < 4; i++)
            {
                //注意这边是string了
                Console.Write(my[i.ToString()] + ",");
            }
        }
    }

    public class MyFloatList<T>
    {
        private T[] list = new T[4]; //当 T  是 int的时候这边就相当于 int[] list = new int[]了。

        public T this[int index]  //
        {
            get => list[index];
            set => list[index] = value;
        }

        public T this[string index]
        {
            get
            {
                var i = Convert.ToInt32(index);
                return list[i];
            }
            set
            {
                var i = Convert.ToInt32(index);
                list[i] = value;
            }
        }
    }

当它运行的时候它会输出

1,2,3,4,

代码的表现就是MyFloatList类里面的T会变替换成 int类型,

我们也可以把类型参数换成 string如下

 static void Main(string[] args)
        {
            var my = new MyFloatList<string>(); //可以看到 上面的int变成了 string。 当然也可以是其它类型了如 float decimal 等
            my[0] = "a";
            my[1] = "b";
            my[2] = "c";
            my[3] = "d";
            for (int i = 0; i < 4; i++)
            {
                //注意这边是string了
                Console.Write(my[i.ToString()] + ",");
            }
        }

当它运行的时候它会输出

a,b,c,d,

代码的表现就是MyFloatList类里面的T会变替换成 string类型,

多个泛型参数

KeyValuePair 是 一个系统类。我们看到它使用了两个类型参数,并且它的类型参数名不在是T了,变成了 TKey,TValue。

class KeyValuePair<TKey, TValue>
{
    public TKey Key { get; set; }
    public TValue Value { get; set; }
}

再来看其它的一个例子, 在 Restful的Api当中,我们有时候会返回一个特定的格式。我们就可以把返回类型 定义在泛型类型参数里。

class Program
    {
        static void Main(string[] args)
        {
            var vm = new ResultVm<DateTime>(DateTime.Now);
            Console.WriteLine(vm.PlayLoad);

        }
    }


    public class ResultVm<T1>
    {
        public ResultVm(T1 playLoad) //构造器这边不需要<>这个东西
        {
            this.PlayLoad = playLoad;
        }

        public T1 PlayLoad { get; set; }

        public List<string> ErrorMessages { get; set; }
    }

泛型(Generic)的特点

  • 泛型类增加了可重用性。 类型参数越多意味着它变得越可重用。 但是,过多的泛化会使代码难以理解和维护。
  • 泛型类可以是其他泛型或非泛型类或抽象类的基类。
  • 泛型类可以从其他泛型或非泛型接口、类或抽象类派生。

泛型方法 Generic Methods

一个方法里面的参数有类型参数,或者返回类型是类型参数的话,我们称它为泛型方法。

class MyList<T>
{
    private T[] _data = new T[10];
    
    public void AddOrUpdate(int index, T item)
    {
        if(index >= 0 && index < 10)
            _data[index] = item;
    }

    public T GetData(int index)
    {
        if(index >= 0 && index < 10)
            return _data[index];
        else 
            return default(T);
    }
}

上面的 AddorUpdate() 和 GetData() 方法是泛型方法。 item 参数的实际数据类型将在实例化 DataStore 类时指定,如下所示。


class Program
    {
        static void Main(string[] args)
        {
            var names = new MyList<string>();
            names.AddOrUpdate(0, "Ma");
            names.AddOrUpdate(1, "Lema");
            names.AddOrUpdate(2, ".net");

            var empIds = new MyList<int>();
            empIds.AddOrUpdate(0, 50);
            empIds.AddOrUpdate(1, 65);
            empIds.AddOrUpdate(2, 89);
            var id = empIds.GetData(0);

        }
    }

泛型参数类型可以与带或不带非泛型参数和返回类型的多个参数一起使用。 以下是有效的泛型方法重载。

public void AddOrUpdate(int index, T data) { }
public void AddOrUpdate(T data1, T data2) { }
public void AddOrUpdate<U>(T data1, U data2) { }
public void AddOrUpdate(T data) { }

非泛型类可以通过在带有方法名称的尖括号中指定类型参数来包含泛型方法,如下所示。

class Printer
{
    public void Print<T>(T data)
    {
        // var t = typeof(T); //通过这个也可以获取到运行时的类型
        Console.WriteLine(data);
    }

    public static void Print2<T>(T data)
    {
        Console.WriteLine(data);
    }
}

class Program
{
    static void Main(string[] args)
    {
        var printer = new Printer();
        printer.Print<int>(100);
        printer.Print(200); // 可以做到自动类型推断
        printer.Print<string>("Hello");
        Printer.Print2("World!"); // 可以做到自动类型推断 string

    }
}

静态类,和静态方法都是支持泛型的

泛型的优势

  • 泛型增加了代码的可重用性。 您不需要编写代码来处理不同的数据类型。
  • 泛型是类型安全的。 如果您尝试使用与定义中指定的数据类型不同的数据类型,则会出现编译时错误。
  • 泛型具有性能优势,因为它消除了装箱和拆箱的可能性。 (在没有泛型之前用的是所有类的基类object来做类似的效果)
下一篇:C# 泛型约束
最近更新的
...