MiCoos 哟,写bug呢?

C#基础-泛型

2018-07-04
GHMicoos

概述:C#中泛型的使用。

一 什么是泛型

通过参数化类型来实现在同一份代码上操作多种数据类型。利用“参数化类型”将类型抽象化,从而实现灵活的复用。

二 泛型的特点

  • NET 自从2.0版本就开始支持泛型。
  • 泛型不仅是C#编程语言的一部分,而且是CLR定义的。即若泛型类在C#中定义,也可以在VB中用一个特定的类型实例化。
  • 泛型不仅限于类,接口、方法、结构、委托都可以是泛型的。

三 使用泛型的好处

  • 不必给不同类型编写功能相同的许多方法和类,只需创建一个方法或类即可。
  • 类型安全(相比于Object类)
  • 性能 避免不必要的装箱和拆箱
  • 抽象之后,更容易修改代码。

四 泛型类的定义

1.泛型的命名约定

  • 泛型类型的名称用 T 作为前缀
  • 若只使用一个泛型类型且没有特殊要求,可用 T 代替泛型类型名称;若泛型类型有特殊要求或者使用了多个泛型类型,就应该是用描述性的名称

2.创建泛型类

public class Generic<T>
{
    public List<T> List { get; set; }

    public Generic(T data)
    {
        if (List == null)
        {
            List = new List<T>();
        }
        List.Add(data);
    }

}

五 泛型类的功能

1.默认值

  • 不能把 null 赋值给泛型类型(变量),因为泛型类型可以实例化为值类型,而 null 只能用于引用类型
  • 如果要给泛型(变量)赋值默认值,那么使用 default 关键字,那么值类型赋值为0,引用类型赋值为null
public class Generic<T>
{
    public List<T> List { get; set; }

    public static T BaseGeneric = default(T);

    public Generic(T data)
    {
        if (List == null)
        {
            List = new List<T>();
        }
        List.Add(data);
    }
}

2.约束

  • 约束泛型类型的类型,如 必须实现某个接口、必须是引用、必须是值类型、必须派生自某个基类、必须有一个默认的构造函数。
约束 说明
where T : struct 结构约束,类型T必须是值类型。
where T : class 类约束,类型T必须是引用类型。
where T : IFoo 类型T必须实现接口IFoo。
where T : Foo 类型T必须派生自基类Foo。
where T : new() 构造函数约束,类型T必须有一个默认构造函数。
where T1 : T2 裸型约束, 类型T1必须派生自泛型类型T2.

注意:只能为默认构造函数定义构造函数约束,其他的构造函数不能对顶构造函数约束。

public class Generic<T, T2> where T : class, IFoo, IFoo, T2 where T2 : class, new()
{
    //new()约束必须是最后一个约束
    //class 和 struct约束必须在其他约束之前
    // class 和 struct是不能同时存在的(理论)
    public List<T> List { get; set; }

    public static T BaseGeneric = default(T);

    public Generic(T data)
    {
        if (List == null)
        {
            List = new List<T>();
        }
        List.Add(data);
    }

}
public interface IFoo
{
}
public class Foo : IFoo
{
}

3.继承

泛型类型可以实现泛型接口,也可以派生自一个类。 要求必须重复接口的泛型类型,或者必须指定基类的类型。 所以派生出来的子类,可以是泛型的也可以不是泛型的

public class Father<T> { }
//重复接口(基类)的泛型类型
public class SonOne<T> : Father<T> { }
//指定接口(基类)的泛型类型
public class SonTwo : Father<object> { }

4.静态成员

泛型的静态成员只能在类的一个实例中共享。

public class StaticDemo<T>
{
    //泛型类的静态字段
    public static int X;
}

class Program
{
    static void Main(string[] args)
    {
        StaticDemo<string>.X = 1;
        StaticDemo<int>.X = 2;
        Console.WriteLine(StaticDemo<string>.X);//2
    }
}

六 泛型类的使用(Demo)

class Program
{
    static void Main(string[] args)
    {
        IList<int> list = new List<int>() { 9, 5, 4, 7, 1, 2, 5, 3, 2, 5, 8, 6, 5, 4, 7, 8 };
        for (int i = 0; i < list.Count; i++)
        {
            Console.Write("{0} , ", list[i]);
        }
        Console.WriteLine();
        BubbleSort<int>.Sort(list);
        for (int i = 0; i < list.Count; i++)
        {
            Console.Write("{0} , ", list[i]);
        }
        Console.ReadKey();
    }
}

public class BubbleSort<T> where T : IComparable<T>
{
    public static void Sort(IList<T> list)
    {
        int count = list.Count;
        for (int k = 0; k < count - 1; k++)
        {
            for (int i = 0; i < count - 1; i++)
            {
                if (list[i].CompareTo(list[i + 1]) > 0)
                {
                    T temp = list[i];
                    list[i] = list[i + 1];
                    list[i + 1] = temp;
                }
            }
        }
    }
}

七 C#中的泛型

**1.泛型结构(Nullable)**

  • 结构也可以是泛型的。
  • 场景:数据库和XML文件中数字和编程语言中的数字有显著不同的特性,编程语言中的数字不能为null,但是数据库和XML中的数字都可以为null
  • Nullable<T> 定义了一个约束:其中的泛型类型T必须是一个结构。
  • 可空类型可以与算术运算符一起使用,如果其中一个是null,那么结果就是null。
  • 可空类型转换为T时候,需要强制转换,或者使用合并运算符(??)
  • Nullalbe<T> 可是使用 int? bool? datetime? 等代替
static void Main(string[] args)
{
    Nullable<int> nullInt = null;
    Nullable<long> nullLong = null;
    int numberInt = nullInt ?? 0;
    long numberLong = 0;
    if (nullLong.HasValue)
    {
        numberLong = nullLong.Value;
    }
    Console.ReadKey();
}

八 泛型的协变和抗变

本节主要参看博文

1.泛型协变的特点

  • 以前的泛型系统(或者说没有in/out关键字时),是不能“变”的,无论是“逆”还是“顺(协)”。
  • 当前仅支持接口和委托的逆变与协变 ,不支持类和方法。但数组也有协变性。
  • 值类型不参与逆变与协变。

  

2.下面只是我自己的总结,

详情可以看引用的博文。

  • 协变 T只能作为方法的返回类型 Foo<父类>=Foo<子类>
  • 抗变 T只能作为方法的参数 Foo<子类>=Foo<父类>
  • 协变和抗变的根本是利用子类的引用能安全第赋值给父类的引用。

Similar Posts

Comments