where(泛型类型约束)

where关键词一个最重要的用法就是在泛型的声明、定义中做出约束。
约束又分为接口约束、基类约束、构造函数约束、函数方法的约束,我们慢慢介绍。

接口约束

顾名思义,泛型参数必须实现相应的接口才可以,看一个例子:

public interface IAccount {

        string Name {
get;
} decimal Balance {
get;
}
} public class Account : IAccount {
private string name;
public string Name {
get {
return name;
} } private decimal balance;
public decimal Balance {
get {
return balance;
}
} public Account(string name = "", decimal balance = 0) {
this.name = name;
this.balance = balance;
}
} public class MyClass<T> where T : IAccount { public MyClass() {
Console.WriteLine("In MyClass<T> Ctor");
} }

public class MyClass<T> where T : IAccount中,where关键词指定了T必须实现IAcoount的接口才可以成功构造,例如:

namespace CSharp {
class Program {
static void Main(string[] args) { MyClass<Account> mc = new MyClass<Account>();
//成功,Account实现了IAccount接口 MyClass<string> m = new MyClass<string>();
//构造失败,string没有实现IAccount接口,编译器提示错误
}
}
}

T也可以是泛型接口,例如MSDN给出的例子:

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

基类约束

类型参数必须是指定的基类或派生自指定的基类,多用于继承体系之下,看个例子:

public class Account : IAccount {
private string name;
public string Name {
get {
return name;
} } private decimal balance;
public decimal Balance {
get {
return balance;
}
} public Account(string name = "", decimal balance = 0) {
this.name = name;
this.balance = balance;
}
} public class AccountDrived : Account { public AccountDrived(string name = "", decimal balance = 0):base(name, balance) {
Console.WriteLine("In AccountDrived Ctor");
} }
//泛型参数只能是Account或者Account的派生类
public class MyClass2<T> where T : Account { public MyClass2() {
Console.WriteLine("In MyClass2<T> Ctor");
} } class Program {
static void Main(string[] args) { MyClass2<Account> a = new MyClass2<Account>();
MyClass2<AccountDrived> b = new MyClass2<AccountDrived>();
//MyClass2<string> c = new MyClass2<string>(); - error
}
}

构造函数约束

顾名思义,对类的构造函数进行了一定的约束,举个例子:

public class NoDefaultAccount : IAccount {
private string name;
public string Name {
get {
return name;
} } private decimal balance;
public decimal Balance {
get {
return balance;
}
} public NoDefaultAccount(string name) {
this.name = name;
this.balance = 0;
}
} public class Account : IAccount {
private string name;
public string Name {
get {
return name;
} } private decimal balance;
public decimal Balance {
get {
return balance;
}
}
public Account(string name = "", decimal balance = 0) {
this.name = name;
this.balance = balance;
}
} public class AccountDrived : Account {
} public class MyClass3<T> where T : class, new(){ public MyClass3(){
Console.WriteLine("In MyClass3<T> Ctor");
}
} class Program {
static void Main(string[] args) { //1.MyClass3<Account> a = new MyClass3<Account>();
MyClass3<AccountDrived> b = new MyClass3<AccountDrived>();//默认生成一个无参构造函数
//2.MyClass3<NoDefaultAccount> c = new MyClass3<NoDefaultAccount>();//必须是有默认构造函数的非抽象类
}
}

这里的重点是public class MyClass3<T> where T : class, new(),这表明参数T对应的类型必须是一个引用类型(class),new()表示具备无参构造函数。

NoDefaultAccount类内显然没有默认的构造函数,在Account中有public Account(string name = "", decimal balance = 0),给定了默认值,在AccountDerived中,由于我们没有显式的声明一个构造函数,于是C#会自动生成一个AccountDerived()。

令人疑惑的是,Account是有默认构造函数的,为何//1.MyClass3<Account> a = new MyClass3<Account>();这条语句编译器会报错呢?
尝试后发现,C#和C++不一样,当你写下Account a = new Account();这条语句的时候,编译器会优先查找是否有public Account(),如果存在那么就构造对象,否则查找public Account(value = defaultvalue)这种带默认值的构造函数,两者是不一样的,并且是可以共存的。

class Account{
//和C++不同,这并不是重定义
public Account() {
this.name = "xxxxx";
this.balance = 10;
} public Account(string name = "", decimal balance = 0) {
this.name = name;
this.balance = balance;
}
}

new()这种约束特指是否存在 Account()这样的无参默认构造函数。

函数方法的约束

这种形式就比较简单了,上述三个约束不加在泛型类中,加在函数中即可,举个例子:

 public class Algorithm {

        public static decimal Total<TAccount>(IEnumerable<TAccount> e)
where TAccount : IAccount
//这意味着调用Total函数传入的参数e必须是1.实现了IEnumerable接口的可迭代对象 2.e的可迭代元素必须是实现了IAcoount接口的
{
decimal total = 0;
foreach(TAccount element in e) {
total += element.Balance;
}
return total;
} public static void Add<T>(T lhs, T rhs) where T : class, new() {
//约束了T必须是引用类型,且必须定义了默认构造函数
T ans = new T();
}
} class Program {
static void Main(string[] args) { List<Account> accounts = new List<Account>(); accounts.Add(new Account("sixday", 100));
accounts.Add(new Account("fiveday", 50));
accounts.Add(new Account("sevenday", 70)); Console.WriteLine("The answer is {0}", Algorithm.Total<Account>(accounts)); }
}

泛型类型约束总结

最后,做一个小总结:

  • where T : struct 这表明T必须是一个值类型,像是int,decimal这样的
  • where T : class 这表明T必须是一个引用类型,像是自定义的类、接口、委托等
  • where T : new() 这表明T必须有无参构造函数,且如果有多个where约束,new()放在最后面
  • where T : [base class name] 这表明T必须是base class类获其派生类
  • where T : [interface name] 这表明T必须实现了相应的接口

更多例子可以参考MSDN

where (查询表达式)

除了用于泛型约束之外,where还常用于查询表达式,可以直接参考MSDN的例子。

出处:https://blog.csdn.net/sixdaycoder/article/details/75356055

Where关键词的用法的更多相关文章

  1. (转)C# Where关键词的用法

    where(泛型类型约束) where关键词一个最重要的用法就是在泛型的声明.定义中做出约束. 约束又分为接口约束.基类约束.构造函数约束.函数方法的约束,我们慢慢介绍. 接口约束 顾名思义,泛型参数 ...

  2. c++学习笔记2(const关键词的用法)

    定义常量指针 优势(便于类型检查,define无类型检查(目前不是很理解)) (函数参数为常量指针时,可避免函数内部不小心改变参数指针所指的地方,如有出现此类语句,编译则会报错) strcpy:复制字 ...

  3. Linq的基本用用法

    Linq 的基本用法: Sort , OrderBy, Skip,Take,Where,Compare,Join,Distinct ,InsertRange 等关键词 Select用法 var sel ...

  4. Hibernate学习-Hibernate查询语言HQL

    HQL(Hibernate Query Language)Hibernate查询语言,语法类似于SQL,可以直接使用实体类及属性. 使用HQL 可以避免使用JDBC 查询的一些弊端 不需要再编写繁复的 ...

  5. 第九章 C语言在嵌入式中的应用

    上章回顾 编码的规范和程序版式 版权管理和申明 头文件结构和作用 程序命名 程序注释和代码布局规范 assert断言函数的应用 与0或NULL值的比较 内存的分配和释放细节,避免内存泄露 常量特性 g ...

  6. var 和 dynamic在实际项目中的应用

    先回顾一下这两个关键词的用法. var是个语法糖,是在用var声明变量的那一刻就确定了其变量的类型. 因为需要在声明的时候就确定其类型,所以要求在用var声明隐式局部变量的时候必须初始化该变量. 编译 ...

  7. 【阿里云产品公测】简单日志服务SLS使用评测 + 教程

    [阿里云产品公测]简单日志服务SLS使用评测 + 教程 评测介绍 被测产品: 简单日志服务SLS 评测环境: 阿里云基础ECS x2(1核, 512M, 1M) 操作系统: CentOS 6.5 x6 ...

  8. Spring Data JPA教程, 第三部分: Custom Queries with Query Methods(翻译)

    在本人的Spring Data JPA教程的第二部分描述了如何用Spring Data JPA创建一个简单的CRUD应用,本博文将描述如何在Spring Data JPA中使用query方法创建自定义 ...

  9. SLS评测报告

    什么是SLS?  简单日志服务(Simple Log Service,简称SLS)是针对日志收集.存储.查询和分析的服务.用户只需简单地配置日志产生的位置和格式等信息,就能实时查询海量日志,并可通过S ...

随机推荐

  1. Cracking The Coding Interview 5.6

    //Write a program to swap odd and even bits in an integer with as few instructions as possible (e.g. ...

  2. python中的循环以及,continue和break的使用

    循环 目标 程序的三大流程 while 循环基本使用 break 和 continue while 循环嵌套 01. 程序的三大流程 在程序开发中,一共有三种流程方式: 顺序 —— 从上向下,顺序执行 ...

  3. day 37-8 关于mysql 的增 删 改 查 及联合列表

    1      VARCHAR : 字符串的意思 (括号内的数字代表尾数) 输入的字符串在这个范围内都行 CHAR  :字符串的意思(区别就有几位数字 必须写几位) INT   整数(位数) 输入的数字 ...

  4. uniGUI HyperServer

    uniGUI HyperServer 是一种新的服务器体系架构, 旨在高度提高 uniGUI 应用程序的可用性.稳定性和特定的可伸缩性. 这一目标是通过应用业界已知和广泛使用的技术 (如负载平衡和过程 ...

  5. python django字段类型

    <1> CharField #字符串字段, 用于较短的字符串. #CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符 ...

  6. python编码问题分析

    本文首先简要介绍编码转换的基本原理,然后针对字符串处理.文件读写的两个实例,具体分析编码问题的处理方式. 1.编码转换的基本原理 我们知道,只有在面对中文.日文等编码字符(以下均以中文字符为例)时,才 ...

  7. java学习笔记20(Arraylist复习,Collection接口方法,迭代器,增强型for循环)

    集合:集合是Java提供的一种容器,可以用来存储多个数据: 集合与数组的区别:集合的长度是可变的,数组的长度是固定的 集合中存储的数据必须是引用类型数据: ArrayList回顾: public cl ...

  8. Python 数据结构--查找

    1 顺序查找O(n) def sequential_search(a_list, item): pos = 0 found = False while pos < len(a_list) and ...

  9. 获取页面元素的css属性

    function getStyle(obj, name){    if(obj.currentStyle)    {         return obj.currentStyle[name];//兼 ...

  10. VMware虚拟机与主机共享文件夹

    VMware也可以像docker容器那样"挂载"主机上的目录给虚拟机,在虚拟机上访问共享目录就跟访问自己的目录一样方便. 1. 虚拟机(M) -> 设置(S)-> 选项 ...