当前位置: 首页 > news >正文

C# 中where类型约束

转载自MSDN


类型参数的约束(C# 编程指南)

Visual Studio 2005
其他版本

在定义泛型类时,可以对客户端代码能够在实例化类时用于类型参数的类型种类施加限制。如果客户端代码尝试使用某个约束所不允许的类型来实例化类,则会产生编译时错误。这些限制称为约束。约束是使用 where 上下文关键字指定的。下表列出了六种类型的约束:

约束 说明

T:结构

类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。有关更多信息,请参见使用可空类型(C# 编程指南)。

T:类

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

T:new()

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

T:<基类名>

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

T:<接口名称>

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

T:U

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

使用约束的原因

如果要检查泛型列表中的某个项以确定它是否有效,或者将它与其他某个项进行比较,则编译器必须在一定程度上保证它需要调用的运算符或方法将受到客户端代码可能指定的任何类型参数的支持。这种保证是通过对泛型类定义应用一个或多个约束获得的。例如,基类约束告诉编译器:仅此类型的对象或从此类型派生的对象才可用作类型参数。一旦编译器有了这个保证,它就能够允许在泛型类中调用该类型的方法。约束是使用上下文关键字 where 应用的。下面的代码示例演示可通过应用基类约束添加到 GenericList<T> 类(在泛型介绍(C# 编程指南)中)的功能。



public class Employee
{
    private string name;
    private int id;


    public Employee(string s, int i)
    {
        name = s;
        id = i;
    }


    public string Name
    {
        get { return name; }
        set { name = value; }
    }


    public int ID
    {
        get { return id; }
        set { id = value; }
    }
}


public class GenericList<T> where T : Employee
{
    private class Node
    {
        private Node next;
        private T data;


        public Node(T t)
        {
            next = null;
            data = t;
        }


        public Node Next
        {
            get { return next; }
            set { next = value; }
        }


        public T Data
        {
            get { return data; }
            set { data = value; }
        }
    }


    private Node head;


    public GenericList() //constructor
    {
        head = null;
    }


    public void AddHead(T t)
    {
        Node n = new Node(t);
        n.Next = head;
        head = n;
    }


    public IEnumerator<T> GetEnumerator()
    {
        Node current = head;


        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }


    public T FindFirstOccurrence(string s)
    {
        Node current = head;
        T t = null;


        while (current != null)
        {
            //The constraint enables access to the Name property.
            if (current.Data.Name == s)
            {
                t = current.Data;
                break;
            }
            else
            {
                current = current.Next;
            }
        }
        return t;
    }
}


通过约束类型参数,可以增加约束类型及其继承层次结构中的所有类型所支持的允许操作和方法调用的数量。因此,在设计泛型类或方法时,如果要对泛型成员执行除简单赋值之外的任何操作或调用 System.Object 不支持的任何方法,您将需要对该类型参数应用约束。

在应用 where T : class 约束时,建议不要对类型参数使用 == 和 != 运算符,因为这些运算符仅测试引用同一性而不测试值相等性。即使在用作参数的类型中重载这些运算符也是如此。下面的代码说明了这一点;即使 String 类重载 == 运算符,输出也为 false。


C#

public static void OpTest<T>(T s, T t) where T : class
{
    System.Console.WriteLine(s == t);
}
static void Main()
{
    string s1 = "foo";
    System.Text.StringBuilder sb = new System.Text.StringBuilder("foo");
    string s2 = sb.ToString();
    OpTest<string>(s1, s2);
}


这种情况的原因在于,编译器在编译时仅知道 T 是引用类型,因此必须使用对所有引用类型都有效的默认运算符。如果需要测试值相等性,建议的方法是同时应用 where T : IComparable<T> 约束,并在将用于构造泛型类的任何类中实现该接口。

未绑定的类型参数

没有约束的类型参数(如公共类 SampleClass<T>{} 中的 T)称为未绑定的类型参数。未绑定的类型参数具有以下规则:

  • 不能使用 != 和 == 运算符,因为无法保证具体类型参数能支持这些运算符。

  • 可以在它们与 System.Object 之间来回转换,或将它们显式转换为任何接口类型。

  • 可以将它们与 null 进行比较。将未绑定的参数与 null 进行比较时,如果类型参数为值类型,则该比较将始终返回 false。

裸类型约束

用作约束的泛型类型参数称为裸类型约束。当具有自己的类型参数的成员函数需要将该参数约束为包含类型的类型参数时,裸类型约束很有用,如下面的示例所示:


C#

class List<T>
{
    void Add<U>(List<U> items) where U : T {/*...*/}
}




在上面的示例中,T 在 Add 方法的上下文中是一个裸类型约束,而在 List 类的上下文中是一个未绑定的类型参数。

裸类型约束还可以在泛型类定义中使用。注意,还必须已经和其他任何类型参数一起在尖括号中声明了裸类型约束:

C#
//naked type constraint
public class SampleClass<T, U, V> where T : V { }



泛型类的裸类型约束的作用非常有限,因为编译器除了假设某个裸类型约束派生自 System.Object 以外,不会做其他任何假设。在希望强制两个类型参数之间的继承关系的情况下,可对泛型类使用裸类型约束。


相关文章:

  • oracle函数之:translate
  • json 序列化与反序列化类
  • 关于RenderTarget的注意事项
  • HTTP listener 的封装---HttpListenerEx
  • C#把外部文件拖入PictureBox中
  • C# 中 字符串和byte数组的转换
  • 《重构——改善既有代码的设计》
  • 二分查找法 C#实现
  • 清华申请退学博士作品:完全用Linux工作
  • C# 压缩和解压缩(Gzip和Zip的方式)
  • Jsp联接Access数据库文件的方法
  • 中国教育“绞肉机”
  • 在指定范围内 生成随机序列算法(可用于洗牌算法)
  • 不用‘/’ 实现除法运算
  • HTML5中的 querySelector
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 【剑指offer】让抽象问题具体化
  • 5分钟即可掌握的前端高效利器:JavaScript 策略模式
  • ES6 ...操作符
  • Python十分钟制作属于你自己的个性logo
  • redis学习笔记(三):列表、集合、有序集合
  • Vue实战(四)登录/注册页的实现
  • 半理解系列--Promise的进化史
  • 关于for循环的简单归纳
  • 诡异!React stopPropagation失灵
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 延迟脚本的方式
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • ​软考-高级-信息系统项目管理师教程 第四版【第23章-组织通用管理-思维导图】​
  • #NOIP 2014#Day.2 T3 解方程
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (TOJ2804)Even? Odd?
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (一)Kafka 安全之使用 SASL 进行身份验证 —— JAAS 配置、SASL 配置
  • (转)Android中使用ormlite实现持久化(一)--HelloOrmLite
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .axf 转化 .bin文件 的方法
  • .gitignore文件---让git自动忽略指定文件
  • .NET Core 和 .NET Framework 中的 MEF2
  • .net framework 4.0中如何 输出 form 的name属性。
  • .Net 转战 Android 4.4 日常笔记(4)--按钮事件和国际化
  • .NET/ASP.NETMVC 大型站点架构设计—迁移Model元数据设置项(自定义元数据提供程序)...
  • .NET6 命令行启动及发布单个Exe文件
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接
  • .netcore如何运行环境安装到Linux服务器
  • .NET和.COM和.CN域名区别
  • .NET中GET与SET的用法
  • /etc/fstab和/etc/mtab的区别
  • /proc/interrupts 和 /proc/stat 查看中断的情况