Неявное (bool) и == переопределение оператора - обрабатывать, если утверждения правильно

У меня есть собственный класс с реализацией как ==, так и 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())

Ответ 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

Компилятор будет использовать неявный преобразователь типов, чтобы получить значение bool от Foo, а затем использовать стандартный оператор равенства для 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 для каждого теста. Поэтому этот упрощенный код должен удовлетворять вашим потребностям, если я правильно их понял.

UPDATE

Чтобы оператор равенства работал для экземпляров Foo (который может быть нулевым):

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

Я думаю, что вы явно охватили все базы вашего кода; если необходимо, вы должны учитывать порядок параметров для оператора, поэтому, если вы хотите, чтобы ваша функция Equals вызывалась для обоих параметров порядка, вы сделали правильно.

Однако при сравнении Foo с bool он выглядит немного переполненным, поскольку вы можете просто полагаться на неявное преобразование. Это позволит вам удалить все операторы между этими двумя типами, а также методы Equals и NotEquals.

Что еще, это позволит избежать некоторой несогласованности вашего кода в отношении преобразования нулевого Foo в boolean. Когда вы передадите null Foo в метод Equals, он вернет true, тогда как при неявном преобразовании null 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);
    }
}