泛型和可空(class vs 结构)

Generics and Nullable (class vs struct)

编辑: 在给我模式等选项之前,请参阅下面关于我对 C#8.0 中 Null 的立场的更新


原题

我正在尝试将我的基础库升级为 "null enabled",也就是使用 C# 8.0 <Nullable>enable</Nullable> 标志。

在尝试使用抽象和特定泛型时,我 运行 遇到了一些问题。考虑以下代码片段,它接受一个 Action 并将其转换为 Func<TResult>。这是 pre-nullable-enable:

public static Func<TResult> ToFunc<TResult>(this Action action)
        => () => { action(); return default; }
;

但是 post-nullable-enable 我似乎很挣扎,因为我不能使 TResult 可为空(或 TResult?),因为机智需要约束where TResult: classwhere TResult: struct。我无法将这两个约束结合起来让编译器知道 TResult 可以是 class 或值类型。目前我觉得这很烦人——因为一个人应该能够表达 不管是 class 还是结构,它都可以为空 (不管以前的 .NET继承设计)。

所以,post-nullable-enable 我好像只有一个选择,那就是代码重复,例子如下:

public static Func<TResult?> ToFuncClass<TResult>(this Action action)
        where TResult : class
        => () => { action(); return null; }
;

public static Func<TResult?> ToFuncStruct<TResult>(this Action action)
        where TResult : struct
        => () => { action(); return null; }
;

代码重复以及随之而来的命名方案都让我很困扰。我可能误解了正确的用法,或者我可能遗漏了规范的另一个功能,但你会如何解决这个问题?


UPDATE:事实上,我想我宁愿坚持自己的 "null-handling" 实现 而不是使用 C# 8.0 的可空特性。作为一个 "Void" 对象或充实的 "Option/Maybe/None"-解决方案 似乎可以更好地传达信息。我唯一担心的是它在推动语言发展培训新编码员方面不是很有帮助 =61=] 并引入另一个 第三 party/non 本机解决方案 通用 问题我们都有 处理 null 。 因为 自己的 null 处理实现非常棒,但是在每个代码库中不同,需要由社区维护,并且你有各种不同的口味。因此,如果语言完全强制执行它,并且 标准 上升,那将是非常有帮助和非常有益的。我希望这会是 - 显然不是,我理解。但我觉得这是一个错失的机会。

谢谢 伊夫

The issue with T? 部分的 Try out Nullable Reference Types 中解释了原因。本质上,没有办法解决这个问题。

首先,当我们使用T?时,T是什么?它可以为空还是不可为空?

A natural definition of T? would mean, "any nullable type". However, this would imply that T would mean "any non-nullable type", and that is not true! It is possible to substitute a T with a nullable value type today (such as bool?).

其次,每种情况下使用的类型不同 - string? 仍然是 string,而 int?Nullable<int>。每种情况下生成的具体方法是完全不同的。在一种情况下,您会得到

Func<string> ToFuncClass<string>(this Action action)

在另一个中,你得到一个

Func<Nullable<int>> ToFuncStruct<int>(this Action)

Next, it’s important to note that a nullable reference type is not the same thing as a nullable value type. Nullable value types map to a concrete class type in .NET. So int? is actually Nullable. But for string?, it’s actually the same string but with a compiler-generated attribute annotating it. This is done for backwards compatibility. In other words, string? is kind of a "fake type", whereas int? is not.

文章的示例证明了这一点:

This distinction between nullable value types and nullable reference types comes up in a pattern such as this:

void M<T>(T? t) where T: notnull

This would mean that the parameter is the nullable version of T, and T is constrained to be notnull.

If T were a string, then the actual signature of M would be :

M<string>([NullableAttribute] T t)

but if T were an int, then M would be

M<int>(Nullable<int> t)

These two signatures are fundamentally different, and this difference is not reconcilable.