【问题标题】:Implicit (bool) and == operator override - handle if statements correctly隐式 (bool) 和 == 运算符覆盖 - 正确处理 if 语句
【发布时间】:2023-03-30 11:11:02
【问题描述】:

我有一个自定义类,它同时实现了 ==implicit 用于布尔运算符。

这是处理所有可能的正确方法吗,如果 ==/!= 语句并获得预期结果? 像这样:

public class Foo
{
    public bool Result { get; set; }

    public static bool operator ==(bool @bool, Foo foo)
    {
        return Equals(foo, @bool);
    }
    public static bool operator !=(bool @bool, Foo foo)
    {
        return NotEquals(foo, @bool);
    }
    public static bool operator ==(Foo foo, bool @bool)
    {
        return Equals(foo, @bool);
    }
    public static bool operator !=(Foo foo, bool @bool)
    {
        return NotEquals(foo, @bool);
    }
    public static bool operator ==(Foo foo, Foo fooB)
    {
        return Equals(foo, fooB);
    }
    public static bool operator !=(Foo foo, Foo fooB)
    {
        return NotEquals(foo, fooB);
    }
    public static implicit operator bool(Foo foo)
    {
        try { return foo.Result; }
        catch { return false; }
    }

    private static bool Equals(Foo foo, Foo fooB)
    {
        if (object.Equals(foo, null))
        {
            if (object.Equals(fooB, null))
                return true;

            return false;
        }

        if (object.Equals(fooB, null))
            return false;

        return foo.Result == fooB.Result;
    }
    private static bool NotEquals(Foo foo, Foo fooB)
    {
        if (object.Equals(foo, null))
        {
            if (object.Equals(fooB, null))
                return false;

            return true;
        }

        if (object.Equals(fooB, null))
            return true;

        return fooB.Result != foo.Result;
    }
    private static bool Equals(Foo foo, bool @bool)
    {
        if (object.Equals(foo, null))
            return true;

        return @bool == foo.Result;
    }
    private static bool NotEquals(Foo foo, bool @bool)
    {
        if (object.Equals(foo, null))
            return false;

        return @bool != foo.Result;
    }

}

我特别想知道你似乎真的需要为任何一个实现重载

if (new Foo() != true)

if (true != new Foo())

【问题讨论】:

  • 你为什么要这样做而不是有一个方法bool PossibleResult()
  • @Jon 我正在开发一个 EntityFramework 应用程序,以 Firebird 作为我的数据库,遗憾的是,当前版本的 Firebird 不支持布尔值,所以我想创建一个自定义类,将其分配给我的 EDMX 文件中的字段。通过隐式转换为 bool 和覆盖相等运算符,我可以使用 DataBinding,就好像它是一个布尔值一样,不需要为每个 bool 属性编写包装器。
  • 我不确定我是否了解全部情况,但听起来构建时代码生成可以帮助您。 Google T4 模板,它们内置于 VS 中,完全符合要求。
  • 不清楚你在问什么——或者这在 CodeReview 上是否真的会更好。我真的不喜欢您隐式转换为 bool 实现...捕获 all 异常并吞下它们,而不是仅检查 null?伊克。
  • @Dan 我想不出一个好名字,所以我把它命名为 bool,你必须应用 @,因为 bool 是 C# 中的保留关键字。

标签: c# operators overriding implicit


【解决方案1】:

我认为你写了太多代码:-)

以下内容就足够了:

public class Foo
{
    public bool Result { get; set; }

    public static implicit operator bool(Foo foo)
    {
        return !object.ReferenceEquals(foo, null) && foo.Result;
    }
}

然后编译器将知道如何将Foo 类型的变量隐式转换为bool。 (而null 将转换为false)。

所以,当你写的时候:

new Foo() == false

编译器将使用隐式类型转换器从Foo 获取bool 值,然后对bool 使用标准相等运算符。

如果我们查看编译器为该表达式生成的 IL:

newobj instance void FooBool.Foo::.ctor()               // new Foo()
call bool FooBool.Foo::op_Implicit(class FooBool.Foo)   // implicit operator (Foo => bool)
ldc.i4.0                                                // false
ceq                                                     // equality operator (bool)

这是一个测试:

static void Main(string[] args)
{
    AssertTrue(new Foo() == false);
    AssertTrue(false == new Foo());
    AssertFalse(new Foo() != false);
    AssertFalse(false != new Foo());
    AssertTrue(new Foo { Result = true } == true);
    AssertTrue(true == new Foo { Result = true });
    AssertFalse(new Foo { Result = true } != true);
    AssertFalse(true != new Foo { Result = true });
}

static void AssertTrue(bool value)
{
    Console.WriteLine(value ? "ok" : "not ok");
}

static void AssertFalse(bool value)
{
    Console.WriteLine(value ? "not ok" : "ok");
}

它为每个测试打印ok。所以如果我理解正确的话,这个简化的代码应该可以满足你的需求。

更新

要允许等式运算符适用于 Foo(可能为 null)的实例:

public static bool operator ==(Foo a, Foo b)
{
    if (object.ReferenceEquals(a, b))
    {
        return true;
    }
    else if (object.ReferenceEquals(a, null))
    {
        return !b.Result;
    }
    else if (object.ReferenceEquals(b, null))
    {
        return !a.Result;
    }
    else
    {
        return a.Result == b.Result;
    }
}

你还应该实现不等式运算符:

public static bool operator !=(Foo a, Foo b)
{
    return !(a == b);
}

并且还覆盖GetHashCode + Equals

public override int GetHashCode()
{
    return this.Result ? 1 : 0;
}

public override bool Equals(object obj)
{
    if (object.ReferenceEquals(obj, null))
    {
        return !this.Result;
    }

    Type t = obj.GetType();

    if (t == typeof(Foo))
    {
        return this.Result == ((Foo)obj).Result;
    }
    else if (t == typeof(bool))
    {
        return this.Result == (bool)obj;
    }
    else
    {
        return false;
    }
}

【讨论】:

  • 我认为,在比较 2 Foos 或?还是 IL 隐式转换双方? new Foo() { Result = false) == new Foo() { Result = false } 这将在您的代码中返回 false 不会,所以我相信 samy 的答案更完整还是?
  • 抱歉,不明白您希望等式运算符适用于 Foo 实例。我已经更新了我的答案。
  • @MårtenWikström 我不明白为什么 Foo 和 Foo 的 == 运算符在一个为空时返回另一个 Foo 结果的倒数;如果 FooA = null 和 FooB = Foo(true) 则比较两者将返回 false,但如果 FooA = null 和 FooB = Foo(false) 它将返回 true,这是不正确的?这种行为的原因是什么?
  • 如果 A 为空,如果 B 为空或结果为假,则它应该等于 B。当 FooA=null 和 FooB=Foo(false) 时返回 true 确实是正确的,因为您已经定义了 null Foo 实例应隐式转换为 false 布尔值。
【解决方案2】:

我认为您明确涵盖了代码中的所有基础;如果需要,您必须考虑运算符的参数顺序,因此如果您希望为参数的两个顺序调用 Equals 函数,您所做的是正确的。

但是,在比较 Foobool 的情况下,它看起来有点矫枉过正,因为您可以简单地依赖隐式转换。这将允许您删除这两种类型之间的所有运算符以及 EqualsNotEquals 方法。

更重要的是,它可以避免代码中关于将空 Foo 转换为布尔值的一些不一致。当您将空 Foo 传递给 Equals 方法时,它将返回 true 而在隐式转换中,空 Foo 将返回 false

true == (Foo)null; //true
true == Convert.ToBoolean((Foo)null); //false

最后,这是我编写 Foo 类的方法,我认为这就足够了:

public class Foo
{
    public bool Result { get; set; }

    public static bool operator ==(Foo foo, Foo fooB)
    {
        return Equals(foo, fooB);
    }
    public static bool operator !=(Foo foo, Foo fooB)
    {
        return NotEquals(foo, fooB);
    }
    public static implicit operator bool(Foo foo)
    {
        return foo == null ? false : foo.Result;
    }

    private static bool Equals(Foo foo, Foo fooB)
    {
        if (object.Equals(foo, null))
        {
            return object.Equals(fooB, null);
        }

        if (object.Equals(fooB, null))
            return false;

        return foo.Result == fooB.Result;
    }
    private static bool NotEquals(Foo foo, Foo fooB)
    {
        return !Equals(foo, fooB);
    }
}

【讨论】:

  • 感谢您的回答,非常感谢。
  • 我强烈建议不要在静态隐式运算符 bool 块中使用 try-catch 块。只需检查 Foo 是否为 null,如果返回 false,否则返回 foo.Result
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-17
  • 1970-01-01
  • 1970-01-01
  • 2019-03-13
  • 1970-01-01
  • 2012-02-22
相关资源
最近更新 更多