我们有一个具有隐式字符串运算符的类型。它看起来像这样:
public class Foo
{
readonly string _value;
Foo(string value)
{
_value = value;
}
public static implicit operator string(Foo foo)
{
return foo._value;
}
public static implicit operator Foo(string fooAsText)
{
return new Foo(fooAsText);
}
}
我们只有一个场景,传递给隐式运算符的实例是null
。显然,我们最终得到了NullReferenceException
。
我认为这是公平的,毕竟,什么是一个null的类型的字符串表示 - 很难说 - 所以异常似乎是有效的,我们不应该拦截/抑制/处理/忽略。我的理由是,有人给我一个null,为什么我应该返回null。我不会用其他方法做到这一点'。我想,“我知道,我只是在转换它之前检查null”,如下所示:
string s = foo == null ? null : foo;
但这不起作用,因为它现在转换为字符串之前 比较为null。当然,这种比较可行:
Foo f = null;
string s;
if (f != null)
{
s = f;
}
......但那只是丑陋。
我阅读了Essential C# 5 4th edition(a' Rough Cuts' Safari上的书)中的部分,并说:
不要从隐式转换中抛出异常。
这表示不会抛出异常,但是如果我压制异常会让我感到疑惑?
最明显的做法是跳转到方法并将其更改为:
public static implicit operator string(Foo foo)
{
return foo == null ? null : foo._value;
}
问题解决了。但我觉得很脏。我觉得我通过检查null来隐藏潜在的bug。我是偏执狂/肛门/傻瓜吗?
答案 0 :(得分:11)
在这种情况下我建议的是,失败的运算符应该是显式的而不是隐式的。隐式转换的想法是它正在扩大(即它永远不会失败。)另一方面,明确的转换被理解为有时会失败。
答案 1 :(得分:2)
我希望Foo
的行为与string
相同。所以,我希望
Foo foo = null;
string.Equals(null, foo);
是真的。
string.Equals将尝试将foo
转换为字符串,以便调用隐式运算符。空值foo
应该与空值string
暴露相同的值,即null
。
因此,如果您确实希望有一个隐式运算符,则可以将其实现为:
public static implicit operator string(Foo foo)
{
return foo?._value;
}
答案 2 :(得分:-1)
您正在尝试将Foo类型强制为null,因为:
两侧的类型必须相等。
string s = foo == null ? null : foo;
所以您应该使用
string s = foo == null ? (string)null : foo;
或
string s = foo == null ? null : (string)foo;
答案 3 :(得分:-1)
最好在隐式转换运算符内“抑制”异常。实际上,正如您所指出的那样,这是推荐的方法。
考虑这种情况:
Foo actualFoo = null;
string stringFoo = actualFoo;
如果此编译很好,为什么还要让第二行在运行时引发异常?那会很有意义。
同样如此:
string stringFoo = null;
Foo actualFoo = stringFoo;
如果您要同时定义两个隐式转换运算符,则意味着您要像对待 string 一样对待和使用 Foo 类型。在这种情况下,以上两种情况都是完全有效的构造(您的编译器已确认),并且应产生null而不是引发异常。
现在,如Dan所指出的那样,如果这不是您想要的,那么您将需要定义一个显式转换。