Node.js может функционировать до некоторой степени без V8, благодаря использованию проекта node-chakracore . Продолжается работа по уменьшению тесной связи между V8 и Node, чтобы можно было использовать различные механизмы JavaScript на месте.
Мне всегда было проще написать статический оператор с нулевой обработкой и заставить переопределение Equals вызывать перегруженный оператор с помощью "this" в качестве одного из параметров.
Из Указания по перегрузке Equals () и Operator == (Руководство по программированию C #)
//add this code to class ThreeDPoint as defined previously
//
public static bool operator ==(ThreeDPoint a, ThreeDPoint b)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
// Return true if the fields match:
return a.x == b.x && a.y == b.y && a.z == b.z;
}
public static bool operator !=(ThreeDPoint a, ThreeDPoint b)
{
return !(a == b);
}
Вот как ReSharper создает операторы равенства и реализует IEquatable
, чему я, конечно, слепо доверяю; -)
public class ClauseBE : IEquatable<ClauseBE>
{
private int _id;
public bool Equals(ClauseBE other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return other._id == this._id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
if (obj.GetType() != typeof(ClauseBE))
return false;
return Equals((ClauseBE)obj);
}
public override int GetHashCode()
{
return this._id.GetHashCode();
}
public static bool operator ==(ClauseBE left, ClauseBE right)
{
return Equals(left, right);
}
public static bool operator !=(ClauseBE left, ClauseBE right)
{
return !Equals(left, right);
}
}
public class Foo : IEquatable<Foo>
{
public Int32 Id { get; set; }
public override Int32 GetHashCode()
{
return this.Id.GetHashCode();
}
public override Boolean Equals(Object obj)
{
return !Object.ReferenceEquals(obj as Foo, null)
&& (this.Id == ((Foo)obj).Id);
// Alternative casting to Object to use == operator.
return ((Object)(obj as Foo) != null) && (this.Id == ((Foo)obj).Id);
}
public static Boolean operator ==(Foo a, Foo b)
{
return Object.Equals(a, b);
}
public static Boolean operator !=(Foo a, Foo b)
{
return !Object.Equals(a, b);
}
public Boolean Equals(Foo other)
{
return Object.Equals(this, other);
}
}
Проверить наличие null и вернуть false. Equals всегда должно быть false, если один из операндов равен нулю;
Другие ответы дают хорошие решения общей проблемы.
Однако ваш собственный код может быть упрощен до относительно простого решения ...
Во-первых, в начале вашего оператора ==
у вас есть следующее:
// First test
if (a as object == null && b as object == null)
{
return true;
}
Это квалифицируется как "рабочий слишком сложно ».
Если ClauseBE
является ссылочным типом, то вам нужно только сравнить с null
-« как объект
»является избыточным; в равной степени, если ClauseBE
является типом значения, то он никогда не может быть нулевым
.
Предполагая, что ClauseBE
является ссылочным типом (наиболее вероятный случай), вы можете упростить это - обратите внимание, что мы используем Object.Equals ()
, чтобы избежать бесконечной рекурсии и выброс стека.
// First test
if (Object.Equals(a, null) && Object.Equals(b, null))
{
return true;
}
Один полезный ярлык - использовать объект . ReferenceEquals ()
- который обрабатывает значения NULL для вас.
Таким образом, вы можете написать вместо этого:
// First test
if (Object.ReferenceEquals(a, b))
{
return true;
}
с бонусом, который также обрабатывает случай, когда a
и b
] - это тот же самый объект.
Пройдя тест Object.ReferenceEquals ()
, вы узнаете, что a
и b
разные.
Итак, ваш следующий тест:
// Second test
if ((a as object == null && b as object != null)
|| (b as object == null && a as object != null))
{
return false;
}
можно упростить - поскольку вы знаете, что если a
имеет значение NULL, то b
не может иметь значение NULL и т. Д.
// Second test
if (Object.Equals(a, null) || Object.Equals(b, null))
{
return false;
}
Если этот тест не выполняется, то вы знаете, что a
и b
различны, и что ни один из них не является нулевым. Хорошее время для вызова вашего переопределенного Equals ()
.
// Use the implementation of Equals() for the rest
return a.Equals(b as object);
Я думаю, что это немного менее громоздко, чем преобразование в Object перед проверкой на null:
ReferenceEquals(a, null)
Я использовал следующий подход, и мне показалось, что он хорошо работает. Фактически, Resharper предлагает этот подход.
public bool Equals(Foo pFoo)
{
if (pFoo == null)
return false;
return (pFoo.Id == Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(obj, this))
return true;
return Equals(obj as Foo);
}
Я предпочитаю выполнять всю логику сравнения в методе Equals (T) и оставлять "if this or that is null, else ..." в перегрузках операторов для платформы.
Единственная сложность при переопределении перегрузок операторов заключается в том, что вы больше не можете использовать эти операторы в своей реализации Equals, например, для сравнения с null
. Вместо этого для достижения того же эффекта можно использовать object.ReferenceEquals
.
Следуя примеру TwoDPoint в статье MSDN Рекомендации по переопределению Equals () и Operator == , это шаблон, который я генерирую при реализации равенства значений для типов:
public override bool Equals( object obj ) {
// Note: For value types, would use:
// return obj is TwoDPoint && this.Equals( (TwoDPoint)obj );
return this.Equals( obj as TwoDPoint );
}
public bool Equals( TwoDPoint other ) {
// Note: null check not needed for value types.
return !object.ReferenceEquals( other, null )
&& EqualityComparer<int>.Default.Equals( this.X, other.X )
&& EqualityComparer<int>.Default.Equals( this.Y, other.Y );
}
public static bool operator ==( TwoDPoint left, TwoDPoint right ) {
// System.Collections.Generic.EqualityComparer<T> will perform the null checks
// on the operands, and will call the Equals overload if necessary.
return EqualityComparer<TwoDPoint>.Default.Equals( left, right );
}
public static bool operator !=( TwoDPoint left, TwoDPoint right ) {
return !EqualityComparer<TwoDPoint>.Default.Equals( left, right );
}
Форма выше - самая безопасная реализация, поскольку он просто перенаправляет проверки равенства полей в структуру и не требует знания о том, перегружают ли поля операторы равенства. Совершенно нормально упростить это там, где вы знаете, что существует перегрузка:
public bool Equals( TwoDPoint other ) {
return !object.ReferenceEquals( other, null )
&& this.X == other.X
&& this.Y == other.Y;
}
Вы также можете заменить вызовы EqualityComparer
в перегрузках операторов вызовами статического объекта .Equals
] при сравнении ссылочных типов или когда типы значений бокса не имеют значения:
public static bool operator ==( TwoDPoint left, TwoDPoint right ) {
return object.Equals( left, right );
}
public static bool operator !=( TwoDPoint left, TwoDPoint right ) {
return !object.Equals( left, right );
}
См. также Какой лучший алгоритм для переопределенного GetHashCode? для реализации GetHashCode
.