windows C#-泛型类型参数的约束详解(二)

未绑定的类型参数

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

不能使用 != 和 == 运算符,因为无法保证具体的类型参数能支持这些运算符。
可以在它们与 System.Object 之间来回转换,或将它们显式转换为任何接口类型。
可以将它们与 null 进行比较。 将未绑定的参数与 null 进行比较时,如果类型参数为值类型,则该比较始终返回 false。

类型参数作为约束

在具有自己类型参数的成员函数必须将该参数约束为包含类型的类型参数时,将泛型类型参数用作约束非常有用,如下例所示:

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

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

类型参数还可在泛型类定义中用作约束。 必须在尖括号中声明该类型参数以及任何其他类型参数:

//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }

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

notnull 约束

可以使用 notnull 约束指定类型参数必须是不可为 null 的值类型或不可为 null 的引用类型。 与大多数其他约束不同,如果类型参数违反 notnull 约束,编译器会生成警告而不是错误。

notnull 约束仅在可为 null 上下文中使用时才有效。 如果在过时的可为 null 上下文中添加 notnull 约束,编译器不会针对违反约束的情况生成任何警告或错误。

class 约束

可为 null 的上下文中的 class 约束指定类型参数必须是不可为 null 的引用类型。 在可为 null 上下文中,当类型参数是可为 null 的引用类型时,编译器会生成警告。

default 约束

添加可为空引用类型会使泛型类型或方法中的 T? 使用复杂化。 T? 可以与 struct 或 class 约束一起使用,但必须存在其中一项。 使用 class 约束时,T? 引用了 T 的可为空引用类型。 可在这两个约束均未应用时使用 T?。 在这种情况下,对于值类型和引用类型,T? 解读为 T?。 但是,如果 T 是 Nullable<T>的实例,则 T? 与 T 相同。 换句话说,它不会成为 T??。

由于现在可在没有 class 或 struct 约束的情况下使用 T?,因此在重写或显式接口实现中可能会出现歧义。 在这两种情况下,重写不包含约束,但从基类继承。 当基类不应用 class 或 struct 约束时,派生类需要通过某种方式在不使用任一种约束的情况下指定应用于基方法的重写。 派生方法应用 default 约束。 default 约束不阐明 class 和 struct 约束。

非托管约束

可使用 unmanaged 约束来指定类型参数必须是不可为 null 的非托管类型。 通过 unmanaged 约束,用户能编写可重用例程,从而使用可作为内存块操作的类型,如以下示例所示:

unsafe public static byte[] ToByteArray<T>(this T argument) where T : unmanaged
{
    var size = sizeof(T);
    var result = new Byte[size];
    Byte* p = (byte*)&argument;
    for (var i = 0; i < size; i++)
        result[i] = *p++;
    return result;
}

以上方法必须在 unsafe 上下文中编译,因为它并不是在已知的内置类型上使用 sizeof 运算符。 如果没有 unmanaged 约束,则 sizeof 运算符不可用。

unmanaged 约束表示 struct 约束,且不能与其结合使用。 因为 struct 约束表示 new() 约束,且 unmanaged 约束也不能与 new() 约束结合使用。

委托约束

可以使用 System.Delegate 或 System.MulticastDelegate 作为基类约束。 CLR 始终允许此约束,但 C# 语言不允许。 使用 System.Delegate 约束,用户能够以类型安全的方式编写使用委托的代码。 以下代码定义了合并两个同类型委托的扩展方法:

public static TDelegate? TypeSafeCombine<TDelegate>(this TDelegate source, TDelegate target)
    where TDelegate : System.Delegate
    => Delegate.Combine(source, target) as TDelegate;

可使用上述方法来合并相同类型的委托:

Action first = () => Console.WriteLine("this");
Action second = () => Console.WriteLine("that");

var combined = first.TypeSafeCombine(second);
combined!();

Func<bool> test = () => true;
// Combine signature ensures combined delegates must
// have the same type.
//var badCombined = first.TypeSafeCombine(test);

如果对最后一行取消注释,它将不会编译。 first 和 test 均为委托类型,但它们是不同的委托类型。

枚举约束

还可指定 System.Enum 类型作为基类约束。 CLR 始终允许此约束,但 C# 语言不允许。 使用 System.Enum 的泛型提供类型安全的编程,缓存使用 System.Enum 中静态方法的结果。 以下示例查找枚举类型的所有有效的值,然后生成将这些值映射到其字符串表示形式的字典。

public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item)!);
    return result;
}

Enum.GetValues 和 Enum.GetName 使用反射,这会对性能产生影响。 可调用 EnumNamedValues 来生成可缓存和重用的集合,而不是重复执行需要反射才能实施的调用。

如以下示例所示,可使用它来创建枚举并生成其值和名称的字典:

enum Rainbow
{
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Indigo,
    Violet
}
var map = EnumNamedValues<Rainbow>();

foreach (var pair in map)
    Console.WriteLine($"{pair.Key}:\t{pair.Value}");

类型参数实现声明的接口

某些场景要求为类型参数提供的参数实现该接口。 例如:

public interface IAdditionSubtraction<T> where T : IAdditionSubtraction<T>
{
    static abstract T operator +(T left, T right);
    static abstract T operator -(T left, T right);
}

此模式使 C# 编译器能够确定重载运算符或任何 static virtual 或 static abstract 方法的包含类型。 它提供的语法使得可以在包含类型上定义加法和减法运算符。 如果没有此约束,需要将参数和自变量声明为接口,而不是类型参数:

public interface IAdditionSubtraction<T> where T : IAdditionSubtraction<T>
{
    static abstract IAdditionSubtraction<T> operator +(
        IAdditionSubtraction<T> left,
        IAdditionSubtraction<T> right);

    static abstract IAdditionSubtraction<T> operator -(
        IAdditionSubtraction<T> left,
        IAdditionSubtraction<T> right);
}

上述语法要求实现者对这些方法使用显式接口实现。 提供额外的约束使接口能够根据类型参数来定义运算符。 实现接口的类型可以隐式实现接口方法。

Allows ref struct

allows ref struct 反约束声明相应的类型参数可以是 ref struct 类型。 该类型参数的实例必须遵循以下规则:

  • 它不能被装箱。
  • 它参与引用安全规则。
  • 不能在不允许 ref struct 类型的地方使用实例,例如 static 字段。
  • 实例可以使用 scoped 修饰符进行标记。

不会继承 allows ref struct 子句。 在以下代码中:

class SomeClass<T, S>
    where T : allows ref struct
    where S : T
{
    // etc
}

S 的参数不能是 ref struct,因为 S 没有 allows ref struct 子句。

具有 allows ref struct 子句的类型参数不能用作类型参数,除非相应的类型参数也具有 allows ref struct 子句。 下面的示例说明了此规则:

public class Allow<T> where T : allows ref struct
{

}

public class Disallow<T>
{
}

public class Example<T> where T : allows ref struct
{
    private Allow<T> fieldOne; // Allowed. T is allowed to be a ref struct

    private Disallow<T> fieldTwo; // Error. T is not allowed to be a ref struct
}

前面的示例表明,对于一个可能是 ref struct 类型的类型参数,不能将其替换为不能是 ref struct 类型的类型参数。 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/951000.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于ADAS 与关键点特征金字塔网络融合的3D LiDAR目标检测原理与算法实现

一、概述 3D LiDAR目标检测是一种在三维空间中识别和定位感兴趣目标的技术。在自动驾驶系统和先进的空间分析中&#xff0c;目标检测方法的不断演进至关重要。3D LiDAR目标检测作为一种变革性的技术&#xff0c;在环境感知方面提供了前所未有的准确性和深度信息. 在这里&…

【玩转全栈】----Django连接MySQL

阅前先赞&#xff0c;养好习惯&#xff01; 目录 1、ORM框架介绍 选择建议 2、安装mysqlclient 3、创建数据库 4、修改settings&#xff0c;连接数据库 5、对数据库进行操作 创建表 删除表 添加数据 删除数据 修改&#xff08;更新&#xff09;数据&#xff1a; 获取数据 1、OR…

基于Android的疫苗预约系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业多年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了多年的设计程序开发&#xff0c;开发过上千套设计程序&#xff0c;没有什么华丽的语言&#xff0c;只有实…

基于 Apache Commons Pool 实现的 gRPC 连接池管理类 GrpcChannelPool 性能分析与优化

基于 Apache Commons Pool 实现的 gRPC 连接池管理类 GrpcChannelPool 性能分析与优化 1. 输出关键信息的代码示例 日志记录方法 使用以下代码记录连接池的关键信息&#xff0c;帮助分析连接池的状态和性能瓶颈&#xff1a; import org.apache.commons.pool2.impl.GenericO…

矩阵碰一碰发视频的视频剪辑功能源码搭建,支持OEM

在短视频创作与传播领域&#xff0c;矩阵碰一碰发视频结合视频剪辑功能&#xff0c;为用户带来了高效且富有创意的内容产出方式。这一功能允许用户通过碰一碰 NFC 设备触发视频分享&#xff0c;并在分享前对视频进行个性化剪辑。以下将详细阐述该功能的源码搭建过程。 一、技术…

CClinkIEfield Basic转Modbus TCP网关模块连接三菱FX5U PLC

捷米特JM-CCLKIE-TCP是自主研发的一款CCLINK IE FB从站功能的通讯网关。该产品主要功能是将各种 MODBUS-TCP 设备接入到 CCLINK IE FB网络中。 捷米特JM-CCLKIE-TCP网关连接到CCLINK IE FB总线中做为从站使用&#xff0c;连接到 MODBUS-TCP 总线中做为主站或从站使用。 为了打破…

农产品智慧物流系统

本文结尾处获取源码。 本文结尾处获取源码。 本文结尾处获取源码。 一、相关技术 后端&#xff1a;Java、JavaWeb / Springboot。前端&#xff1a;Vue、HTML / CSS / Javascript 等。数据库&#xff1a;MySQL 二、相关软件&#xff08;列出的软件其一均可运行&#xff09; I…

设计模式-结构型-桥接模式

1. 什么是桥接模式&#xff1f; 桥接模式&#xff08;Bridge Pattern&#xff09; 是一种结构型设计模式&#xff0c;它旨在将抽象部分与实现部分分离&#xff0c;使它们可以独立变化。通过这种方式&#xff0c;系统可以在抽象和实现两方面进行扩展&#xff0c;而无需相互影响…

后台管理系统引导功能的实现

引导是软件中经常见到的一个功能&#xff0c;无论是在后台项目还是前台或者是移动端项目中。 那么对于引导页而言&#xff0c;它是如何实现的呢&#xff1f;通常情况下引导页是通过 聚焦 的方式&#xff0c;高亮一块视图&#xff0c;然后通过文字解释的形式来告知用户该功能的作…

现场展示deepseek VS openAI o1模型大对比

DeepSeek-V3 模型的发布在 AI 领域引起了广泛关注。作为一款拥有 6850 亿参数的混合专家&#xff08;MoE&#xff09;语言模型&#xff0c;DeepSeek-V3 在多个基准测试中表现出色&#xff0c;甚至超越了一些闭源模型。其在 Aider 代码能力排行榜上的正确率达到 48.4%&#xff0…

Golang的并发编程框架比较

# Golang的并发编程框架比较 中的并发编程 在现代软件开发中&#xff0c;处理高并发的能力愈发重要。Golang作为一门支持并发编程的编程语言&#xff0c;提供了丰富的并发编程框架和工具&#xff0c;使得开发者能够更轻松地处理并发任务。本文将介绍Golang中几种常用的并发编程…

SSL,TLS协议分析

写在前面 工作中总是会接触到https协议&#xff0c;也知道其使用了ssl&#xff0c;tls协议。但对其细节并不是十分的清楚。所以&#xff0c;就希望通过这篇文章让自己和读者朋友们都能对这方面知识有更清晰的理解。 1&#xff1a;tls/ssl协议的工作原理 1.1&#xff1a;设计的…

网络安全-XSS跨站脚本攻击(基础篇)

漏洞扫描的原理 1.跨站脚本攻击介绍 xss跨站脚本攻击&#xff1a; xSS 全称&#xff08;Cross site Scripting &#xff09;跨站脚本攻击&#xff0c;是最常见的Web应用程序安全漏洞之一&#xff0c;位于OWASP top 10 2013/2017年度分别为第三名和第七名&#xff0c;XSS是指攻…

SpringBoot之核心配置

学习目标&#xff1a; 1.熟悉Spring Boot全局配置文件的使用 2.掌握Spring Boot配置文件属性值注入 3.熟悉Spring Boot自定义配置 4.掌握Profile多环境配置 5.了解随机值设置以及参数间引用 1.全局配置文件 Spring Boot使用 application.properties 或者application.yaml 的文…

【Word_笔记】Word的修订模式内容改为颜色标记

需求如下&#xff1a;请把修改后的部分直接在原文标出来&#xff0c;不要采用修订模式 步骤1&#xff1a;打开需要转换的word后&#xff0c;同时按住alt和F11 进入&#xff08;Microsoft Visual Basic for Appliations&#xff09; 步骤2&#xff1a;插入 ---- 模块 步骤3&…

【C++】字符数|组与字符串的深度解析

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;一、字符数组的基本概念1. 什么是字符数组&#xff1f;2. C语言风格字符串的特点 &#x1f4af;二、字符数组的初始化1. 字符串直接赋值2. 按字符逐个赋值数据对比示例 &am…

计算机网络——网络层—IP数据报与分片

一、IP 数据报的格式 • 一个 IP 数据报由首部和数据两部分组成。 • 首部的前一部分是固定长度&#xff0c;共 20 字节&#xff0c;是所有 IP 数据报必须具有的。 • 在首部的固定部分的后面是一些可选字段&#xff0c;其长度是可变的。 IP 数据报首部的固定部分中的各字段 版…

【Python学习(八)——异常处理】

Python学习&#xff08;八&#xff09;——异常处理 本文介绍了异常处理的知识&#xff0c;仅作为本人学习时记录&#xff0c;感兴趣的初学者可以一起看看&#xff0c;欢迎评论区讨论&#xff0c;一起加油鸭~~~ 心中默念&#xff1a;Python 简单好学&#xff01;&#xff01;&…

Python 爬虫验证码识别

在我们进行爬虫的过程中&#xff0c;经常会碰到有些网站会时不时弹出来验证码识别。我们该如何解决呢&#xff1f;这里分享 2 种我尝试过的方法。 0.验证码示例 1.OpenCV pytesseract 使用 Python 中的 OpenCV 库进行图像预处理&#xff08;边缘保留滤波、灰度化、二值化、…

[离线数仓] 总结二、Hive数仓分层开发

接 [离线数仓] 总结一、数据采集 5.8 数仓开发之ODS层 ODS层的设计要点如下: (1)ODS层的表结构设计依托于从业务系统同步过来的数据结构。 (2)ODS层要保存全部历史数据,故其压缩格式应选择压缩比率,较高的,此处选择gzip。 CompressedStorage - Apache Hive - Apac…