C# 8中可空类型和泛型的问题

14
在添加 <Nullable>enable</Nullable>#nullable enable 后,我的通用方法出现了以下问题:
这样做是不起作用的:
public T? GetDefault<T>()
{
    return default;
}

enter image description here

这个可以工作,但是有警告:

public T GetDefault<T>()
{
   return default;
}

enter image description here

这两部分单独使用是有效的,但是一起使用时却不行。

public T? GetDefault<T>() where T : class
{
    return default;
}

public T? GetDefault<T>() where T : struct
{
    return default;
}

enter image description here

从逻辑上讲,第一种方法应该可行。
在不创建多个方法和抑制警告的情况下,无论使用哪个框架,应该如何正确处理这种情况?
[MaybeNull] 属性仅适用于 .Net Core 3.0+。

此外,我在这里提出了这个问题。


“Does not work” 是什么意思? - Chetan
我添加了截图。 - Konstantin S.
我投票支持重新开放这个问题。这个“重复”的内容与可空引用特性没有任何关系。 - György Kőszeg
是的,它被关闭了确实很奇怪。这是语言的一个新功能,一些开发人员可能不知道它。 - Konstantin S.
这种行为已经有很多重复了。这回答了你的问题吗?可空引用类型:如何指定“T?”类型而不限制为class或struct,以及具有通用返回类型的可空引用类型。它们是一年多前提出的,通过几分钟的谷歌搜索就可以找到,没有什么新内容了。 - Pavel Anikhouski
[MaybeNull] 属性仅适用于 .Net Core 3.0+。 - Konstantin S.
3个回答

13

T?只有在类型参数已知为引用类型或值类型时才能使用。否则,我们无法确定将其视为System.Nullable<T>还是可空参考类型T

在C# 8中,您可以使用[MaybeNull]属性来表达这种情况。

#nullable enable
using System.Diagnostics.CodeAnalysis;

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

这个属性只在 .NET Core 3.0+ 中包含,但是可以在你的项目中声明和使用 internal 属性(虽然官方不支持,但没有理由认为行为会在未来出现问题)。要实现这一点,你只需向代码中添加一个类似以下的命名空间+类声明:

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 项目中实现此功能的方法。 - Konstantin S.
你可以在你的项目中定义该属性。这些可空性属性很简单:https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs - Julien Couvreur
它会在NuGet库外可见吗(没有public修饰符,因为这可能会导致冲突)? - Konstantin S.
看起来像 Newtonsoft.Json 这样的知名库在 pre-netcoreapp3.0 构建中具有内部的 nullability 属性。我认为内部类型不会与其他程序集中的类型发生冲突。 - Rikki Gibson
是的,它有效。编译器正在查找具有完整名称(尽管没有程序集)System.Diagnostics.CodeAnalysis.MaybeNullAttribute的类型上的属性。它不是在寻找特定类型。这类似于较早版本的 .net framework 中异步工作的方式。内部属性将具有相同的名称。 - Konstantin S.
这个答案与链接的重复问题的答案几乎相同 https://dev59.com/8FQJ5IYBdhLWcg3wGB1N - Pavel Anikhouski

9
问题的解释

在你提供的第一个代码示例中,问题发生在编译器处理可空值类型和可空引用类型时不同的情况:

  • 可空值类型 T? 由类型 Nullable<T> 表示。
  • 可空引用类型 T? 是相同的类型 T,但带有编译器生成的属性注释。

编译器无法同时生成适用于这两种情况的代码,因此会出现编译错误。而这个错误迫使我们指定 classstruct 约束。这个行为在C# 规范中也有说明:

对于类型参数 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


5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接