C#中的"operator true"只能在两个位置使用吗?

9

c# 允许你重载一个类的 "operator true" 和 "operator false":

class Foo
{
   bool Thing;

   Foo(bool thing)
   {
      Thing = thing;
   }

   public static bool operator true(Foo foo) => foo.Thing;
   public static bool operator false(Foo foo) => !foo.Thing;
}

它有点起作用了。你可以说

Foo foo = new Foo(true);
if (foo)
   Stuff();
string s = foo ? "yes" : "no";

但是你不能这样说。
Foo foo = new Foo(true);
bool boo = true;
if (boo && foo)
   Stuff();
if (boo & foo)
   Stuff();
if (boo & (foo == true))
   Stuff();
if (boo & ((bool)foo))
   Stuff();
Foo foo2 = new Foo(true);
if (foo && foo2)
   Stuff();
if (foo & foo2)
   Stuff();
if (foo == boo)
   Stuff();
if (foo != boo)
   Stuff();
bool boo2 = foo;

在每种情况下编译器都会抱怨。
除了那些非常特定的语法外,c#编译器在任何地方都使用"operator true"和"operator false"吗?
编辑
我发现了另一个地方可以使用“operator true”和“operator false”。如果您定义了一个“operator&”,它返回Foo以及true和false运算符,则编译器将采用表达式“foo1 && foo2”并假装您编写了“foo1&foo2”,因此调用您重载的运算符。换句话说,即使从未调用那些运算符,存在"operator true"和"operator false"也会改变编译器的行为。

1
为什么您期望 true 运算符重载其他不相关的运算符,例如 &,以及显式转换如 (bool)? - Hirasawa Yui
你尝试过 boo == foo 或者 boo != foo 吗? - Trevor
好问题@Çöđěxěŕ - 答案是 - 它不能编译。 - Betty Crokker
语句 if (boo && foo) 必须将 boo 和 foo 都转换为 bool 类型,而类 Foo 没有实现所需的隐式转换。为了使其他使用位运算符的语句正常工作,您应该实现 & 和 | 运算符。 - Oguz Ozgul
我并不是试图让这些其他语句工作;我试图理解为什么“operator true”和“operator false”甚至存在。你是对的 - 进行隐式转换几乎可以使所有这些语句编译 - 那么为什么语言设计者创建了“operator true”和“operator false”呢? - Betty Crokker
@BettyCrokker,请查看我回答的最新编辑,了解为什么你编辑中的最后一句话是不正确的。 - Sweeper
2个回答

9
“operator true”在C#中只能在两个地方使用吗?
并不完全是这样。你可以在C#语言规范上搜索“operator true”(我已经这样做了),看看它的作用。第7.12.27.147.20节都提到了它。第7.14节基本上是关于三元运算符的,你已经知道了,但在第7.20节中,它说:

一个 boolean-expression 是一个产生布尔类型结果的表达式,可以直接产生或者通过在特定上下文中应用 true 运算符来产生。

if-statement(§8.7.1)、while-statement(§8.8.1)、do-statement(§8.8.2)或 for-statement(§8.8.3)的控制条件表达式都是 boolean-expression

所以不仅仅在 if 语句中,也包括在 whiledofor 中。

在 7.12.2 中,它说:

当使用 &&|| 的操作数属于声明适用的用户定义运算符 & 或运算符 | 的类型时,必须满足以下两个条件,其中 T 是选择的运算符所声明的类型:

  • 所选运算符的返回类型和每个参数的类型都必须是 T。换句话说,运算符必须计算类型为 T 的两个操作数的逻辑 AND 或逻辑 OR,并且必须返回类型为 T 的结果。
  • T 必须包含运算符 true 和运算符 false 的声明。

因此,如果您还声明了 &,则可以在自定义类型上使用 &&


编辑:

刚刚发现这个链接,非常清晰地概括了这个问题。


换句话说,即使编译器从未调用这些运算符,"operator true"和"operator false"的存在也会改变其行为。
实际上,它确实调用了这些运算符。根据语言规范7.12.2:
操作"x && y"被评估为"T.false(x) ? x : T.&(x, y)",其中"T.false(x)"是在"T"中声明的"false"运算符的调用,"T.&(x, y)"是所选运算符"&"的调用。换句话说,首先对"x"进行评估,并在结果上调用运算符"false"以确定"x"是否绝对为"false"。然后,如果"x"绝对为"false",则操作的结果是先前为"x"计算的值。否则,将评估"y",并在先前为"x"计算的值和计算出的"y"值上调用所选运算符"&",以产生操作的结果。

基本上,由于 && 是短路运算符,它必须知道其操作数中是否有一个为假,通过使用 false 运算符。

那么为什么语言设计者创建了 "operator true" 和 "operator false" 呢?

这在这里 here 解释得很好,我认为:

true 运算符返回 bool 值 true,以指示其操作数肯定为 true。 false 运算符返回 bool 值 true,以指示其操作数肯定为 false。

这基本上是为了在您希望自定义类型具有真/假值的情况下使用。同一链接中的 LaunchStatus 类型和此处的 DBBool 类型 here 是很好的例子。


啊哈!那么我会这样总结答案:“单独使用运算符true和运算符false并不是非常有用。但是,如果您还定义了到bool的隐式转换并覆盖了operator|和operator&,那么您将得到一些很酷的东西。” - Betty Crokker
@BettyCrokker 我已经在编辑中尝试回答了“为什么”的问题。 - Sweeper
我认为很难找到任何实现了 "operator true" 和 "operator false" 的生产代码,而没有任何其他的覆盖。但是,你非常清楚地解释了为什么要创建它们以使其他功能正常工作,这是完全合理的 - 谢谢! - Betty Crokker

4

如果您希望所有的条件语句都能编译通过,那么除了使用 truefalse 之外,您还需要实现更多的运算符。您可以使用以下实现使其全部正常工作:

public class Foo
{
    bool Thing;

    public Foo(bool thing)
    {
        Thing = thing;
    }

    public static bool operator true(Foo foo) => foo;
    public static bool operator false(Foo foo) => !foo;
    public static implicit operator bool(Foo foo) => foo?.Thing ?? false;
    public static implicit operator Foo(bool b) => new Foo(b);
    public static Foo operator &(Foo left, Foo right) => (left?.Thing & right?.Thing) ?? false;
    public static Foo operator |(Foo left, Foo right) => (left?.Thing | right?.Thing) ?? false;
}

现在,如果您删除truefalse运算符,您会发现短路运算if(boo && foo)if(foo && foo2)将不再编译。正如@Sweeper在他的回答中所写的那样,这些运算符是必要的,以便使短路表达式编译。

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