C#基础知识之泛型

泛型在c#中有很重要的位置,对于写出高可读性,高性能的代码有着关键的作用。

其实官方文档说明的很详细,我这边算是做个记录吧

一、什么是泛型?

  泛型是 2.0 版 C# 语言和公共语言运行库 (CLR) 中的一个非常重要的新功能。

  泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。您可以通过数据类型的替代参数编写类或方法的规范。当编译器遇到类的构造函数或方法的函数调用时,它会生成代码来处理指定的数据类型。

  简单的说,我们在编写程序时,经常遇到两个模块的功能非常相似,只是一个是处理int数据,另一个是处理string数据,或者其他自定义的数据类型,但我们没有办法,只能分别写多个方法处理每个数据类型,因为方法的参数类型不同。有没有一种办法,在方法中传入通用的数据类型,这样不就可以合并代码了吗?泛型的出现就是专门解决这个问题的。

二、为什么使用泛型?

  我们先来看一下下面的代码

static void Main(string[] args)
{
    #region 方式一 普通方法
    ShowInt(1);
    ShowStr("string1");
    #endregion
    Console.ReadKey();
}
public static void ShowInt(int num)
{
    Console.WriteLine("输出INT型数据:{0}", num);
}
public static void ShowStr(string str)
{
    Console.WriteLine("输出String型数据:{0}",str);
}         

这两个方法,除了传入的参数不同外,其里面实现的功能都是一样的。在1.1版的时候,还没有泛型这个概念,那么怎么办呢。就有人想到了OOP三大特性之一的继承,C#语言中,所有类型都源自同一个类型,那就是object。其实就是一个装箱拆箱的过程,会损耗一些性能。

static void Main(string[] args)
{
    #region 方式二 继承,C#语言中,所有类型都继承自object。
    ShowObj(2);
    ShowObj("string2");
    #endregion
    Console.ReadKey();
}
public static void ShowObj(object obj)
{
    Console.WriteLine("输出的类型{0},值{1}",obj.GetType(),obj);
}           

微软在2.0的时候发布了泛型。接下来我们用泛型方法来实现该功能。下面是一个简单的泛型示例:

static void Main(string[] args)
{
    #region 方式三 泛型
    ShowInfo(3);
    ShowInfo("STRING3");
    ShowInfo<int>(4);
    ShowInfo<string>("STRING4");
    #endregion
    Console.ReadKey();
}
public static void ShowInfo<T>(T para)
{
    Console.WriteLine("输出的类型{0},值{1}",para.GetType(),para);
}

三、泛型的特性

  使用泛型是一种增强程序功能的技术,具体表现在以下几个方面:

  • 它有助于您最大限度地重用代码、保护类型的安全以及提高性能。
  • 您可以创建泛型集合类。.NET 框架类库在 System.Collections.Generic 命名空间中包含了一些新的泛型集合类。您可以使用这些泛型集合类来替代 System.Collections 中的集合类。
  • 您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
  • 您可以对泛型类进行约束以访问特定数据类型的方法。
  • 关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。

 四、泛型类型参数

  在泛型类型或方法定义中,类型参数是在其实例化泛型类型的一个变量时,客户端指定特定类型的占位符。泛型类无法按原样使用,因为他不是真正的类型,他更像是类型的蓝图,若要使用GenericList<T>,客户端代码必须通过指定尖括号内的类型参数来声明并实例化构造类型。此特定类的类型参数可以是编译器可识别的任何类型,可创建任意数量的构造类型实例,其中每个使用不同的类型参数。在 GenericList<T> 的每个实例中,类中出现的每个 T 在运行时均会被替换为类型参数。 通过这种替换,我们已通过使用单个类定义创建了三个单独的类型安全的有效对象。

GenericList<float> list1 = new GenericList<float>(); GenericList<ExampleClass> list2 = new GenericList<ExampleClass>(); GenericList<ExampleStruct> list3 = new GenericList<ExampleStruct>();

五、泛型类型参数约束

  1、什么是泛型类型参数约束

  约束告知编译器类型参数必须具备的功能。在没有约束的情况下,类型参数可以是任何类型,编译器只能假定Object的成员,object是任何.NET类型的最终基类。如果客户端代码尝试使用约束所不允许的类型来实例化类,则会产生编译时错误。通过使用where上下文关键字指定约束。下表列出了其中类型的约束:

  某些约束是互斥的。所有值类型必须具有可访问的无参数构造函数。struct约束包含new()约束,且new()约束不能与struct约束结合使用。unmanaged约束包含struct约束。unmanaged约束不能与struct或new()约束结合使用。

  2、使用泛型参数约束的原因

  通过约束类型参数,可以增加约束类型及其继承层次结构中的所有类型所支持的允许操作和方法调用的数量。设计泛型类或方法时,如果要对泛型成员执行除简单赋值之外的任何操作或调用System.Object不支持的任何方法,则必须对该类型参数应用约束。例如,基类约束告诉编译器,仅此类型的对象或派生自此类型的对象可用作类型参数。编译器有了此保证后,就能够允许在泛型类中调用该类型的方法。 基类约束的例子如下:

   3、各个泛型参数约束简介  

    (1)where T:类(类型参数必须是引用类型;这一点也适用于任何类、接口、委托或数组类型。)

class Program
{
    static void Main(string[] args)
    {
        #region 基类约束实例
        int stuNum = 10;
        int teacherNum = 12;
        SchoolGeneric<BeijingSchool> beijingSchool = new SchoolGeneric<BeijingSchool>();
        beijingSchool.Add(new BeijingSchool(stuNum,teacherNum, "beijing",true));
        beijingSchool.FindSchoolByNum(stuNum);
        #endregion
        Console.ReadKey();
    }
}
class School
{
    private int _studentNum;
    private int _teacherNum;
    private string _schoolName;
    public int StudentNum
    {
        get { return this._studentNum; }
        set { this._studentNum = value; }
    }
    public int TeacherNum
    {
        get { return this._teacherNum; }
        set { this._teacherNum = value; }
    }
    public string SchoolName
    {
        get{ return this._schoolName; }
        set { this._schoolName = value; }
    }
    public School(int studentNum,int teacherNum,string schoolName)
    {
        this.StudentNum = studentNum;
        this.TeacherNum = teacherNum;
        this.SchoolName = schoolName;
    }
}
class BeijingSchool:School
{
    private bool isPrivateSchool;              

    public bool IsPrivateSchool
    {
        get { return isPrivateSchool; }
        set
        {
            isPrivateSchool = value;
        }
    }
    public BeijingSchool(int stuNum, int teacherNum,string schoolName,bool isPrivateSchool) : base(stuNum, teacherNum, schoolName)
    {
        this.IsPrivateSchool = isPrivateSchool;
    }
}
class SchoolGeneric<T> where T : School
{
    T[] TList;
    int end;
    public SchoolGeneric()
    {
        this.TList = new T[1];
        end = 0;
    }
    public void Add(T school)
    {
        TList[end] = school;
        end++;
    }
    public void FindSchoolByNum(int StuNum)
    {
        Console.WriteLine("学生人数满足:{0} 的学校有:", StuNum);
        for (int i = 0; i < end; i++)
        {
            if (TList[i].StudentNum == StuNum)
            {
                Console.WriteLine(TList[i].SchoolName);
            }
         }
    }
}    

    (2)where T:结构(类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。)

class PersonGeneric<T> where T:struct
    {
        public void Print(T person)
        {
            Console.WriteLine("{0}",person);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            #region where T : struct 类型参数必须是值类型,可以指定除 Nullable<T> 以外的任何值类型
            PersonGeneric<int> personAge = new PersonGeneric<int>();
            personAge.Print(23);
            PersonGeneric<double> personName = new PersonGeneric<double>();
            personName.Print(12.21);
            #endregion
            Console.ReadKey();
        }
    }

    (3)where T:new()(类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。)

class People<T> where T : Person, System.IComparable<T>, new()
{
    // ...
}

    (4)where T:<接口名称>(类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。)

interface IMyInterface
    {

    }
    class Dictionary<TKey, TVal> where TKey : IComparable,IEnumerable where TVal : IMyInterface
    {
        public void Add(TKey key,TVal val)
        {
            Console.WriteLine("key:{0},val:{1}",key,val);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            #region where T:new()(类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。)
            SplitLine("where T:new()");

            #endregion
            Console.ReadKey();
        }
    }

    (5)where T:U(为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。也就是说T和U的参数必须一样)

class List<T>
{
    void Add<U>(List<U> items) where U : T {/*...*/}
}

六、泛型类

  通常,创建泛型类是从现有具体类开始,然后每次逐个将类型更改为类型参数,直到泛化和可用性达到最佳平衡。创建自己的泛型类时,需要考虑以下重要注意事项:

  • 要将哪些类型泛化为类型参数。

通常,可参数化的类型越多,代码就越灵活、其可重用性就越高。但过度泛化会造成其他开发人员难以阅读或理解代码。

其中一个有用的规则是,应用最大程度的约束,同时仍可处理必须处理的类型。例如,如果知道泛型类仅用于引用类型,则请应用类约束。这可防止将类意外用于值类型,并使你可在 T 上使用 as 运算符和检查 null 值。

  • 是否将泛型行为分解为基类和子类。

因为泛型类可用作基类,所以非泛型类的相同设计注意事项在此也适用。请参阅本主题后文有关从泛型基类继承的规则。

  • 实现一个泛型接口还是多个泛型接口。

class BaseNode { }
class BaseNodeGeneric<T> { }

// concrete type
class NodeConcrete<T> : BaseNode { }

//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }

//open constructed type
class NodeOpen<T> : BaseNodeGeneric<T> { } 

七、泛型方法

  泛型方法是通过类型参数声明的方法

static void Swap<T>(ref T lhs, ref T rhs)
{
    T temp;
    temp = lhs;
    lhs = rhs;
    rhs = temp;
}

public static void TestSwap()
{
    int a = 1;
    int b = 2;

    Swap<int>(ref a, ref b);
    System.Console.WriteLine(a + " " + b);
}

八、泛型接口

  为泛型集合类或表示集合中的项的泛型类定义接口通常很有用处。 为避免对值类型的装箱和取消装箱操作,泛型类的首选项使用泛型接口,例如 IComparable<T>而不是 IComparable。 .NET Framework 类库定义多个泛型接口,以将其用于 System.Collections.Generic 命名空间中的集合类。接口被指定为类型参数上的约束时,仅可使用实现接口的类型。 如下代码示例演示一个派生自 GenericList<T> 类的 SortedList<T> 类。 SortedList<T> 添加约束 where T : IComparable<T>。这可使 SortedList<T> 中的 BubbleSort 方法在列表元素上使用泛型 CompareTo 方法。 在此示例中,列表元素是一个实现 IComparable<Person> 的简单类 Person。

public class GenericList<T> : System.Collections.Generic.IEnumerable<T>
{
    protected Node head;
    protected Node current = null;

    protected class Node
    {
        public Node next;
        private T data;  

        public Node(T t)
        {
            next = null;
            data = t;
        }

        public Node Next
        {
            get { return next; }
            set { next = value; }
        }

        public T Data  //T as return type of property
        {
            get { return data; }
            set { data = value; }
        }
    }

    public GenericList()
    {
        head = null;
    }

    public void AddHead(T t)
    {
        Node n = new Node(t);
        n.Next = head;
        head = n;
    }

    public System.Collections.Generic.IEnumerator<T> GetEnumerator()
    {
        Node current = head;
        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class SortedList<T> : GenericList<T> where T : System.IComparable<T>
{

    public void BubbleSort()
    {
        if (null == head || null == head.Next)
        {
            return;
        }
        bool swapped;

        do
        {
            Node previous = null;
            Node current = head;
            swapped = false;

            while (current.next != null)
            {
                if (current.Data.CompareTo(current.next.Data) > 0)
                {
                    Node tmp = current.next;
                    current.next = current.next.next;
                    tmp.next = current;

                    if (previous == null)
                    {
                        head = tmp;
                    }
                    else
                    {
                        previous.next = tmp;
                    }
                    previous = tmp;
                    swapped = true;
                }
                else
                {
                    previous = current;
                    current = current.next;
                }
            }
        } while (swapped);
    }
}

public class Person : System.IComparable<Person>
{
    string name;
    int age;

    public Person(string s, int i)
    {
        name = s;
        age = i;
    }

    // This will cause list elements to be sorted on age values.
    public int CompareTo(Person p)
    {
        return age - p.age;
    }

    public override string ToString()
    {
        return name + ":" + age;
    }

    // Must implement Equals.
    public bool Equals(Person p)
    {
        return (this.age == p.age);
    }
}

class Program
{
    static void Main()
    {
        //Declare and instantiate a new generic SortedList class.
        //Person is the type argument.
        SortedList<Person> list = new SortedList<Person>();

        //Create name and age values to initialize Person objects.
        string[] names = new string[]
        {
            "Franscoise",
            "Bill",
            "Li",
            "Sandra",
            "Gunnar",
            "Alok",
            "Hiroyuki",
            "Maria",
            "Alessandro",
            "Raul"
        };

        int[] ages = new int[] { 45, 19, 28, 23, 18, 9, 108, 72, 30, 35 };

        //Populate the list.
        for (int x = 0; x < 10; x++)
        {
            list.AddHead(new Person(names[x], ages[x]));
        }

        //Print out unsorted list.
        foreach (Person p in list)
        {
            System.Console.WriteLine(p.ToString());
        }
        System.Console.WriteLine("Done with unsorted list");

        //Sort the list.
        list.BubbleSort();

        //Print out sorted list.
        foreach (Person p in list)
        {
            System.Console.WriteLine(p.ToString());
        }
        System.Console.WriteLine("Done with sorted list");
    }
}

九、泛型和数组

  在 C# 2.0 和更高版本中,下限为零的单维数组自动实现 IList<T>。 这可使你创建可使用相同代码循环访问数组和其他集合类型的泛型方法。 此技术的主要用处在于读取集合中的数据。 IList<T> 接口无法用于添加元素或从数组删除元素。 如果在此上下文中尝试对数组调用 IList<T> 方法(例如 RemoveAt),则会引发异常。如下代码示例演示具有 IList<T> 输入参数的单个泛型方法如何可循环访问列表和数组(此例中为整数数组)。

class Program
{
    static void Main()
    {
        int[] arr = { 0, 1, 2, 3, 4 };
        List<int> list = new List<int>();

        for (int x = 5; x < 10; x++)
        {
            list.Add(x);
        }

        ProcessItems<int>(arr);
        ProcessItems<int>(list);
    }

    static void ProcessItems<T>(IList<T> coll)
    {
        System.Console.WriteLine
            ("IsReadOnly returns {0} for this collection.",
            coll.IsReadOnly);

        foreach (T item in coll)
        {
            System.Console.Write(item.ToString() + " ");
        }
        System.Console.WriteLine();
    }
}

十、泛型委托

  委托可以定义它自己的类型参数。 引用泛型委托的代码可以指定类型参数以创建封闭式构造类型,就像实例化泛型类或调用泛型方法一样,如以下示例中所示:

public delegate void Del<T>(T item);
public static void Notify(int i) { }

Del<int> m1 = new Del<int>(Notify);

原文地址:https://www.cnblogs.com/qtiger/p/11176272.html

时间: 2024-10-04 02:49:18

C#基础知识之泛型的相关文章

黑马程序员——集合基础知识(泛型)

集合:泛型基础知识 泛型.(泛型就是类型参数化,默认的时object,虽然不用强制类型转换,这个就要你自己去写特性方法,比如compareto是string的特有方法吧,你可以写但是父类肯定调用不了) itnex t对象都是obj要使用对象的特性功能必须强.编译的时候没问题,因为都不知道你会传什么对象,你橙子也可以当作apple来传,设计的时候并不知道! 泛型作用.1.用于解决安全问题.运行时期出现的问题classcastexception转移到编译时期.2.迭代器里面的itnext()不用强转

(整理)C#基础知识_泛型的实现

本文是截取自MSDN的文章部分,方便自己查看,原文地址:https://msdn.microsoft.com/zh-cn/library/ms379564(VS.80).aspx 泛型实现 表面上,C# 泛型的语法看起来与 C++ 模板类似,但是编译器实现和支持它们的方式存在重要差异.正如您将在后文中看到的那样,这对于泛型的使用方式具有重大意义. 注 在本文中,当提到 C++ 时,指的是传统 C++,而不是带有托管扩展的 Microsoft C++. 与 C++ 模板相比,C# 泛型可以提供增强

《Java核心技术 卷1 基础知识 原书第9版》pdf

下载地址:网盘下载 内容简介 编辑 CayS.Horstmann等编著,公飞编译的<Java核心技术>(CoreJava)自第1版出版以来,一直备受广大Java程序设计人员的青睐,畅销不衰,是Java经典书籍.第8版针对JavaSE6平台进行了全面更新,囊括了Java平台标准版(JavaSE/J2SE)的全部基础知识,提供了大量完整且具有实际意义的应用实例,详细介绍了Java语言基础知识.面向对象程序设计.接口与内部类.事件监听器模型.swing图形用户界面程序设计.打包应用程序.异常处理.登

Scala学习(1)——Scala基础知识

本文要解决的问题: Spark主要是由Scala语言编写而成的,所以要真正深入了解Spark,必须要熟悉Scala,在此结合阅读<Scala编程>这本书的情况,对Scala语言做一个基本的总结. Scala的优势 (1)简洁 类型推断 函数创建的文法支持 (2)Java互操作性 可重用Java库 可重用Java工具 没有性能惩罚 Scala工作机制 编译成Java字节码 可在任何标准JVM上运行,甚至是一些不规范的JVM上 Scala编译器是Java编译器的作者写的 启动解释器 输入Scala

C++STL泛型编程基础知识讲解--------2015年2月3日

今天学习了C++STL泛型编程的基础知识,我对主要知识整理如下: STL提供三种类型的组件:容器,迭代器,算法.支持泛型程序设计标准.容器主要有两类:顺序容器和关联容器.顺序容器:vector,list,deque,string等都是一系列连续元素的集合.关联容器:set,multiset,map,multimap包含查找元素的键值.迭代器:遍历容器STL算法库:排序算法,不可变序算法,变序性算法,数值算法. /******************************************

[基础] Java目录(摘自Java核心技术·卷1 基础知识)

Java核心技术·卷1 基础知识(原书第9版) 第1章 Java程序设计概述 1.1 Java程序设计平台 1.2 Java"白皮书"的关键术语 1.2.1 简单性 1.2.2 面向对象 1.2.3 网络技能 1.2.4 健壮性 1.2.5 安全性 1.2.6 体系结构中立 1.2.7 可移植性 1.2.8 解释型 1.2.9 高性能 1.2.10 多线程 1.2.11 动态性 1.3 Java applet与Internet 1.4 Java发展简史 1.5 关于Java的常见误解

C#学习笔记(基础知识回顾)之值类型与引用类型转换(装箱和拆箱)

一:值类型和引用类型的含义参考前一篇文章 C#学习笔记(基础知识回顾)之值类型和引用类型 1.1,C#数据类型分为在栈上分配内存的值类型和在托管堆上分配内存的引用类型.如果int只不过是栈上的一个4字节的值,该如何在它上面调用方法? 二:值类型转换为引用类型--装箱 2.1CLR对值类型进行装箱时:新分配托管堆内存,将值类型的实例字段拷贝到新分配的内存中,返回托管堆中新分配对象的地址.这个地址就是一个指向对象的引用. int i = 10; Object obj = i; 三:将引用类型转换为值

AsyncTask基础知识

大家都知道,在子线程中是不能更新UI的,必须要采用异步消息处理机制.一般的做法就是在有需要的地方,使用Handler发送消息给主线程,然后在主线程中处理消息更新UI.如果好多子线程都有更新UI的需求,那么场面就有点难以控制了,大家必须在代码中手动处理Handler的消息请求.这样子问题显然会复杂很多.其实这样子的情况android已经为我们考虑到了.AsyncTask就是天生被android设计出来为我们解决这样子的问题的.其实翻看源码,就会知道AsyncTask的底层封装的仍然是android

JavaSe基础知识总结

Java基础知识总结 写代码: 1,明确需求.我要做什么? 2,分析思路.我要怎么做?1,2,3. 3,确定步骤.每一个思路部分用到哪些语句,方法,和对象. 4,代码实现.用具体的java语言代码把思路体现出来. 学习新技术的四点: 1,该技术是什么? 2,该技术有什么特点(使用注意): 3,该技术怎么使用.demo 4,该技术什么时候用?test. -------------------------------------------------------------------------