【问题标题】:out parameters with nullable reference types具有可为空引用类型的参数
【发布时间】:2018-12-13 10:00:25
【问题描述】:

我已经为我的一些项目实现了Option 类型,如下所示:

public abstract Option<T> {}

public class None<T> : Option<T>

public class Some<T> : Option<T> 
{
    public T Value { get; }

    public Some(T value)
    {
        Value = value;
    }
}

为了找出一个选项是否包含一个值,我使用了这个利用模式匹配的扩展方法:

public static bool TryGetValue<T>(this Option<T> option, out T value)
{
    if (option is Some<T> some)
    {
        value = some.Value;
        return true;
    }

    value = default;
    return false;
}

我现在收到return default; 的以下警告

无法将 null 文字转换为不可为 null 的引用或不受约束的类型参数

我无法将泛型参数T 限制为classstruct

例如,如果我将泛型参数限制为class,我无法生成Option&lt;int?&gt; 实例,因为Nullable&lt;int&gt; 类型本身就是struct。通过后缀? 将 out 参数声明为可为空也不是一个解决方案。

在我看来,类型系统在这个阶段有些破损或没有经过彻底考虑。 Nullable 应该是 class 或者需要有一个通用参数限制,例如:

public static bool TryGetValue<T>(this Option<T> option, out T value) 
    where T : nullable [...]

是否有其他方法可能适合此问题?我错过了什么?

【问题讨论】:

  • 我认为你应该看看这个youtube.com/watch?v=F9bznonKc64Mark Seemann 谈论单子。在此之后,您停止尝试从 monad 中获取价值。 :)
  • 不管怎样,我想知道不可为空的引用类型的默认值是什么
  • 好吧,我复制了你的代码,它没有提示你的警告。对不起,我帮不了你。你用VS2019吗?
  • 为什么不在抽象类上添加一个抽象的Value属性,在None&lt;T&gt;的情况下通过返回默认值来实现它,在Some&lt;T&gt;上通过返回它的值来实现它?

标签: c# c#-8.0 nullable-reference-types


【解决方案1】:

使用MaybeNullWhenAttributeNotNullWhenAttribute。我推荐MaybeNullWhen,因为它甚至适用于不受结构或引用类型约束的类型参数。

public static bool TryGetValue<T>(this Option<T> option, [MaybeNullWhen(false)] out T value)
{
    if (option is Some<T> some)
    {
        value = some.Value;
        return true;
    }

    value = default;
    return false;
}

用法:

if(option.TryGetValue(out var value))
{
    value.SomeMethod(); // no warning - value is known to be non-null here
}
value.SomeMethod(); // warning - value may be null here.

该属性在 .Net standard 2.1/.new core 3.0 之前不可用,但如果不可用,您可以自己手动定义。确保它是内部的,否则如果另一个库也将它定义为公共的并且有人从这两个库继承它会导致冲突:

namespace System.Diagnostics.CodeAnalysis
{
    /// <summary>Specifies that when a method returns <see cref="ReturnValue"/>, the parameter may be null even if the corresponding type disallows it.</summary>
    [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
    internal sealed class MaybeNullWhenAttribute : Attribute
    {
        /// <summary>Initializes the attribute with the specified return value condition.</summary>
        /// <param name="returnValue">
        /// The return value condition. If the method returns this value, the associated parameter may be null.
        /// </param>
        public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;

        /// <summary>Gets the return value condition.</summary>
        public bool ReturnValue { get; }
    }

(取自https://github.com/dotnet/runtime/blob/6077cf01f951a711a26a8d5970b211b6031b5158/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs#L45-L60

【讨论】:

    【解决方案2】:

    到目前为止,C# 8.0 还没有一个完全通用的解决方案。

    [NotNullWhen()] 属性是前进了一步,但接下来我们会遇到以下情况:

    必须知道可空类型参数是值类型或不可空引用类型。考虑添加“类”、“结构”或类型约束。

    我想说这是现在可空值的主要痛点。我希望它会在 8.1 或其他东西中得到解决......

    相关讨论 - https://github.com/dotnet/csharplang/issues/2194 - 允许泛型方法指定 T?不受类或结构的限制

    作为一种解决方法,可以制作具有所需where 约束的多个扩展方法副本以涵盖所有可能的类型。

    自从问题#1628 得到修复后,现在可以在单个扩展类中包含所有重载。但它仍然需要为每个独立的通用输出参数增加一倍的扩展方法数量。哎呀!

    快速浏览一下,我找不到特定于 out 参数的未解决问题。如果还没有完成,可能值得将此特定用例带到 csharplang github 问题上。

    更新:我在上述问题中提出了comment 的看法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多