如何将C# 8中默认的返回值标记为可为空,仅适用于类?

6

我目前正在尝试将新的C# 8.0非空引用类型特性应用于现有代码,并且不知道如何修复以下数据反序列化方法中的CS8603警告:

T ReadOptional<T>() where T : IEntity, new()
{
    if (ReadBoolean())
    {
        T instance = new T();
        instance.Read(this); // IEntity method
        return instance;
    }
    else
    {
        // CS8603 Possible null reference return.
        return default;
    }
}

如您所见,如果前面的布尔值为假,则该方法可能返回null(类)/ default(结构体),否则它将返回一个实现IEntity的任何内容的T实例。
但是,我无法将返回类型标记为T?,因为如果T是一个结构体,它实际上不会返回null,这正如编译器错误CS8627所正确抱怨的那样。
// CS8627: A nullable type parameter must be known to be a value type or non-nullable
// reference type. Consider adding a 'class', 'struct', or type constraint.
T? ReadOptional<T>() where T : IEntity, new()
  • 我不能通过确保 T 具有 class 约束来修复此问题,因为我还希望 struct 实例能够使用此方法(返回 default)。
  • 我不能创建一个 T 具有 struct 约束的重载,因为约束不能是唯一不同的重载。
  • 我可以创建不同名称的方法,但这将破坏库接口,并且会影响太多依赖于它的代码。

是否有语法可以修复非空警告,而不会破坏针对结构体返回 default 实例的可能性?


如果您将一个类作为<T>传递,并且ReadBoolean返回false,您会期望发生什么? - Lasse V. Karlsen
@LasseVågsætherKarlsen 它将返回“null”。 - Ray
1个回答

15

4
等等,这似乎不对劲。类型T被标记为非空(nulllable),所以无论它是什么,在调用Readoptional时必须保证它不为null,这会欺骗编译器,但更重要的是,这可能会导致微妙的错误。只是想让大家意识到这一点。 - negyxo
@negyxo 很抱歉,这个答案中有一个小的复制粘贴错误(简化源代码并不完全像生产环境中使用的那样) - 当然返回类型必须是 T?,就像答案中第二个示例一样。我现在已经添加了它。 - Ray
你的更改是否使这个作为问题的答案无效?如果问题是“如何从此方法返回非空值”,而你现在只是说“这里是一个可为空的值从这个方法中返回”。感叹号并不使值非空,它只是告诉编译器闭嘴,所以如果你使用ReadOptional<string>,而ReadBoolean返回false,你仍然会得到null - Lasse V. Karlsen
1
公平地说,这个问题并没有太多意义,除非 return default 实际上是指 return new T() - Lasse V. Karlsen
抱歉,我并不是想增加混淆,而是想解决这个解决方案可能存在的问题。虽然我没有表达清楚,但类型T既不是标记为非空也不是可为空的,它只是一种通用类型,可以是非空类型,在这种情况下,这个解决方案就不安全了。在我看来,只有一个解决方案,那就是在函数中传递默认值,类似于ReadOptional<T>(T defaultValue)。这不太好,但它保留了类型安全性。 - negyxo
显示剩余2条评论

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