定义两个可为空的隐式运算符时,无法将null转换为struct

时间:2018-09-20 15:02:23

标签: c# struct null operator-keyword implicit-conversion

以下内容无法编译:

public struct Foo
{
    public static implicit operator Foo(string bar)
    {
        return new Foo();
    }

    public static implicit operator Foo(long? bar)
    {
        return new Foo();
    }

    public static void Test()
    {
        Foo bar = 0;
        Foo bar2 = (long?)null;
        Foo bar3 = "";
        Foo bar4 = null; // Cannot convert null to 'Foo' because it is a non-nullable value type
    }
}

'Foo bar4 = null'失败,可能是因为编译器不知道要使用哪个隐式运算符,因为将运算符从long更改了?到long导致该行被编译,但是'Foo bar2 =(long?)null'相反而失败(需要显式强制转换)。

我的问题是;是否有办法使'Foo bar4 = null'也起作用,或者这仅仅是语言的限制(我不能添加'null'运算符,也不能告诉它例如将哪个用于null)?

我意识到我可以将结构更改为类,但是我不希望它为null,我希望能够为它创建一个实例。

编辑:我应该补充一点,我理解有很多方法可以解决此问题,但是由于'= null'(本质上是执行'new Foo()')只能用于以下一种隐式运算符,我只是想知道是否有可能让它们仍然与它们同时工作(我觉得应该有一种语言可以做到这一点-现在还是将来,不是吗?)。 / p>

3 个答案:

答案 0 :(得分:3)

  

我的问题是;是否有办法使'Foo bar4 = null'也起作用,或者这仅仅是语言的限制(我不能添加'null'运算符,也不能告诉它例如将哪个用于null)?

根据这个问题和您的修改,您基本上是在问

  

如果我添加另一个隐式强制转换运算符,为什么Foo bar4 = null不能编译?

答案很简单。没有任何上下文的null是无类型的,因此编译器不知道要使用哪个运算符。为什么?很好,作为语言基础的重载解析算法不会检查您要分配给null的事物的类型,因此它不知道您打算使用哪个运算符。

您可能会争辩说,可以对将来的语言规范进行修改以进行此额外的分析,但团队可能认为这不值得付出努力,否则将是一个重大突破。

在这种情况下,您最好做的是避免强制转换。您可以根据您的C#级别执行以下任一操作:

  • Foo bar4 = default;
  • Foo bar4 = default(Foo);

这将导致可用的Foo。这两个默认表达式和new Foo()都是等效的。它们都导致结构的所有字段都清零。

有关更多信息,请参见C#编程指南的default value expressions部分。

最后,尽管您可以使用将一个null强制转换为结构的隐式强制转换运算符,但这并不意味着您应该。有人读了那个不了解演员表的代码可能会在几分钟内质疑他们的理智。如果可以的话,最好不要偏离惯用代码。

答案 1 :(得分:2)

您的结构不可为空,因为您引入了一种强制类型转换,该强制类型转换可将null值强制转换为该类型。强制转换本身只是一个不会更改语义类型的成员。

实际上,struct本身为从不为null,但是对其的引用可以为,但仅当它们为Nullable<Foo>类型时。因此,bar4的类型必须为Foo?,与Nullable<Foo>相同。

答案 2 :(得分:0)

如果我没记错的话,就永远不能为结构分配空值,因为它是一种值类型,类似于intboolDateTime

但是,您可以像这样使用Nullable
Foo? bar4 = null;

Nullable<Foo> bar4 = null;

请确保您像对待其他任何Nullable 一样对待它,并在引用.HasValue之前检查bar4.Value以避免出现NullReferenceException异常。