简体   繁体   English

Generics 和 Nullable(类与结构)

[英]Generics and Nullable (class vs struct)

EDIT: see my update below on my stance on Null in C#8.0 before giving me options to patterns and such编辑:在给我模式等选项之前,请参阅下面我对 C#8.0 中 Null 立场的更新


Original question原始问题

I am trying to upgrade my base libraries to be "null enabled", aka using the C# 8.0 <Nullable>enable</Nullable> flag.我正在尝试将我的基础库升级为“启用空值”,也就是使用 C# 8.0 <Nullable>enable</Nullable>标志。

When trying to work with abstractions, and in specific generics, I ran into some problems.在尝试使用抽象时,在特定的 generics 中,我遇到了一些问题。 Consider the following code snippet which takes in an Action and converts this to a Func<TResult> .考虑以下代码片段,它接受一个Action并将其转换为Func<TResult> This is pre-nullable-enable :这是pre-nullable-enable

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

but post-nullable-enable I seem to struggle, since I cant make TResult nullable (or TResult? ) as wit would require a constraint of either where TResult: class or where TResult: struct .但是post-nullable-enable我似乎很挣扎,因为我不能让 TResult 可以为空(或TResult? ),因为机智需要限制where TResult: classwhere TResult: struct There is no way I can combine these two constraints to let the compiler know that TResult can be a class or a valuetype.我无法将这两个约束结合起来让编译器知道 TResult 可以是 class 或值类型。 Which at the moment I find annoying - as one should be able to express it doesn't matter whether class or struct, it'll be nullable (regardless of previous .NET intherited design).目前我觉得这很烦人 - 因为应该能够表达class 或结构都没有关系,它可以为空(不管以前的 .NET 继承设计)。

So, post-nullable-enable I seem to have only one option, and that is code duplication, examples below:所以, 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; }
;

Both the code duplication as well as the naming schemes that come with it bother me a lot.代码重复以及随之而来的命名方案都让我很困扰。 I might be misunderstanding proper usage, or maybe I'm missing another feature of the spec, but how would you solve this?我可能误解了正确的用法,或者我错过了规范的另一个功能,但是您将如何解决这个问题?


UPDATE : In fact, I think I'd rather stick to my own "null-handling" implementations than using C#8.0's nullable feature.更新:事实上,我认为我宁愿坚持自己的“空处理”实现,也不愿使用 C#8.0 的可空特性。 As a "Void" object or fleshed out "Option/Maybe/None"-solution seems to communicate things better.作为“无效” object 或充实“选项/可能/无”的解决方案似乎可以更好地传达信息。 The only thing I am concerned about is that it isn't very helpful in moving the language forward , training new coders and introduces another third party/non native solutation to a universal provlem we all have dealing with null .我唯一担心的是,对于推动语言向前发展培训新编码人员以及为我们都处理 null通用问题引入另一个第三方/非本地解决方案并不是很有帮助。 Because own implementations of handling with null are great and all, but come up differently in each code base, need to be maintained by the community, and you have various different flavours.因为自己处理 null 的实现非常棒,但是在每个代码库中出现的方式不同,需要由社区维护,并且您有各种不同的风格。 So it would be so helpful and hugely beneficial if the language enforced it fully, and if a standard rised.因此,如果语言完全执行它,并且如果标准提高,那将是非常有帮助和非常有益的。 Which I hoped this would be - clearly it is not, and I understand.我希望这是 - 显然不是,我理解。 But I feel this is a missed opportunity.但我觉得这是一个错失的机会。

Thx Yves谢谢伊夫

The reason is explained in Try out Nullable Reference Types in the section The issue with T?原因在“ T The issue with T? . . Essentially, there's no way around this.本质上,没有办法解决这个问题。

First, what is T when we use T?首先,当我们使用T时,T 是T? ? ? Is it nullable or non-nullable?它是可空的还是不可空的?

A natural definition of T? T 的自然定义? would mean, "any nullable type".意思是“任何可以为空的类型”。 However, this would imply that T would mean "any non-nullable type", and that is not true?但是,这意味着 T 意味着“任何不可为空的类型”,这不是真的吗? It is possible to substitute a T with a nullable value type today (such as bool.).现在可以将 T 替换为可为空的值类型(例如 bool.)。

Second, the types used in each case are different - a string?其次,每种情况下使用的类型都不同—— string? is still a string while an int?仍然是一个string ,而一个int? is a Nullable<int> .是一个Nullable<int> The generated concrete methods in each case are completely different.每种情况下生成的具体方法完全不同。 In one case you get a在一种情况下,您会得到一个

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

In the other, you get a另一方面,你得到一个

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.可空值类型 map 到 .NET 中的具体 class 类型。 So int?所以int? is actually Nullable.实际上是可空的。 But for string?, it's actually the same string but with a compiler-generated attribute annotating it.但是对于 string?,它实际上是相同的字符串,但带有一个编译器生成的属性对其进行注释。 This is done for backwards compatibility.这样做是为了向后兼容。 In other words, string?换句话说,字符串? is kind of a "fake type", whereas int?是一种“假类型”,而 int? is not.不是。

The article's example demonstrates this:这篇文章的例子证明了这一点:

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.这意味着参数是 T 的可空版本,并且 T 被限制为非空。

If T were a string, then the actual signature of M would be:如果 T 是一个字符串,那么 M 的实际签名将是:

M<string>([NullableAttribute] T t)

but if T were an int, then M would be但如果 T 是一个 int,那么 M 将是

M<int>(Nullable<int> t)

These two signatures are fundamentally different, and this difference is not reconcilable.这两个签名本质上是不同的,而且这种差异是不可调和的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM