C# 泛型编程

来源:互联网 发布:mud游戏编程 百度云 编辑:程序博客网 时间:2024/05/29 19:28

为什么使用泛型

为什么使用泛型?那我们先来说说不使用泛型会怎样。这里就会涉及到装箱拆箱,首先了解一下装箱,装箱拆箱
    装箱分为三个步骤:将值类型转换为引用类型
  1. 内存的分配:在堆中分配内存空间来存放复制的实际数据
  2. 完成实际数据的赋值:将值类型实例的实际数据复制到新分配的内存中。
  3. 地址返回:将堆中的独享地址返回给引用类型变量

拆箱将引用类型转换成值类型

  1. 检查实例:首先检查进行拆箱操作的引用类型是否为null,如果为null就抛出异常,如果不为null则继续检查变量是否和拆箱后的类型是同一类型。 注意:被装箱过的数据才可以拆箱
  2. 地址返回:返回已装箱变量的实际数据部分的地址

  3. 数据复制:将堆中实际数据复制到栈中

这里说一下ref,out关键字的使用
ref关键字  和c语言中的指针类似。我觉得就是相当于把实参自身传了过去,在方法里用实参进行操作
class Program    {        public static void Main()        {            string a="hello";            fun(ref a);//按引用进行传递,传递的是a的地址,在函数中通过形参str直接修改实参a;            Console.WriteLine(a);        }        public static void fun(ref string str)        {            str="world";//如果不按引用传递,改变的是引用的指向,并没有影响实参        }    }
out 关键字  在使用时out声明的形参必须赋初值,out就是讲方法中的值带回到主函数中,将值赋值给形参
public static void Main()        {            int a=10;            fun(out a);//out 关键字,必须要通过形参给实参做赋值操作            Console.WriteLine(a);        }        public static void fun(out int n)        {            //Console.WriteLine(n);            n = 100;//out关键字声明的形参必须要先初始化才能操作            Console.WriteLine(n);        }

      这是简单的变量的装箱拆箱操作,下面说一下方法

普通的方法中,定义的参数是什么类型就只能传递什么类型的参数。

首先 定义的是什么类型的数组就存什么类型

public void add(int v){      array[size++]=v; //这样就只能添加int类型的数据}

然后你会想到用一个通用的类型数据object

public void add(object v){array[size++]=v;}

使用object可以解决数据类型的问题,但是在会出现装箱、拆箱操作,这将在堆上分配和回收大量的变量,若数据量大,性能会

损失非常严重,在处理引用类型时虽然没有装箱和拆箱操作,但是将数据类型的强制转换操作,增加处理器的负担。

这里就可以使用一段代码测试一下统计一下两种操作的时间。(直接复制下来在你的机器上运行一下)

using System;using System.Diagnostics;//统计时间的命名空间namespace aa{    class Program    {        public static void Main()        {            int max = 10000000;            Vector1 v1 = new Vector1(max);            Stopwatch watch1 = new Stopwatch();            watch1.Start();            for (int i = 0; i < max; i++)            {                v1.add(i);            }            watch1.Stop();            TimeSpan span1 = watch1.Elapsed;            Console.WriteLine("{0}:{1}",span1.Seconds,span1.Milliseconds);            Vector2<int> v2 = new Vector2<int>(max);            Stopwatch watch2 = new Stopwatch();            watch2.Start();            for (int i = 0; i < max; i++)            {                v2.add(i);            }            watch2.Stop();            TimeSpan span2 = watch2.Elapsed;            Console.WriteLine("{0}:{1}",span2.Seconds,span2.Milliseconds);        }    }    public class Vector1    {        private object []arrary;        private int size;        public Vector1(int n)        {            arrary = new object[n];            size = 0;        }        public object this[int index]        {            get{ return arrary[index];}            set{ arrary[index] = value;}        }        public void add(object obj)        {            arrary[size++] = obj;        }    }    public class Vector2<T>    {        private T[]arr;        private int size;        public Vector2(int n)        {            arr = new T[n];            size = 0;        }        public T this[int index]        {            get{ return arr[index];}            set{ arr[index] = value;}        }        public void add(T x)        {            arr[size++] = x;        }    }}

使用泛型的优点

使用泛型就可以有传递多种类型的参数,减少了相同方法的重复定义。不仅可以解决类型问题还可以很大程度的优化性能

Vector<int> v1 = new Vector<int>(5);            for (int i = 0; i < 5; i++)            {                v1.add(i);            }            for (int i = 0; i < 5; i++)            {                Console.WriteLine(v1[i]);            }            Vector<double> v2 = new Vector<double>(5);            for (int i = 0; i < 5; i++)            {                v2.add(i * 1.1d);                Console.WriteLine(v2[i]);            }

1.他是类型安全的,实例化了int 类型的栈,就不能处理string类型,其他数据类型也是一样

2.无需装箱和拆箱。这个类在实例化时,按照所传入的数据类型生成本地代码,本地代码类型已经确定,所以无需装箱和拆箱。

3.无需类型转换。

4.泛型将方法实现行为与方法操作的数据类型分离,实现了代码重用。

泛型中的两个容器

  1. List<>容器

List<>容器中每个元素都对应着一个整型下标。自己创建容器时都要自己定义一个长度,但是list容器不需要我们自己传递参数

系统会自动为我们定义,并且会自动扩展容量(List中包含多个属性和方法自己在编辑器中查看就行)

2.Dictionary 容器 以键值对的形式存储数据

每个元素(值)都对应着一个下标(键)。包含两个类型 键的类型和值的类型

foreach(KeyValuePair<string,int>i in d) 使用这样的方式遍历字典中的数据

 Dictionary<string ,int> d = new Dictionary<string, int>(); //通过键来访问对应的值            for (int i = 0; i < 5; i++)            {                d.Add("hel" + i, i);            }            for (int i = 0; i < 5; i++)            {                Console.WriteLine(d["hel"+i]);            }public class Channel    {        //这个是Channel的标题        private string tittle;        //创建了一个存放News 的字典        private Dictionary<string,News>newsList;        //创建一个索引,就可以通过channel对象直接访问 channel c1["aaa"]直接访问        //不用再使用c1.newslist["aaa"];        public News this[string index]        {            get{ return newsList[index];}            set{ newsList[index] = value;}        }        public Channel(string _tittle)        {            tittle = _tittle;            newsList = new Dictionary<string, News>();        }        //调用系统的add方法将新闻添加到newslist中,然后每一个channel中都有一个词典,        public void addNews(News n)        {            newsList.Add(n.Tittle, n);        }    }

泛型方法,缩小了泛型的定义,只在方法中使用泛型

另外T只是一个表示符,可以使用其他的字母,不是非得使用T ,只是T 已经成了一种公认的习惯

泛型约束

首先要知道,泛型在没有约束的情况下可以指向任何数据类型

泛型约束

   通过where关键字给T添加约束
    1.基类约束

基类约束,指定是某个类或该类的子类,约束后,T的类型必须是该类型或者该类型的子类
public class Vector<T> where T:Person
{}

2.接口约束

给T 指定了类型必须继承接口中所有的方法

只有继承了接口了的实体类才能作为泛型参数

3.new()构造函数约束

允许开发人员实例化一个泛型类型的对象new()约束要求类型实参必须提供一个无参数的公有构造函数。使用new()约束时,

可以通过调用该无参数的构造函数来创建对象。

(1)  new()约束可以与其他约束一起使用,但必须位于约束列表的末端。

(2)  仅允许使用无参的构造函数创建对象,即使同时存在其他的构造函数也是如此

(3)  不可以同时使用new()约束和值类型约束。因为值类型都隐式的提供了一个无参公共构造函数。

4.引用类型约束

where T:class  这时T 必须是引用类型

5.值类型约束

where T :struct 这是T必须是值类型。

不能同时出现的约束类型
不能同时出现的约束基类约束值类型约束引用类型约束构造函数和值类型不能一起用
多个约束时的顺序

顺序 第一位基类型约束值类型约束引用类型约束第二位 接口约束 第三位 构造函数约束  
泛型接口

泛型接口在继承接口时就要确定接口的类型

public interface IAdd<T>    {        void add(T x);    } public class Vector1:IAdd<int>//接口继承前要指定泛型参数    {
    }

泛型接口中的泛型参数可以使用泛型类中的参数

public class Vector2<T>:IAdd<T>{     }

泛型委托

public delegate T Mydelegate<T> (T x,T y);

泛型委托的优点在于,他允许开发人员以类型安全的方式定义一种通用形式,可用于匹配任意兼容的方法


泛型方法重载参数类型一样时就会出现问题,因为方法名一样,参数列表也一样

泛型方法中的泛型参数和类中的泛型参数是没有关系的可以不一致

原创粉丝点击