分享

.NET高级编程之泛型详解

 weijianian 2016-08-09


来源:KMonkey

链接:http://www.cnblogs.com/kmonkeywyl/p/5632677.html


泛型是具有占位符(类型参数)的类、结构、接口和方法,这些占位符是类、结构、接口和方法所存储或使用的一个或多个类型的占位符。类型参数使得设计类和方法时,不必确定一个或多个具体参数,具体参数可延迟到客户代码中声明、实现。使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。


就像我们写一个类MyList,客户代码可以这样调用:


MyList, MyList或 MyList。方便我们设计更加通用的类型,也避免了容器操作中的装箱和拆箱操作。


PS:为什么要避免装箱和拆箱? 


我们知道值类型存放在堆栈上,引用类型存放在堆上。


(1)装箱:CLR需要做额外的工作把堆栈上的值类型移动到堆上,这个操作就被称为装箱.


(2)拆箱:装箱操作的反操作,把堆中的对象复制到堆栈中,并且返回其值。 


装箱和拆箱都意味着堆和堆栈空间的一系列操作,毫无疑问,这些操作的性能代价是很大的,尤其对于堆上空间的操作,速度相对于堆栈的操作慢得多,并且可能引发垃圾回收,这些都将大规模地影响系统的性能。


1.泛型的约束


在指定一个类型参数时,可以指定类型参数必须满足的约束条件,约束是使用 where 上下文关键字指定的。


下表列出了五种类型的约束:


1.1.派生约束


基本形式 where T:base-class-name


1.1.1.常见的


public class MyClass where T :IComparable { }


1.1.2.约束放在类的实际派生之后


public class B { }public class MyClass : B where T : IComparable { }


1.1.3.可以继承一个基类和多个接口,且基类在接口前面



基本形式 where T:interface-name


public class B { }public class MyClass where T : B, IComparable, ICloneable { }


1.2.构造函数约束


基本形式: where T : new()


1.2.1.常见的


public class MyClass where T :  new() { }


1.2.2.可以将构造函数约束和派生约束组合起来,前提是构造函数约束出现在约束列表的最后


public class MyClass where T : IComparable, new() { }


1.3.值约束


基本形式:where T : struct


1.3.1.常见的


public class MyClass where T : struct { }


1.3.2.与接口约束同时使用,在最前面(不能与基类约束,构造函数约束一起使用)


public class MyClass where T : struct, IComparable { }


1.4.引用约束


基本形式:where T : class


1.4.1.常见的


public class MyClass where T : class { }


1.5.多个泛型参数


基本形式:where T : class where u:class


 public class MyClass where T : IComparable  where U : class { }


1.6.继承和泛型


public class B{ }


1.6.1. 在从泛型基类派生时,可以提供类型实参,而不是基类泛型参数


public class SubClass : B{ }


1.6.2.如果子类是泛型,而非具体的类型实参,则可以使用子类泛型参数作为泛型基类的指定类型


public class SubClass : B{ }


1.6.3.在子类重复基类的约束(在使用子类泛型参数时,必须在子类级别重复在基类级别规定的任何约束)


public class B where T : ISomeInterface { }public class SubClass2 : B where T : ISomeInterface { }


1.6.4.构造函数约束


public class B where T : new()

    {

        public T SomeMethod()

        {

            return new T();

        }

    }

    public class SubClass3 : B where T : new(){ }


泛型继承


1、泛型类继承中,父类的类型参数已被实例化,这种情况下子类不一定必须是泛型类;


2、父类的类型参数没有被实例化,但来源于子类,也就是说父类和子类都是泛型类,并且二者有相同的类型参数;


/如果这样写的话,显然会报找不到类型T,S的错误  


public class TestChild : Test< T, S> { }  

 

//正确的写法应该是  


public class TestChild : Test< string, int>{ }  

public class TestChild< T, S> : Test< T, S> { }  

public class TestChild< T, S> : Test< String, int> { }


关键字default


我们在不知道参数是值类型还是引用类型;是值类型的时候不知道他是结构还是数值的情况下定义了T,如果需要返回泛型类型的默认值则会用到这个关键字。这个时候就要用到default了。


1.T是值类型而非结构的则defaultT) 数值类型返回0,字符串返回空


2.T 是非引用类型是结构时候返回初始化为零或空的每个结构成员


3.引用类型返回NULL


class DefaultTest

    {

        public T Main()

        {

            T t = null; 

            return t;

        }

    }


如上如果类型为int则会异常,用 return default(T),就可以避免这种问题。


泛型方法


1.泛型方法是使用类型参数声明的方法


 void Method(T t){}


也可以写成


 void Method(t){}


2.泛型方法也有相应的约束


(1) public void MyMethod(X x) where X:IComparable

(2) public class MyClass where T:IComparable

    {

     public void MyMethod(X x,T t) where X:IComparable 

   }


注:实例(2)在类上已经有相应T的约束,在方法中就不能在给T加新的约束了。


3.泛型虚方法


泛型虚方法在重写的时候,一定要重新定义泛型,并且也不能重复基类的虚方法约束。


如下:


public class BaseClass

    {

        public virtual void Method(T t)

        {

            //

        }

    }

    public class Class :BaseClass

    {

        public override void Method(X x)

        {

            //

        }

    }


泛型接口


1.泛型接口实


interface IPerson

    {

        void add( T t);

    }

 class PersonManager : IPerson

    {

        #region IPerson 成员

        public void add( Person t )

        {

           //

        }

//一个接口可定义多个类型参数

interface IDictionary

{

//

}


2.多重接口可作为单个类型上的约束


class Stack where T : System.IComparable, IEnumerable


3.泛型接口继承也遵循类之间的规则


interface IMonth { }interface IJanuary     : IMonth { }  //No errorinterface IFebruary : IMonth { }  //No errorinterface IMarch    : IMonth { }    //No error//interface IApril  : IMonth {}  //Error


泛型数组


下限为零的一维数组自动实现 IList。 这使您可以创建能够使用相同代码循环访问数组和其他集合类型的泛型方法。 此技术主要对读取集合中的数据很有用。 IList 接口不能用于在数组中添加或移除元素。 如果尝试对此上下文中的数组调用 IList 方法(例如 RemoveAt),则将引发异常。


泛型委托


ps:委托是什么?


使用委托可以将方法作为参数进行传递。委托是一种特殊类型的对象,其特殊之处在于委托中包含的只是一个活多个方法的地址,而不是数据。定义方式如:public delegate void MethodDelegate();稍后的文章会有详细介绍。


http://www.cnblogs.com/kmonkeywyl/p/5626432.html这篇文章叙述了我在封装此类库的时候泛型委托就起了很大作用。


public delegate IHttpActionResult apiaction();

    public delegate IHttpActionResult apiaction_l(long args);

    public delegate IHttpActionResult apiaction_ll(long args1, long args2);

    public delegate IHttpActionResult apiaction_li(long args1, int arg2);

    public delegate IHttpActionResult apiaction_ls(long args1, string args2);

    public delegate IHttpActionResult apiaction_i(int args1);

    public delegate IHttpActionResult apiaction_ii(int args1, int args2);

    public delegate IHttpActionResult apiaction_is(int args1, string args2);

    public delegate IHttpActionResult apiaction_il(int args1, long args2);

    public delegate IHttpActionResult apiaction_si(string args1, int args2);

    public delegate IHttpActionResult apiaction_ss(string args1, string args2);

    public delegate IHttpActionResult apiaction_sl(string args1, long args2);

    public delegate IHttpActionResult apiaction_sss(string args1, string args2, string args3);

public delegate IHttpActionResult apiaction_o(treq data) where treq : class,new();


1.首先介绍两个特殊的泛型委托Action和Fun


Action只能委托必须是无返回值的方法


Fun只是委托必须有返回值的方法


针对于Action和Fun会在后边委托篇章里介绍。这边只需知道泛型委托有两个特殊的他们即可。


2.由于泛型的引入,所以一些内建(Built-in)的类、接口、委托都有了各自的泛型版本。


EventHandler也不例外,它有了自己的泛型版本:EventHandler


定义如下:


[Serializable]  

public delegate void EventHandler(object sender, TEventArgs e) where TEventArgs: EventArgs;


第二个参数的类型由EventArgs变成了TEventArgs,而TEventArgs具体是什么,则由调用方决定。假设IntEventArgs和StringEventArgs都继承于System.EventArgs,那么:


1.EventHandler指代这样一类函数:这些函数没有返回值,有两个参数,第一个参数是object类型,第二个参数是IntEventArgs类型


2.EventHandler指代这样一类函数:这些函数没有返回值,有两个参数,第一个参数是object类型,第二个参数是StringEventArgs类型

其实EventHandler和EventHandler是两个完全不同的委托,它们所指代的函数都分别有着不同的签名形式。请参见下面的示例:


class IntEventArgs : System.EventArgs  

{  

  public int IntValue { get; set; }  

  public IntEventArgs() { }  

  public IntEventArgs(int value)  

  { this.IntValue = value; }  

}  

  

class StringEventArgs : System.EventArgs  

{  

  public string StringValue { get; set; }  

  public StringEventArgs() { }  

  public StringEventArgs(string value)  

  { this.StringValue = value; }  

}  

  

class Program  

{  

  static void PrintInt(object sender, IntEventArgs e)  

  {  

    Console.WriteLine(e.IntValue);  

  }  

  

  static void PrintString(object sender, StringEventArgs e)  

  {  

    Console.WriteLine(e.StringValue);  

  }  

  

  static void Main(string[] args)  

  {  

    EventHandler ihandler = new EventHandler(PrintInt);  

    EventHandler shandler = new EventHandler(PrintString);  

  

    ihandler(null, new IntEventArgs(100));  

    shandler(null, new StringEventArgs('Hello World'));  

  }  

}


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多