【问题标题】:Non-nullable reference types (yet again)不可为空的引用类型(再次)
【发布时间】:2011-12-04 12:25:19
【问题描述】:

关于.NET 中不可为空的引用类型的支持存在许多问题。最大的希望是代码合约,但它仅限于预算有限的人的运行时检查。

至于代码合同以外的方法,Jon Skeet 几年前就此事写了一篇blog post,其中一位评论者提供了一个看起来很有用的NonNull struct,它修改了 IL 以禁用默认构造函数。这似乎是一个很好的方法,我可以想象扩展它以提供各种不可为空的microtypes。 IL 操作可能是由结构上的属性触发的构建后步骤,例如

//Microtype representing a non-zero age, so we want to disable the default ctor
[NoDefaultConstructor]
public struct Age
{
    public Age(int age)
    {
        // Implementation (including validation) elided
    }
} 

在我进一步调查之前,我想问一下是否有人知道这可能会导致任何问题?什么都想不出来。

【问题讨论】:

  • 您需要非空引用类型的用例是什么?
  • 对于那些预算延伸到 ReSharper 的人来说,那里有一些有用的无效性检查功能(尽管显然不如昂贵的 VS 版本中的合同内容那么完整)
  • @AnthonyPegram 我认为引用类型的大多数用法都是隐式非空的,因此通过方法签名强制执行在文档和安全方面是一个胜利。
  • @AakashM 谢谢,我使用 ReSharper,但尚未调查其无效性检查。会调查的!
  • 嗨 Akash,正如您在我的更新中看到的那样,我完全同意 Ani 的观点,即默认构造函数破解是不可行的。您在“这似乎是一个很好的方法”这个问题中自己说,但这并不是因为 Ani 在他的回答中提到的限制,我也同意这些案例并不“罕见”。但是,我同意您的观点,运行时空值检查有点太晚了,但除了更改语言/库/ide 之外,我看不到任何支持设计/构建时间检查的方法...... Aka。代码合同。但遗憾的是,这个功能只出现在昂贵的 vs 版本中。

标签: .net non-nullable


【解决方案1】:

这可以很容易地解决 - 运行时不会尝试在每个场景中调用结构(如果存在)的无参数构造函数。

尤其是在创建struct-type数组时不会被调用。

Age[] ages = new Age[3];

// This guy skips your "real" ctor as well as the "invalid" parameterless one.
Age age = ages[0];

...或在default(structType) 表达式中:

// Invalid state here too.
Age age = default(Age);

从 Jon Skeet 的 empirical research 到这个东西,这里列出了其他不调用构造函数的操作:

  • 只声明一个变量,无论是本地的、静态的还是实例的
  • 拳击
  • 在泛型方法中使用default(T)
  • 在泛型方法中使用new T()

现在让您陷入困境的情况是,您必须以某种方式测试每个 Age 实例,无论该实例是否是通过围绕您的栅栏工作而创建的 - 这并不比没有在第一名。

【讨论】:

  • 谢谢 Ani,我错过了 Jon 的后续文章。虽然这些都是有效的问题,但我仍然想知道这种方法是否有价值,尤其是对于内部代码,例如方法签名中的 NonNull 是关于参数允许值的明确文档(带有 some 编译器强制执行)。如果不完善的解决方案仅通过恶意用户(我的同事是专业人士)或罕见的事故暴露出来,我对不完善的解决方案感到满意。但是,我尚未确定您的示例是否属于“罕见事故”类别!
  • @Akash:我几乎不会在通用方法中将结构数组或default(T) 分类为可能是“恶意”/“罕见事故”。一旦我们接受了这一点,很明显我们将不得不验证每个实例——这让我们回到了开始的地方。 :)
  • 但是如果我们将讨论限制在微类型上,我不明白为什么有人会通过通用工厂方法 (GetMicroType) 来实例化它。同样,您将传入一个 AgeCollection,而不是将 Age[] 传递给方法,它本身将对其 ctor args 进行验证。我正在努力寻找自己缺少的东西,但鉴于我不知道有人使用这种方法,我觉得肯定存在实际问题,而不仅仅是理论上的问题。
  • 正确的做法是使用String,假设 .net 与之交互的 COM 框架将空字符串表示为 null,应该指定编译器必须使用非在String 上调用方法/属性时进行虚拟调用,并使其方法将null 视为空字符串。这可能要求Nullable<T> 不约束T 构造,但无论如何失去该约束将是一件好事。
猜你喜欢
  • 2019-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-21
  • 2022-10-14
  • 1970-01-01
相关资源
最近更新 更多