基础拾遗------泛型详解

2023-05-16


 
  • 基础拾遗:
  • 前言:  
  • 1.泛型的约束
  • 2.泛型继承
  • 3.关键字default
  • 4.泛型方法
  • 5.泛型接口
  • 6.泛型数组
  • 7.泛型委托

基础拾遗:

基础拾遗------特性详解

基础拾遗------webservice详解

基础拾遗------redis详解

基础拾遗------反射详解

基础拾遗------委托详解

基础拾遗------接口详解

基础拾遗------泛型详解

前言:  

  泛型是具有占位符(类型参数)的类、结构、接口和方法,这些占位符是类、结构、接口和方法所存储或使用的一个或多个类型的占位符。类型参数使得设计类和方法时,不必确定一个或多个具体参数,具体参数可延迟到客户代码中声明、实现。使用泛型类型可以最大限度地重用代码、保护类型的安全以及提高性能。就像我们写一个类MyList<T>,客户代码可以这样调用:MyList<int>, MyList<string>或 MyList<MyClass>。方便我们设计更加通用的类型,也避免了容器操作中的装箱和拆箱操作

   PS:为什么要避免装箱和拆箱?                                                                                                                       
 我们知道值类型存放在堆栈上,引用类型存放在堆 上。
(1)装箱:CLR需要做额外的工作把堆栈上的值类型移动到堆上,这个操作就被称为装箱.
(2)拆箱:装箱操作的反操作,把堆中的对象复制到堆栈中,并且返回其值。
装箱和拆箱都意味着堆和堆栈空间的一系列操作,毫无疑问,这些操作的性能代价是很大的,尤其对于堆上空间的操作,速度相对于堆栈的操作慢得多,并且可能引发垃圾回收,这些都将大规模地影响系统的性能

1.泛型的约束

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

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

约束 说明

     T:struct

类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。

     T:class

类型参数必须是引用类型,包括任何类、接口、委托或数组类型

     T:new()

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

     T:<基类名>

类型参数必须是指定的基类或派生自指定的基类。

     T:<接口名称>

类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。

     T:U

为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束.

1.1.派生约束

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

1.1.1.常见的


public class MyClass<T> where T :IComparable { }  

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


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

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

基本形式 where T:interface-name


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

1.2.构造函数约束

基本形式: where T : new()

1.2.1.常见的


public class MyClass<T> where T :  new() { }  

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


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

1.3.值约束

基本形式:where T : struct

1.3.1.常见的


public class MyClass<T> where T : struct { }  

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


public class MyClass<T> where T : struct, IComparable { }  

1.4.引用约束

基本形式:where T : class

1.4.1.常见的


public class MyClass<T> where T : class { }  

1.5.多个泛型参数

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


 public class MyClass<T, U> where T : IComparable  where U : class { }  

1.6.继承和泛型


public class B<T>{ }  

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


public class SubClass : B<int>{ }  

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


public class SubClass<R> : B<R>{ }  

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


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

1.6.4.构造函数约束


   public class B<T> where T : new()
    {
        public T SomeMethod()
        {
            return new T();
        }
    }
    public class SubClass3<T> : B<T> where T : new(){ }  

2.泛型继承

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> { }   
View Code

3.关键字default

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

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

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

3.引用类型返回NULL


class DefaultTest<T>
    {
        public T Main()
        {
            T t = null; 
            return t;
        }
    }  
View Code

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

4.泛型方法

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


 void Method<T>(T t){}  

也可以写成


 void Method<T>(t){}  

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


(1) public void MyMethod<X>(X x) where X:IComparable<X> (2) public class MyClass<T> where T:IComparable<T>     {      public void MyMethod<X>(X x,T t) where X:IComparable<X> 

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

4.3.泛型虚方法

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

如下:


public class BaseClass
    {
        public virtual void Method<T>(T t)
        {
            //
        }
    }
    public class Class :BaseClass
    {
        public override void Method<X>(X x)
        {
            //
        }
    }  
View Code

5.泛型接口

5.1.泛型接口实


interface IPerson<T>
    {
        void add( T t);
    }
 class PersonManager : IPerson<Person>
    {
        #region IPerson<Person> 成员
        public void add( Person t )
        {
           //
        }   
View Code

//一个接口可定义多个类型参数
interface IDictionary<K, V>
{
//
}  
View Code

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


class Stack<T> where T : System.IComparable<T>, IEnumerable<T>  

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


interface IMonth<T> { }

interface IJanuary     : IMonth<int> { }  //No error
interface IFebruary<T> : IMonth<int> { }  //No error
interface IMarch<T>    : IMonth<T> { }    //No error
//interface IApril<T>  : IMonth<T, U> {}  //Error  
View Code

6.泛型数组

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

7.泛型委托

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>(treq data) where treq : class,new();  
View Code

7.1.首先介绍两个特殊的泛型委托Action<T>和Fun<TResult>

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

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

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

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

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

定义如下:


[Serializable]  
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs: EventArgs;  
View Code

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

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

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

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


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<IntEventArgs> ihandler = new EventHandler<IntEventArgs>(PrintInt);  
    EventHandler<StringEventArgs> shandler = new EventHandler<StringEventArgs>(PrintString);  
  
    ihandler(null, new IntEventArgs(100));  
    shandler(null, new StringEventArgs("Hello World"));  
  }  
}    
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基础拾遗------泛型详解 的相关文章

  • c#多维数组的建立及操作 总结

    1C 如何定义和使用多维数组 不建议使用ArrayList xff0c 当数组里的元素是值类型在操作的时候会出现大量的装箱与拆箱步骤性能会损失许多 xff0c 而是应该用什么类型的元素创建什么类型的数组 xff0c 除非你的元素有交叉或不确
  • 在TreeView查找某一节点

    在TreeView 查找某一节点 xff0c 通常有两种方法 xff0c 一种是递归的 xff0c 一种不是递归 xff0c 但都是深度优先算法 其中 xff0c 非递归方法效率高些 xff0c 而递归算法要简洁一些 第一种 xff0c 递
  • C#的数据类型总结

    C 的数据类型可以分为3 类 数值类型 引用类型 指针类型 指针类型仅在不安全代码中使用 一 值类型 值类型包括简单值类型和复合型类型 简单值类型可以再细分为整数类型 字符类型 实数类型和布尔类型 xff1b 而复合类型则是简单类型的复合
  • C#中OpenFileDialog获取文件名和文件路径的常用方法.

    System IO Path GetFullPath openFileDialog1 FileName 绝对路径 System IO Path GetExtension openFileDialog1 FileName 文件扩展名 Syst
  • 操作XML 报错:根级别上的数据无效 和 给定编码中的字符无效 解决办法

    根级别上的数据无效 解决如下 private void button1 Click object sender EventArgs e try XmlDocument doc 61 new XmlDocument string file 6
  • DataGridRow的创建

    用原始datagridview的列名赋值的时候找不到列名 xff0c 用索引就可以 xff0c 不知道是怎么回事 DataGridViewRow dr 61 new DataGridViewRow dr CreateCells this d
  • 常用方法和属性列表

    BitConvert islittle 判断大小端 Array reverse 反排列数组
  • System.Windows.Forms.Timer与System.Timers.Timer的区别

    NET Framework里面提供了三种Timer xff1a System Windows Forms Timer System Timers Timer System Threading Timer VS NET 2005默认只有一个T
  • c++中scanf和printf

    xfeff xfeff scanf函数一般格式是 xff1a scanf 格式控制 输出表列 printf函数的一般格式是 printf 格式控制 输出表列 例3 4 用scanf和printf函数进行输入和输出 include lt io
  • win10无法上网,连网显示黄色三角形探号

    1 打开网络属性 2 打开无线电源开关 3 重启电脑 4 使用网络疑难解答 5 重启DHCP
  • 计算机程序的思维逻辑 (12) - 函数调用的基本原理

    xfeff xfeff 栈 上节我们介绍了函数的基本概念 xff0c 在最后我们提到了一个系统异常java lang StackOverflowError xff0c 栈溢出错误 xff0c 要理解这个错误 xff0c 我们需要理解函数调用
  • c语言中函数调用的原理

    xfeff xfeff 一 函数参数传递机制的基本理论 函数参数传递机制问题在本质上是调用函数 xff08 过程 xff09 和被调用函数 xff08 过程 xff09 在调用发生时进行通信的方法问题 基本的参数传递机制有两种 xff1a
  • 栈中函数调用原理_详解

    xfeff xfeff 函数调用是程序设计中的重要环节 xff0c 本文就函数调用的过程进行分析 一 eip ebp esp介绍 EIP xff0c EBP xff0c ESP都是系统的寄存器 xff0c 里面存储的是些地址 xff0c 我
  • C++中如何定义动态数组

    xfeff xfeff 首先 xff1a 为什么需要动态定义数组呢 xff1f 这是因为 xff0c 很多情况下 xff0c 在预编译过程阶段 xff0c 数组的长度是不能预先知道的 xff0c 必须在程序运行时动态的给出 但是问题是 xf
  • 数组和指针、数组指针和指针数组

    xfeff xfeff 数组和指针 数组指针和指针数组 函数指针和指针函数 数组标识符的意义 静态和动态创建的数组的本质区别 标识符类型判断方法 html view plain copy print include lt iostream
  • 函数中的指针分配的内存怎么释放

    xfeff xfeff 被调用函数里动态分配的内存要不要手动释放 20 我想手动释放来着 xff0c 但是指针是在被调用函数里声明的 xff0c 在调用函数里释放不了 我的被调用函数返回的就是这个指针 xff0c 我也不能在被调用函数里释放
  • duilib各种布局的作用,相对布局与绝对布局的的意义与用法

    duilib各种布局的作用 xff0c 相对布局与绝对布局的的意义与用法 原文 http blog csdn net zhuhongshu article details 38531447 主题 Duilib 转载请说明原出处 xff0c
  • wpf中:xaml中的命名空间的引入方法

    xfeff xfeff wpf中 xff1a xaml中的命名空间的引入 本文章已收录于 xff1a 在xaml中如有要使用c 数据类型 xff0c 那么需要引入c 的命名空间 xff0c 如需要使用String类 xff0c 则需要引入S
  • WPF Application启动界面设置——

    xfeff xfeff 本文章已收录于 xff1a 设置WPF从不同界面启动可以通过设置 StartupUri 属性完成 http blog csdn net bamboo slit article details 7164848 设置St
  • 【2.路由器接口配置】

    1 ip route 0 0 0 0 43 0 0 0 0 43 接口的IP地址 2 ip address ip地址 43 子网掩码 pos framing sdh sonet pos flag s1s0 2 0 3 4 5

随机推荐

  • Linux(debian7)操作基础(七)之LightDM详解及使用

    LightDM自启动默认登录 使用命令sudo vim etc lightdm lightdm conf 将 autologin user 61 更改为 autologin user 61 登录用户名 xff0c 保存退出 更改主机名 xf
  • c#读取指定路径的配置文件

    xfeff xfeff ExeConfigurationFileMap map 61 new ExeConfigurationFileMap map ExeConfigFilename 61 64 34 F App1 config 34 C
  • C#创建com组件

    xfeff xfeff 本文详细阐述如何用C 创建COM组件 xff0c 并能用VC6 0等调用 并附有完整测试通过的代码 废话不多说 xff0c 下面开始介绍 xff1a 开发工具 xff1a VS2010 VS2010命令提示符在 xf
  • Windows下静态链接库的使用

    xfeff xfeff 静态链接是指将一个或多个静态链接库 xff08 lib文件 xff09 在Link时期和调用该库的程序一起形成exe文件 网上关于静态链接库的理论叙述多且详尽 xff0c 我就不再造轮子了 xff0c 此处仅说明一下
  • 异步消息的传递-回调机制

    xfeff xfeff 软件模块之间总是存在着一定的接口 xff0c 从调用方式上 xff0c 可以把他们分为三类 xff1a 同步调用 回调和异步调用 同步调用是一种阻塞式调用 xff0c 调用方要等待对方执行完毕才返回 xff0c 它是
  • C++回调机制的几种实现方式

    xfeff xfeff Callback Callback的本质是设置一个函数指针进去 xff0c 然后在需要触发某个事件时调用该方法 比如Windows的窗口消息处理函数就是这种类型 比如下面的示例代码 xff0c 我们在Download
  • C/C++函数调用的几种方式总结

    xfeff xfeff 本篇文章主要是对C C 43 43 函数调用的几种方式进行了详细的总结介绍 xff0c 需要的朋友可以过来参考下 xff0c 希望对大家有所帮助 调用函数时 xff0c 计算机常用栈来存储传递给函数的参数 栈是一种先
  • Aspose.Cells.dll操作exel

    xfeff xfeff 1 创建execl xff08 不需要服务器或者客户端安装office xff09 public void DCExexl DataTable dt Workbook wb 61 new Workbook Works
  • 使用Aspose.Cells组件生成Excel文件实例

    xfeff xfeff 这篇文章主要介绍了使用Aspose Cells组件生成Excel文件的方法 xff0c 大家参考使用吧 生成带表头的Excel文件 xff0c 格式如下显示 当然更复杂的一些也可以通过 合并单元格的方法 public
  • VS2010中添加lib库引用

    xfeff xfeff 一 VS2010中添加lib库引用 1 菜单 项目 gt 属性 gt 配置属性 gt 链接器 gt 输入 附加依赖项 加入库名 如 my API lib 或是在cpp源文件中用代码 pragma comment li
  • C++静态库与动态库

    xfeff xfeff 这次分享的 宗旨 是 让大家学会创建与使用静态库 动态库 xff0c 知道静态库与动态库的区别 xff0c 知道使用的时候如何选择 这里不深入介绍静态库 动态库的底层格式 xff0c 内存布局等 xff0c 有兴趣的
  • 在mysql设置root用户密码的方法

    在mysql设置root用户密码的方法
  • C++中的“error:LNK2005 已经在*.obj中定义”异常

    xfeff xfeff C 43 43 中的 error LNK2005 已经在 obj中定义 异常问题 异常现象如下 xff1a C 43 43 中的 error LNK2005 已经在 obj中定义 异常问题 图1 error LNK2
  • 指令和数据都用二进制代码存放在内存中,从时空观角度回答CPU如何区分读出的代码是指令还是数据

    xfeff xfeff 指令用来确定 做什么 和 怎样做 xff0c 数据是 做 的时候需要原始数 计算机可以从时间和空间两方面来区分指令和数据 xff0c 在时间上 xff0c 取指周期从内存中取出的是指令 xff0c 而执行周期从内存取
  • C++类的内存地址存放问题

    了解C 43 43 类地址的存放和分配等问题 xff0c 能帮助我们更深入 更清晰了解类的组成及其使用 自己目前不是很清楚 xff0c 先收集一些网上资料 xff0c 而后再慢慢补充增加的了解 网络收集之 关于结构体和C 43 43 类的内
  • 介绍了如何取成员函数的地址以及调用该地址

    xfeff xfeff 摘要 xff1a 介绍了如何取成员函数的地址以及调用该地址 关键字 xff1a C 43 43 成员函数 this指针 调用约定 一 成员函数指针的用法 在C 43 43 中 xff0c 成员函数的指针是个比较特殊的
  • 类中内容在内存中到底是如何分配的呢?

    xfeff xfeff 分类 xff1a 一个类 xff0c 有成员变量 xff1a 静态与非静态之分 xff1b 而成员函数有三种 xff1a 静态的 非静态的 虚的 那么这些个东西在内存中到底是如何分配的呢 xff1f 以一个例子来说明
  • 什么变量在堆内存里存放,什么变量在栈内存里存放

    堆和栈的区别 stack and heap 一般认为在c中分为这几个存储区 1栈 有编译器自动分配释放 2堆 一般由程序员分配释放 xff0c 若程序员不释放 xff0c 程序结束时可能由OS回收 3全局区 xff08 静态区 xff09
  • C# 泛型(Generic)

    xfeff xfeff 泛型 xff08 Generic xff09 允许您延迟编写类或方法中的编程元素的数据类型的规范 xff0c 直到实际在程序中使用它的时候 换句话说 xff0c 泛型允许您编写一个可以与任何数据类型一起工作的类或方法
  • 基础拾遗------泛型详解

    xfeff xfeff 基础拾遗 xff1a 前言 xff1a 1 泛型的约束2 泛型继承3 关键字default4 泛型方法5 泛型接口6 泛型数组7 泛型委托 基础拾遗 xff1a 基础拾遗 特性详解 基础拾遗 webservice详解