C# 8 中 Nullable 类型和泛型的问题

Posted

技术标签:

【中文标题】C# 8 中 Nullable 类型和泛型的问题【英文标题】:A problem with Nullable types and Generics in C# 8 【发布时间】:2020-09-07 07:26:18 【问题描述】:

添加<Nullable>enable</Nullable> or #nullable enable 后,我的通用方法遇到了以下问题:

这不起作用:

public T? GetDefault<T>()

    return default;

这适用于警告:

public T GetDefault<T>()

   return default;

这可以单独使用,但不能一起使用。

public T? GetDefault<T>() where T : class

    return default;


public T? GetDefault<T>() where T : struct

    return default;

从逻辑上讲,第一种方法应该有效。 在不创建多种方法和禁止警告的情况下,摆脱这种情况的正确方法(在任何框架中)是什么? [MaybeNull] 属性仅适用于 .Net Core 3.0+。

另外,我问了这个问题here

【问题讨论】:

does not work 是什么意思? 我添加了截图 我投票决定重新提出这个问题。 “重复”与可空引用功能无关。 是的,关门很奇怪。这是该语言的一项新功能,一些开发人员对此并不了解。 这种行为已经有很多重复了。这回答了你的问题了吗? Nullable reference types: How to specify "T?" type without constraining to class or struct 和 Nullable reference types with generic return type。一年前有人问过,谷歌搜索几分钟就可以找到,已经没有什么新东西了。 【参考方案1】:

T? 只能在已知类型参数是引用类型或值类型时使用。否则,我们不知道是将其视为System.Nullable&lt;T&gt; 还是可空引用类型T

相反,您可以使用 [MaybeNull] 属性在 C# 8 中表达此场景。

#nullable enable
using System.Diagnostics.CodeAnalysis;

public class C

    [return: MaybeNull]
    public T GetDefault<T>()
    
        return default!; // ! just removes warning
    

此属性仅包含在 .NET Core 3.0+ 中,但可以在项目内部声明和使用该属性(虽然官方不支持,但没有理由假设该行为会破坏线) .为此,您只需在代码中添加一个命名空间+类声明,如下所示:

namespace System.Diagnostics.CodeAnalysis

    /// <summary>Specifies that an output may be null even if the corresponding type disallows it.</summary>
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
    internal sealed class MaybeNullAttribute : Attribute  

【讨论】:

[MaybeNull] 属性仅适用于 .Net Core 3.0+。我没有找到为 .Net Standard 2.0 项目执行此操作的方法。 您可以在项目中定义该属性。可空性属性很简单:github.com/dotnet/runtime/blob/master/src/libraries/… 在 nuget 库之外是否可见(没有 public 修饰符,因为它会导致冲突)? 在 netcoreapp3.0 之前的版本中,Newtonsoft.Json 等著名库似乎具有内部可空性属性。我认为内部类型不会与其他程序集中的类型冲突。 是的,它有效。 > “编译器正在寻找具有 System.Diagnostics.CodeAnalysis.MaybeNullAttribute 的全名(虽然没有程序集)的类型的属性。它不是在寻找特定类型。这类似于早期版本中异步的工作方式.net 框架。内部属性将具有相同的名称。"【参考方案2】:

问题说明

出现第一个代码示例中的问题是因为编译器以不同的方式处理可空值类型和可空引用类型:

可空值类型T? 由类型Nullable&lt;T&gt; 表示。 可空引用类型 T?T 类型相同,但具有编译器生成的属性对其进行注释。

编译器无法同时生成涵盖这两种情况的代码,因此会发生编译错误。这个错误迫使我们指定classstruct 约束。 C# specification中也说明了这种行为:

对于类型参数T,仅当已知T 是一个值时才允许T? 类型或已知为引用类型。

可以在这篇文章中找到对这个问题的一个很好的解释:Try out Nullable Reference Types。滚动到“T? 的问题”段落。


解决问题的解决方法

如果您不想创建两个具有不同名称的方法并禁止显示警告,则可以使用下一个解决方法:

// An overload that will be used by reference types.
public T? GetDefault<T>(T? t = default) where T : class

    return default;


// An overload that will be used by value types.
public T? GetDefault<T>(T? t = default) where T : struct

    return default;

在这里,我们在方法GetDefault 中添加了一个参数t,以使编译器能够区分这两种方法。现在我们可以使用方法GetDefault 并且编译器将定义要使用的重载。这种方法的缺点是GetDefault 方法有不可用的参数t

【讨论】:

【参考方案3】:

看来这个问题的最佳解决方案只能在C# 9 as T??

链接: 1.https://github.com/dotnet/csharplang/issues/3471#issuecomment-631722668 2.https://github.com/dotnet/csharplang/issues/3297

目前,Rikki Gibson 提供了一个可行的解决方案。它意味着额外的代码,但它可以正常工作。

【讨论】:

以上是关于C# 8 中 Nullable 类型和泛型的问题的主要内容,如果未能解决你的问题,请参考以下文章

值类型引用类型和泛型的前世今生

关于 c# 接口和泛型的问题

JAVA中,关于可变参数和泛型的问题。

请教一个unity有关于泛型参数的问题

C# 泛型的使用

泛型的泛型的好处