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