泛型概述

泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。泛型类是引用类型,是堆对象,主要是引入了类型参数这个概念。

泛型定义

泛型的定义主要有以下两种:
1.在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。(这是当今较常见的定义)
2.在程序编码中一些包含参数的类。其参数可以代表类或对象等等。(人们大多把这称作模板)不论使用哪个定义,泛型的参数在真正使用泛型时都必须作出指明。
一些强类型编程语言支持泛型,其主要目的是加强类型安全及减少类转换的次数,但一些支持泛型的编程语言只能达到部分目的。

正文

假如我想输出double ,int 和 dateTime类型的类型名字和他们的值。现在需要定义方法如下

using System;
using System.Data; namespace testData
{
class Program
{
static void Main(string[] args)
{
ShowInt();
ShowDateTime(DateTime.Now);
ShowDouble(2.52);
Console.Read();
}
public static void ShowInt(int value)
{
Console.WriteLine($"typeName:{value.GetType().Name},Value:{value}");
}
public static void ShowDouble(double value)
{
Console.WriteLine($"typeName:{value.GetType().Name},Value:{value}");
}
public static void ShowDateTime(DateTime value)
{
Console.WriteLine($"typeName:{value.GetType().Name},Value:{value}");
}
}
}

这样写起来,好像比较麻烦,我们可不可以用一个方法来代替呢???

我们很多初学者都接触过这两个泛型,

List<int> list = new List<int>();
Dictionary<int, string> dic = new Dictionary<int, string>();

第一个是列表,第二个我们称为字典,那么我们可不可以自己定义一个泛型方法呢????

泛型的语法结构及定义

泛型语法: 泛型类或方法的后面 “<T>” T 是占位符,可以是任意字母,主要代表一个类型,如果是方法,参数就应当为占位符参数。 如 Show<T>(T t);方法,User<T> 类   。

泛型的特点:1.延迟声明,2使用的时候在声明

1.泛型方法的定义和使用

using System;
using System.Collections.Generic;
using System.Data; namespace testData
{
class Program
{
static void Main(string[] args)
{
Show<int>();
Show<double>(1.904);
Show<DateTime>(DateTime.Now);
Show<string>("wbc");
Console.Read();
}
public static void Show<T>(T t)
{
Console.WriteLine($"typeName:{t.GetType().Name},Value:{t}");
}
}
}

我们会发现,完全可以运行,一个方法就搞定.那么我们可不可以省略 <int> ,答案是肯定的,当没有涉及到装箱拆箱操作的时候,我们完全可以省略,如下

using System;
using System.Collections.Generic;
using System.Data; namespace testData
{
class Program
{
static void Main(string[] args)
{
Show();
Show (1.904);
Show(DateTime.Now);
Show("wbc");
User u = new User();
Show(u);
Console.Read();
}
public class User { }
public static void Show<T>(T t)
{
Console.WriteLine($"typeName:{t.GetType().Name},Value:{t}");
}
}
}

有人会说了,我使用Object 作为参数,一样可以实现,为什么不使用object呢,一切类型的父类是object ,应当可以的啊??,答案是肯定的,也是可以的,知所以不使用,是因为使用object类型的时候,会产生装箱拆箱操作,这个操作会损失精度。

2泛型类的定义和使用

泛型类的语法和泛型方法的语法很像,只是关键字是class ,如 Public Class MyGer<T>{

}

那么我们来看下泛型类的使用,看如下案列:

using System;
using System.Collections.Generic;
using System.Data; namespace testData
{
class Program
{
static void Main(string[] args)
{
Person p = new Person()
{
Name="zhangsan",Age=,Id=,Id_No="",Address="beijiang",Sex="女"
};
MyGrenric<Person> gren = new MyGrenric<Person>();
gren.Show(p);
Console.Read();
} }
/// <summary>
/// 人的父类
/// </summary>
public class BasePopleModel { public string Name { get; set; }
public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; }
}
public class Pople {
public string Name { get; set; }
public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; }
public override string ToString()
{
string result = "{"+$"name:{this.Name},age : {this.Age},sex : {this.Sex},no : {this.Id_No} "+"}";
return base.ToString();
}
}
//人的信息
public class Person : BasePopleModel { public int Id { get; set; } public override string ToString()
{
string result = "{ "+ $"name : {base.Name},age : {base.Age},sex : {base.Sex},no : {base.Id_No} , id : {this.Id}"+" }";
return result;
}
}
public class MyGrenric<T>
{
public void Show(T t) {
Console.WriteLine(t);
}
}
}

看了上面的代码,我们可以看出,泛型类里面的方法,可以是普通方法和泛型方法,当普通方法使用泛型的时候,我们可以省略泛型,上诉代码替换为:

 public void Show<T>(T t) {
Console.WriteLine(t);
}
是一样的结果。但是泛型类也有一个弊端,那就是失去了继承类的继承性质,如

BasePopleModel p = new Person()
{
Name="zhangsan",Age=28,Id=1,Id_No="110000198212193145",Address="beijiang",Sex="女"
};
MyGrenric<Person> gren = new MyGrenric<Person>();
gren.Show(p);

这个时候,我们的代码就会报错。那如何在泛型中保留类的基础性,使用如下语法就能做到:

using System;
using System.Collections.Generic;
using System.Data; namespace testData
{
class Program
{
static void Main(string[] args)
{
BasePopleModel p = new Person()
{
Name="zhangsan",Age=,Id=,Id_No="",Address="beijiang",Sex="女"
};
MyGrenric<BasePopleModel> gren = new MyGrenric<BasePopleModel>();
gren.Show(p);
Console.Read();
} }
/// <summary>
/// 人的父类
/// </summary>
public class BasePopleModel { public string Name { get; set; }
public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; }
}
public class Pople {
public string Name { get; set; }
public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; }
public override string ToString()
{
string result = "{"+$"name:{this.Name},age : {this.Age},sex : {this.Sex},no : {this.Id_No} "+"}";
return base.ToString();
}
}
//人的信息
public class Person : BasePopleModel { public int Id { get; set; } public override string ToString()
{
string result = "{ "+ $"name : {base.Name},age : {base.Age},sex : {base.Sex},no : {base.Id_No} , id : {this.Id}"+" }";
return result;
}
}
public class MyGrenric<T>
{
public void Show(T t) {
Console.WriteLine(t);
} } public interface Iccc<S> { }
}

当我们所有继承了BasePopleModel的子类,都可以使用这个泛型类。

 

3泛型接口的定义和使用

我们来看下代码,定义一个泛型接口,和定义泛型类的语法结构是一样的,他本身也具有接口的特性。代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleApp3
{
public interface IBaseImpl<T>
{
int State { get; set; } void Show(T t);
void Search<S>(S s,T t);
}
}

当我们普通类继承这个泛型接口的时候,我们会发现,继承不了,生成编译项目的时候,会提示出错误信息:如下图

那我们来试试,泛型类来继承泛型接口,看好使不好使,把我们上面创建的泛型类继承我们的接口(MyGrenric<T>:IBaseImpl<T>)如下:

 public class MyGrenric<T>:IBaseImpl<T>
{
public int State { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } public void Search<S>(S s, T t)
{
throw new NotImplementedException();
} public void Show(T t) {
Console.WriteLine(t);
}
}

我们发现是能运行的.

现在问题来了,刚才我们的实例类继承泛型接口继承不了,我们的实例类继承泛型类能继承吗??,有没有什么办法,让我们的实例类继承我们的泛型接口和泛型类呢?我们先来看看,能不能实例类继承泛型类

我们会发现还是继承不了:既然前边说了,T只是一个占位符,我们可不可以显示的写出一个类呢??

我们发现,这样是可以的,没有编译错误,那么我泛型类和泛型接口的占位符都是T,那么我们使用不同 的类可以吗??,答案是否定的,绝对不可以,一个占位符只能代表一个类型,所以我们要使用不同的类型,就需要使用不同的占位符,如:

泛型约束

前边学习了这么多自定义泛型的知识,我们基本就把整个泛型学习完了,我们之前一直都是说T,S,是泛型的一个占位符,可以是任意类型,那我们可以限定这个类型吗?答案是肯定的,继续看图片

通过上诉,我们能看出,我们限定了类型,只能是Pople 类类型。限定语法,“只有在泛型类和接口之后跟WHERE 泛型占位符 :类型”。

where T: 类型值 说明 限定规范
class 限定泛型只能是class 类型 可以有参数构造函数或无参数构造函数,不能和其他关键字一起使用
struct 限定泛型只能是struct类型 不可以和其他类型一起使用
new() 限定只能是类类型,切有无参数构造函数 必须有参数构造函数,不能和其他关键字一起使用
类类型 如果传入的是父类,则保留继承性质
值类型

这里就不过多演示上述内容,我们在这里只演示class ,代码如下

using System;
using System.Collections.Generic;
using System.Data; namespace testData
{
class Program
{
static void Main(string[] args)
{
BasePopleModel p = new Person()
{
Name="zhangsan",Age=,Id=,Id_No="",Address="beijiang",Sex="女"
};
MyGrenric<BasePopleModel> gren = new MyGrenric<BasePopleModel>();
gren.Show(p);
Console.Read();
}
}
/// <summary>
/// 人的父类
/// </summary>
public class BasePopleModel
{ public string Name { get; set; }
public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; }
}
public class Pople {
public string Name { get; set; }
public int Age { get; set; } public string Address { get; set; } public string Id_No { get; set; } public string Sex { get; set; }
public override string ToString()
{
string result = "{"+$"name:{this.Name},age : {this.Age},sex : {this.Sex},no : {this.Id_No} "+"}";
return base.ToString();
}
}
//人的信息
public class Person: BasePopleModel { public Person() { }
public int Id { get; set; } public void Show(BasePopleModel s)
{
throw new NotImplementedException();
} public override string ToString()
{
string result = "{ "+ $"name : {base.Name},age : {base.Age},sex : {base.Sex},no : {base.Id_No} , id : {this.Id}"+" }";
return result;
}
}
public class MyGrenric<T> where T : class ,new()
{ public void Show(T t) {
Console.WriteLine(t);
}
} //public interface IBaseImpl<S> where S : class
//{
// int State { get; set; } // void Show(S s);
//} }

总结:泛型类必须继承在泛型类上,如果作为普通类的父类,必须显示指定其类型,在约束的时候,泛型类不能指定值类型的约束。

泛型接口必须被泛型类和泛型接口继承,如果被普通接口和普通类继承,必须显示的指定类型,必须放在多个继承文件的最后。在约束的时候,不能使用new()。

本文只是介绍常用的泛型使用方向,很多其他方向没有详细介绍,常用就是泛型类的使用,并不怎么涉及到泛型的继承,如果一个项目涉及到泛型继承,证明这个项目也是快重构了。不过在开发过程中,泛型的约束是经常使用的。仅供参考

另付2.0以后提供的常用泛型如下:

2.0版的.NET框架类库提供了一个新的命名空间,System.Collections.Generic,其中包含了一些已经可以使用的泛型容器类和相关的接口。和早期版本的.NET框架提供的非泛型容器类相比,这些类和接口更高效且是类型安全的。在设计、实现自定义的容器类之前,请你考虑是否使用或继承所列出类中的一个。

下面的表格列出了新的泛型类和接口,旁边是对应的非泛型类和接口。在一些地方要特别注意,如List<T>和Dictionary<T>,新泛型类的行为(behavior)与它们所替换的非泛型类有些不同,也不完全兼容。更详细的内容,请参考System.Collections.Generic的文档

泛型类或接口

描述

对应的非泛型类型

Collection<T>

ICollection<T>

为泛型容器提供基类

CollectionBase

ICollection

Comparer<T>

IComparer<T>

IComparable<T>

比较两个相同泛型类型的对象是否相等、可排序。

Comparer

IComparer

IComparable

Dictionary<K, V>

IDictionary<K,V>

表示用键组织的键/值对集合。

Hashtable

IDictionary

Dictionary<K, V>.KeyCollection

表示Dictionary<K, V>中键的集合。

None.

Dictionary<K, V>.ValueCollection

表示Dictionary<K, V>中值的集合。

None.

IEnumerable<T>

IEnumerator<T>

表示可以使用foreach 迭代的集合。

IEnumerable

IEnumerator

KeyedCollection<T, U>

表示有键值的集合。

KeyedCollection

LinkedList<T>

表示双向链表。

None.

LinkedListNode<T>

表示LinkedList<T>中的节点。

None.

List<T>

IList<T>

使用大小可按需动态增加的数组实现 IList 接口

ArrayList

IList

Queue<T>

表示对象的先进先出集合。

Queue

ReadOnlyCollection<T>

为泛型只读容器提供基类。

ReadOnlyCollectionBase

SortedDictionary<K, V>

表示键/值对的集合,这些键和值按键排序并可按照键访问,实现IComparer<T>接口。

SortedList

Stack<T>

表示对象的简单的后进先出集合。

Stack

net 自定义泛型那点事的更多相关文章

  1. Net is as typeof 运行运算符详解 net 自定义泛型那点事

    Net is as typeof 运行运算符详解   概述 在了解运行运算符的前提我们需要了解什么是RTTI ,在任何一门面向对象的语言中,都有RTTI这个概念(即 运行时). RTTI(Run-Ti ...

  2. java JDK8 学习笔记——第18章 自定义泛型、枚举与注释

    第十八章 自定义泛型.枚举与注释 18.1 自定义泛型 泛型定义: (1)仅定义在方法上的泛型语法 (2)用来限制泛型可用类型的extends与super关键字(3)?类型通配字符的使用 18.1.1 ...

  3. .NET基础之自定义泛型

    在.NET中泛型使用非常频繁,在控制台应用程序中,默认的引入了System.Collection.Generics名称空间,其中就提供了我们经常使用的泛型:List<T>和Dictiona ...

  4. Swagger UI改造 增加 Token验证、显示控制器注释、自定义泛型缓存应用、

    /// <summary> /// Swagger 增加 Token 选项和控制器描述 /// </summary> public class CustomOperationF ...

  5. JAVA基础_自定义泛型

    泛型的来源 在Java中,泛型借鉴了C++的模版函数,从而引入了泛型. C++泛型 int add(int x,int y){ return x + y; } float add(float x.fl ...

  6. Day 8:方法上自定义泛型、类上、接口上、泛型的上下限

    泛型 泛型是jdk1.5使用的新特性  泛型的好处:   1. 将运行时的异常提前至了编译时    2. 避免了无谓的强制类型转换 泛型在集合中的常见应用:    ArrayList<Strin ...

  7. Day11_57_自定义泛型

    自定义泛型 package com.shige.Generic; //自定义泛型 public class CustomizeGeneric { public static void main(Str ...

  8. 匿名方法、Lambda表达和自定义泛型委托以及Func、Action系统泛型委托

    1.匿名方法的概念:一个方法没有具体的名称,而只有关键字delegate.方法参数.方法体.这种方法是匿名方法. 匿名方法的好处:将具体方法和委托直接关联在一起,如果我们基于委托只需要一个方法的时候, ...

  9. 自定义泛型_无多态_通配符无泛型数组_jdk7泛型使用

    通配符 T, K, V, E 等泛型字母为有类型, 类型参数赋予具体的值 ? 未知类型 类型参数赋予不确定值, 任意类型 只能用在 声明类型上,方法参数上, 不能用在定义泛型类上 上限 extends ...

随机推荐

  1. caffe 教程

    Caffe是一个清晰而高效的深度学习框架,本文详细介绍了caffe的优势.架构,网络定义.各层定义,Caffe的安装与配置,解读了Caffe实现的图像分类模型AlexNet,并演示了CIFAR-10在 ...

  2. 一次典型的TFS故障处理:域控失联

    问题描述 突然收到客户报告,开发人员登录TFS系统时,出现登录异常现象.即使输入了正确的账户和密码,TFS系统任然提示重新登录的页面,导致用户无法打开TFS系统. 即使登录成功,在修改代码或者修改工作 ...

  3. 源自KPI交谈的思考

    说明白一件事情不容易 前言 跟领导谈及下半年KPI的时候,问我什么打算/计划,在交谈过程中,有几个有意思的点 问题 Q: 目标是hold住服务端,那么怎么样才算hold住服务端? Q: 如何推动别人去 ...

  4. JS学习笔记5_DOM

    1.DOM节点的常用属性(所有节点都支持) nodeType:元素1,属性2,文本3 nodeName:元素标签名的大写形式 nodeValue:元素节点为null,文本节点为文本内容,属性节点为属性 ...

  5. 【推荐】Win7任务栏增强工具 7+ Taskbar Tweaker 强大的任务栏标签管理工具

    我曾经推荐过一款XP的任务栏管理工具 Taskix,这是一款在XP系统中拖动任务栏内标签的小工具. XP 32位可以下载我汉化的版本 http://www.cnblogs.com/clso/archi ...

  6. 背水一战 Windows 10 (42) - 控件(导航类): Frame 动画

    [源码下载] 背水一战 Windows 10 (42) - 控件(导航类): Frame 动画 作者:webabcd 介绍背水一战 Windows 10 之 控件(导航类) Frame 动画 示例An ...

  7. 【BZOJ1053】 反素数ant

    BZOJ1053 反素数ant 我们先考虑唯一分解定理求出约数个数: \(x=a_1^{p_1}a_2^{p_2}a_3^{p_3}...a_k^{p_k}\) 然后\(num=\Pi_{i=1}^k ...

  8. DS-博客作业03--栈和队列

    1.本周学习总结 第三章主要介绍栈和队列的基本概念,存储结构,基本运算算法设计和应用实例.从组成元素的逻辑关系来看,栈和队列都属于线性结构.栈和队列与线性表的不同之处就在于他们的相关运算具有一些特殊性 ...

  9. dubbo-admin 出现警告(不影响使用)

    <dubbo:application name="pyg-sellergoods-s" />. <dubbo:application name="pyg ...

  10. Spring Boot中使用Flyway来管理数据库版本

    flyway是一个开源的数据库迁移工具.类似于数据库的版本控制工具.flyway的数据库修改文件默认放在resource下的db.migration文件夹中,以V{version_number}__{ ...