目录

介绍

使用代码

兴趣点


介绍

可以通过C# 项目 (.csproj) 文件中的<Project><PropertyGroup><Nullable>enable</Nullable>元素启用新的可为空上下文。它在编译时为您提供完整的空状态静态分析,并承诺一劳永逸地消除每一个NullReferenceException 。你试过吗?你喜欢它?为什么不?代码质量至关重要,值得您再看一遍。本文旨在提供空值​​和泛型is以及as关键字概念的概述,以便您可以再次爱上新的C#。小事很重要。

使用代码

C#提供了两种“类型安全的宏”,很像委托作为“类型安全的函数指针”,第一种是扩展方法,第二种是值类型,两者都足够小以至于它们经常被JIT内联,因此“宏”。使用这些小的C#宏,您几乎可以创建任何您想要的东西,并使C#成为您的语言风格。要启动一个新的健壮的C#项目,您需要从一个根接口开始:

using System;
using System.Collections.Generic;
[assembly: CLSCompliant(true)]
namespace Pyramid.Kernel.Up
{/// <summary>The extension method <see langword="class"/>.</summary>public static partial class It { }/// <summary>An <see cref="object"/>.</summary>public partial interface JIt{/// <summary>An <see langword="interface"/> for external 0's.</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>partial interface J0<out JOut>{/// <summary>The 0 <typeparamref name="JOut"/> <see cref="IEnumerator{T}"/>.</summary>static readonly IEnumerator<JOut> ZTo = Z.GetEnumerator();/// <summary>The 0 <typeparamref name="JOut"/> <see cref="Array"/>.</summary>static JOut[] ZArray => Array.Empty<JOut>();/// <summary>The 0 <typeparamref name="JOut"/> <see cref="IEnumerable{T}"/>.</summary>static IEnumerable<JOut> Z => ZArray;/// <summary>The 0 <typeparamref name="JOut"/> <see cref="IReadOnlyCollection{T}"/>./// </summary>static IReadOnlyCollection<JOut> ZReadOnlyCollection => ZArray;/// <summary>The 0 <typeparamref name="JOut"/> <see cref="IReadOnlyList{T}"/>.</summary>static IReadOnlyList<JOut> ZReadOnlyList => ZArray;}}/// <summary>An autonomous <see cref="JIt"/>.</summary>/// <typeparam name="J">Its self-referencing <see cref="JIt{J}"/>.</typeparam>public partial interface JIt<J> : JIt where J : JIt<J>, new(){/// <summary>The 0 <typeparamref name="J"/>.</summary>static readonly J Z0 = new();}/// <summary>An autonomous <see cref="JIt{J}"/>.</summary>/// <typeparam name="J">Its self-referencing <see cref="It{J}"/>.</typeparam>[Serializable]public abstract partial class It<J> : object, JIt<J> where J : It<J>, new() { }
}

鉴于空值是邪恶的,我们希望我们创建的每种类型都有一个零对象,但空值必须存在以表示尚未到达的数据,这在异步编程中尤其有用。我们使用C#接口的J-前缀来提醒自己,所有接口实例成员都是virtual,就像Java一样,以及C#常量和只读字段的Z-前缀,以注意它们是final,也像Java。枚举器将被用作非常轻量级的阶梯式线程,而不是异步任务和委托,这甚至比纤程更好,因为枚举器的yield比Thread.Yield()便宜得多,大约快10,000多倍,更不用说作为自然循环的简单进度条支持了。这就是空值很重要的原因,因为您总是可以产生空值或特殊对象,例如DBNull.Value发出I/O等待信号。这是以后用的。在本文中,我们要关注缓存中的操作,它通常比内存中的操作快数百倍,而内存中的操作又比磁盘或网络I/O操作快数千或数百万倍,一个I/O层次结构就像一个金字塔,因此我们的新命名空间金字塔。

首先,我们引入空检查:

namespace Pyramid.Kernel.Up
{partial class It{/// <summary>Is (non-<see langword="null"/>)?</summary>/// <typeparam name="J">Its self-referencing <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="J"/>.</param>public static bool Is<J>(this J it) => it != null;/// <summary>Is (non-<see langword="null"/>) for the <paramref name="alias"/>?</summary>/// <typeparam name="J">Its self-referencing <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="J"/>.</param>/// <param name="alias">An alias <typeparamref name="J"/>.</param>public static bool Is<J>(this J it, [MaybeNullWhen(false), NotNullWhen(true)] out J alias) =>(alias = it).Is();}
}

它看起来很完美,但第二个方法重载不适用于Nullable<T>,因为调用出来的别名将携带相同的类型,因此仍然可以为空。当然,我们可以使用第三个方法重载Is<J>(this J? it, out J alias) where J : struct,但这会使调用Is(out var o) 不明确,现在需要显式类型说明符而不是var。这是C#需要认真改进的地方,希望能在C# 10或11或其他版本中得到解决。模棱两可是万恶之源,说得够多了。然而,既然你 无论如何都必须写作o != null? (O)o: new(),为什么不接受我们的新风格,即o.Is()? o.Be(): new()?好吧,是时候添加一些简洁的方法了:

namespace Pyramid.Kernel.Up
{partial class It{/// <summary>Be (non-<see langword="null"/>) a <typeparamref name="JOut"/>/// <see cref="IEnumerable{T}"/> to <see langword="return"/>.</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="JOut"/> <see cref="IEnumerable{T}"/>.</param>public static IEnumerable<JOut> Be<JOut>(this IEnumerable<JOut>? it) => it ?? JIt.J0<JOut>.Z;/// <summary>Be (non-<see langword="null"/>) a <typeparamref name="JOut"/>/// <see cref="IEnumerator{T}"/> to <see langword="return"/>.</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="JOut"/> <see cref="IEnumerator{T}"/>.</param>public static IEnumerator<JOut> Be<JOut>(this IEnumerator<JOut>? it) =>it ?? JIt.J0<JOut>.ZTo;/// <summary>Be (non-<see langword="null"/>) a <typeparamref name="JOut"/>/// <see cref="IReadOnlyCollection{T}"/> to <see langword="return"/>.</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="JOut"/> <see cref="IReadOnlyCollection{T}"/>./// </param>public static IReadOnlyCollection<JOut> Be<JOut>(this IReadOnlyCollection<JOut>? it) =>it ?? JIt.J0<JOut>.ZReadOnlyCollection;/// <summary>Be (non-<see langword="null"/>) a <typeparamref name="JOut"/>/// <see cref="IReadOnlyList{T}"/> to <see langword="return"/>.</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="JOut"/> <see cref="IReadOnlyList{T}"/>.</param>public static IReadOnlyList<JOut> Be<JOut>(this IReadOnlyList<JOut>? it) =>it ?? JIt.J0<JOut>.ZReadOnlyList;/// <summary>Be (non-<see langword="null"/>) a <typeparamref name="J"/> to/// <see langword="return"/>.</summary>/// <typeparam name="J">Its self-referencing <see cref="JIt{J}"/>.</typeparam>/// <param name="it">A <typeparamref name="J"/>.</param>public static J Be<J>(this J? it) where J : JIt<J>, new() => it ?? JIt<J>.Z0;/// <summary>Be (non-<see langword="null"/>) a <typeparamref name="J"/> to/// <see langword="return"/>.</summary>/// <typeparam name="J">Its self-referencing <see cref="ValueType"/>.</typeparam>/// <param name="it">A <typeparamref name="J"/>.</param>public static J Be<J>(this J? it) where J : struct => it ?? new();/// <summary>Be (non-<see langword="null"/>) a <typeparamref name="JOut"/>/// <see cref="Array"/> to <see langword="return"/>.</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="JOut"/> <see cref="Array"/>.</param>public static JOut[] Be<JOut>(this JOut[]? it) => it ?? JIt.J0<JOut>.ZArray;/// <summary>Be (non-<see langword="null"/>) a <see cref="string"/> to/// <see langword="return"/>.</summary>/// <param name="it">A <see cref="string"/>.</param>public static string Be(this string? it) => it ?? "";}
}

就是这个!每当我们调用o.Be()时,如果类型是不可变的并且线程安全为零,我们将最终得到一个共享的默认空类型对象。如果我们愿意遵循所有类型都是不可变的原则,我们甚至可以利用JIt<J>.Z0.。值得注意的是,Array.Empty<JOut>().GetEnumerator() 为您提供了一个不可变且线程安全的IEnumerator,这是我们家庭的另一个很好的方法重载。空检查很容易。我们现在继续进行类型检查:

namespace Pyramid.Kernel.Up
{partial class It{/// <summary>Is (<see langword="is"/>) <typeparamref name="JOut"/>?</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">An <see cref="object"/>.</param>public static bool Is<JOut>(this object? it) => it is JOut;/// <summary>Is (<see langword="is"/>) for the <paramref name="alias"/>?</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">An <see cref="object"/>.</param>/// <param name="alias">An alias <typeparamref name="JOut"/>.</param>public static bool Is<JOut>(this object? it, [NotNullWhen(true)] out JOut alias)where JOut : notnull => it is JOut o ? (alias = o).So(true) : (alias = default!).So(false);}
}

我们正在使用this object? 通过空检查巧妙地避免调用歧义,知道类型检查总是装箱值类型以获取它们的类型指针。类型检查并没有什么特别之处,它可以被看作是更通用的空检查形式。类型强制更有趣一点,因为as 关键字仅适用于引用类型。我们想概括一下:

namespace Pyramid.Kernel.Up
{partial class It{/// <summary>Be a <typeparamref name="JOut"/> to <see langword="return"/>.</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">An <see cref="object"/>.</param>[return: NotNullIfNotNull("it")] public static JOut Be<JOut>(this object? it) => (JOut)it!;/// <summary>Be the <paramref name="alias"/> to <see langword="return"/>.</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">An <see cref="object"/>.</param>/// <param name="alias">An alias <typeparamref name="JOut"/>.</param>[return: NotNullIfNotNull("it")]public static JOut Be<JOut>(this object? it, [NotNullIfNotNull("it")] out JOut alias) =>alias = it.Be<JOut>();}
}

我们选择Be 作为他们的方法名称来将方法组与as 关键字区分开来,该关键字“吞下”了InvalidCastException。 我们不想那样。我们总是希望在发生运行时错误时出现异常。最重要的是,我们希望包括值类型和引用类型,同时允许空值通过类型检查,因为它们应该。为什么不?为什么你可以写return null而不是return (O)null?空值必须通过所有类型检查以确保类型一致性!好吧,事实证明,如果我们让JOut可空,新的C#类型转换正是我们想要的,不再需要as关键字。要使用空检查并行类型检查,我们也非常希望有另一个重载:

namespace Pyramid.Kernel.Up
{partial class It{/// <summary>Be the <paramref name="alias"/> to <see langword="return"/>.</summary>/// <typeparam name="J">Its self-referencing <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="J"/>.</param>/// <param name="alias">An alias <typeparamref name="J"/>.</param>public static J Be<J>(this J it, out J alias) => alias = it;}
}

该方法无非是声明一个具有相同值的新变量,事实证明这在单行lambda表达式中非常方便,实际上使所有C#表达式都可以声明内联变量。最后,为了防止引用检查中的意外装箱,我们想添加另一个快捷方法:

namespace Pyramid.Kernel.Up
{partial class It{/// <summary>Is <paramref name="that"/>?</summary>/// <typeparam name="J">Its self-referencing <see cref="object"/>.</typeparam>/// <typeparam name="JThat">Its other <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="J"/>.</param>/// <param name="that">A <typeparamref name="JThat"/>.</param>public static bool Is<J, JThat>(this J it, JThat that)where J : class? where JThat : class? => ReferenceEquals(it, that);}
}

通过仅在引用检查中确保引用类型,我们可以在编译时捕获所有意外装箱。无意识的装箱不仅在垃圾收集上缓慢而繁重,而且在产生即使是最有经验的开发人员和专家也可能错过的细微错误方面也很危险。如果可以,请在编译时捕获所有错误!最后,我们包括 补充我们Is 方法的Isnt方法:

namespace Pyramid.Kernel.Up
{partial class It{/// <summary>Isn't (<see langword="null"/>)?</summary>/// <typeparam name="J">Its self-referencing <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="J"/>.</param>public static bool Isnt<J>(this J it) => it == null;/// <summary>Isn't (<see langword="null"/>) for the <paramref name="alias"/>?</summary>/// <typeparam name="J">Its self-referencing <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="J"/>.</param>/// <param name="alias">An alias <typeparamref name="J"/>.</param>public static bool Isnt<J>(this J it,[MaybeNullWhen(true), NotNullWhen(false)] out J alias) => (alias = it).Isnt();/// <summary>Isn't <paramref name="that"/>?</summary>/// <typeparam name="J">Its self-referencing <see cref="object"/>.</typeparam>/// <typeparam name="JThat">Its other <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="J"/>.</param>/// <param name="that">A <typeparamref name="JThat"/>.</param>public static bool Isnt<J, JThat>(this J it, JThat that)where J : class? where JThat : class? => !it.Is(that);/// <summary>Is (<see langword="is"/>) not <typeparamref name="JOut"/>?</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">An <see cref="object"/>.</param>public static bool Isnt<JOut>(this object? it) => !it.Is<JOut>();/// <summary>Is (<see langword="is"/>) not for the <paramref name="alias"/>?</summary>/// <typeparam name="JOut">Its output <see cref="object"/>.</typeparam>/// <param name="it">An <see cref="object"/>.</param>/// <param name="alias">An alias <typeparamref name="JOut"/>.</param>public static bool Isnt<JOut>(this object? it, [NotNullWhen(false)] out JOut alias)where JOut : notnull => !it.Is(out alias);}
}

这对于现在来说已经足够了,我们将不时重新访问一小部分代码空间,以在全局范围内添加快速和大胜利。所有这些扩展方法都非常小,自动候选JIT内联,在提供代码安全性和质量的同时成本为零。哦,是的, 我们lambda表达式中的那个小So方法是什么?好吧,作为lambda爱好者,我们绝对希望在我们的武器库中有这个快捷方式,它只不过是将表达式链接到一行中:

namespace Pyramid.Kernel.Up
{partial class It{/// <summary>So the <paramref name="next"/> to <see langword="return"/>.</summary>/// <typeparam name="J">Its self-referencing <see cref="object"/>.</typeparam>/// <typeparam name="JNext">Its next <see cref="object"/>.</typeparam>/// <param name="it">A <typeparamref name="J"/>.</param>/// <param name="next">A <typeparamref name="JNext"/>.</param>[SuppressMessage("", "IDE0060")]public static JNext So<J, JNext>(this J it, JNext next) => next;}
}

我们的短文到此结束。下一次,我们将讨论“索引”检查,除了空检查和类型检查,还有另一个关于运行时安全的提示和技巧。之后,我们可以进入更有趣的事情。

兴趣点

  1. 空数组是不可变的和线程安全的,它们的枚举器也是。尽可能利用这一点。
  2. 歧义是代码中万恶之源。我们的微小引用检查方法重载巧妙地避免了这种情况,通过使用两种泛型类型,甚至符合CLS签名要求,其中in和out修饰符不能成为方法重载的唯一区别。另外,它可以防止无意的拳击。
  3. 该[AllowNull]属性标记是现在基本上是不必要的,由于泛型和非空的整合微软最新的改进,因为不受约束的泛型类型可以在默认情况下接受一个可空类型,没有更多的需求,使之明确。如果您的Visual Studio尚未执行此操作,请确保将其升级到最新版本。

https://www.codeproject.com/Tips/5305799/How-to-Start-a-Robust-Csharp-Project-in-the-New-Nu

如何使用泛型在新的可空上下文中启动健壮的C#项目?相关推荐

  1. 【一元多项式算法】设一个一元多项式采用带头结点的单链表存储,所有结点 按照升幂方式链接。设计一个算法,求两个多项式 A 和 B 的乘积,结果多项式 C 存放在新辟的空间中。

    [一元多项式算法]设一个一元多项式采用带头结点的单链表存储,所有结点 按照升幂方式链接.设计一个算法,求两个多项式 A 和 B 的乘积,结果多项式 C 存放在新辟的空间中. #include<s ...

  2. 联想台式计算机 不启动u盘,新电脑不识别u盘启动盘?不用送修,自己就能搞定!...

    原标题:新电脑不识别u盘启动盘?不用送修,自己就能搞定! 新买的电脑想要通过u盘启动来装系统,但是一些用户却遇到难题了,以为制作好u盘启动盘就能直接开机启动了.殊不知,很多新电脑都是预装的win10电 ...

  3. combit Report新导出格式选项,combit Report添加子报表和项目

    combit Report新导出格式选项,combit Report添加子报表和项目 设计器对象:利用报表模板的新对象,如仪表.数据图形.复选框和PDF文件. 图表:只需单击几下即可直观地创建饼图.将 ...

  4. Oracle11g新特性导致空表不能导出问题

        ORACLE 11G在用EXP导出时,发现空表(没有数据或者没有用过的表)不能导出了. 查了一下资料,说是Oracle 11G中有个新特性,当表无数据时,不分配segment,以节省空间,所以 ...

  5. iPhone11系列新配色售空 暗夜绿溢价超过500元

    苹果iPhone 11系列新机于9月20日正式开售,作为苹果开设的全球唯一官方旗舰店,天猫数据显示,仅用半天时间iPhone 11系列新品就超越了去年iPhone新品首发全天的销售额. 目前苹果官网. ...

  6. C# 8 新特性 - 可空引用类型

    Nullable Reference Type. 在写C#代码的时候,你可能经常会遇到这个错误: 但如果想避免NullReferenceException的发生,确实需要做很多麻烦的工作. 可空引用类 ...

  7. 新计算机如何用u盘启动,处理新电脑怎么使用u盘装系统

    对于刚买回来的组装机来说,一般都是裸机也是说还没有安装操作系统的,需要我们自己动手安装,那么我们要怎样给新电脑安装系统呢?接下来,小编就给大家介绍一下新电脑使用u盘安装系统的方法. 现在市面上的新型电 ...

  8. 贵州省新农合业务系统容灾技术支撑服务项目

    2.1项目背景 新农合业务是中国移动贵州公司在医疗卫生行业类的一大重要信息化应用.贵州省新农合项目建设至今,在线运营业务包括68个县(区).1224个乡(镇).16508个村:建档总人数3358.44 ...

  9. 加拿大高等研究院宣布新的科研计划,Bengio、LeCun 任 AI 项目负责人...

    雷锋网 AI 科技评论消息,加拿大高等研究院 CIFAR 近日宣布了最新的科研计划布局,涵盖生命科学与健康.个人与社会.地球与空间科学.信息与物质等多个学科大类.其中,信息与物质大类下的「机器与大脑的 ...

最新文章

  1. 白洁血战Node.js并发编程 01 状态机
  2. Java对象初始化执行顺序
  3. oracle10g 如何打开,oracle10g  oem无法打开解决方法
  4. 138. 复制带随机指针的链表
  5. CentOS7中rpm,yum软件安装命令
  6. 表关系+表的详细操作+字段详细操作+特殊表--day40
  7. 计算机硬件的基本结构和工作原理
  8. airtest 不同目录下导入air文件方法
  9. Python爬虫爬取个人主页信息(拖拽验证码验证)+Linux部署
  10. MPLS(Multi-Protocol Label Switching)——多协议标签交换
  11. Internet security
  12. 英雄联盟 python 刷等级_自从学了python,我再也不缺英雄联盟的皮肤了
  13. matlab画指定角度圆弧,CAD中如何根据指定的角度画圆弧
  14. Python基本语法一
  15. Ubuntu16.04安装ftp配置
  16. 信号采样基本概念 —— 1. 冲激函数
  17. 怎么在html中创建单选按钮?
  18. 自我刷新2.5次后工资涨了1.5倍!
  19. 物联网标准和协议概述(postscapes)
  20. 超级好用的7个程序员笔记软件,你最常用的是哪个?

热门文章

  1. mysql 当前时区_如何获取MySQL的当前时区?
  2. java中为什么还要防止内存泄露_JAVA防止内存的泄漏什么意思,内存还能泄露?...
  3. 智能水位检测系统proteus_浅谈智能视觉检测系统的6大优点
  4. 计算机不属于发明保护客体,如何判断两种类型的计算机程序发明能否成为专利保护客体?...
  5. UI设计灵感|逻辑感十足的数据可视化界面设计
  6. 高品质空间合成海报,让夜色更迷人
  7. python算法的缺陷和不足_决策树基本概念及算法优缺点
  8. 乐高百变工程旋转飞椅知识点_每日一个知识点:关于磁盘的一些事儿
  9. 使用printf函数输出其ascii。_输入一个N*N的矩阵,将其转置后输出。要求:不得使用任何数组(就地逆置)。...
  10. python列表添加元组_python 列表与元组的操作简介