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

(转)C# Where关键词的用法的更多相关文章

  1. 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. python简说(二十六)异常

    # try:# res = 1 / 0# except ZeroDivisionError as e:# print('出错啦,除数不能为0',e) # l = list()# l.append(1) ...

  2. Install jdk on Ubuntu16

    wikiHow to Install Oracle Java JDK on Ubuntu Linux This tutorial will cover the installation of 32-b ...

  3. Bootstrap3基础 img-thumbnail 给图片加一个圆角的边框

      内容 参数   OS   Windows 10 x64   browser   Firefox 65.0.2   framework     Bootstrap 3.3.7   editor    ...

  4. Django框架 (一) 虚拟环境配置及简单使用

    虚拟环境 什么是虚拟环境 对真实的python解释器的一个拷贝版本 是事实有效的,可以独立存在运行解释python代码 可以在计算机上拷贝多个虚拟环境 为什么要使用虚拟环境 保证真实环境的纯净性 框架 ...

  5. 关于BOARD_SYSTEMIMAGE_PARTITION_SIZE【转】

    本文转载自:https://blog.csdn.net/ttxgz/article/details/7542380 1. 系统需要,把需要预置在系统的所有apk放在目录 device/softwinn ...

  6. dart实例

    import 'package:flutter/material.dart'; void main() => runApp(new MyApp()); class MyApp extends S ...

  7. 取球游戏|2012年蓝桥杯B组题解析第十题-fishers

    (25')取球游戏 今盒子里有n个小球,A.B两人轮流从盒中取球,每个人都可以看到另一个人取了多少个,也可以看到盒中还剩下多少个,并且两人都很聪明,不会做出错误的判断. 我们约定: 每个人从盒子中取出 ...

  8. Print a file's last modified date in Bash

    date -r <filename> #!/usr/bin/env bash for i in /var/log/*.out; do stat -f "%Sm" -t ...

  9. 【AI】微软人工智能学习笔记(三)

    微软R服务 01|开源的R R实际上是统计学的编程语言,主要作用是对数据挖掘,统计,分析,可视化,机器学习等. 02|微软R 03| HDInsight R Spark集群存储在azure blob ...

  10. POJ 3693 Maximum repetition substring(连续重复子串)

    http://poj.org/problem?id=3693 题意:给定一个字符串,求重复次数最多的连续重复子串. 思路: 这道题确实是搞了很久,首先枚举连续子串的长度L,那么子串肯定包含了r[k], ...