Что такое “Лучшая практика” Для Сравнения Двух Экземпляров Ссылочного типа?

Исключение нулевого указателя генерируется, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  1. Вызов метода экземпляра объекта null.
  2. Доступ или изменение поля объекта null.
  3. Принимая длину null, как если бы это был массив.
  4. Доступ или изменение слотов null, как если бы это был массив.
  5. Бросок null как будто это было значение Throwable.

Приложения должны бросать экземпляры этого класса, чтобы указать на другие незаконные использования объекта null.

Ссылка: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

45
задан ΩmegaMan 28 December 2012 в 15:23
поделиться

6 ответов

Похоже, что Вы кодируете в C#, которому назвали метод, Равняется тому Вашему классу, должен реализовать, необходимо ли хотеть выдержать сравнение, два объекта с помощью некоторой другой метрики, чем "являются этими двумя указателями (потому что объектные дескрипторы просто что, указатели) к тому же адресу памяти?".

я захватил некоторый пример кода от здесь :

class TwoDPoint : System.Object
{
    public readonly int x, y;

    public TwoDPoint(int x, int y)  //constructor
    {
        this.x = x;
        this.y = y;
    }

    public override bool Equals(System.Object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to Point return false.
        TwoDPoint p = obj as TwoDPoint;
        if ((System.Object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (x == p.x) && (y == p.y);
    }

    public bool Equals(TwoDPoint p)
    {
        // If parameter is null return false:
        if ((object)p == null)
        {
            return false;
        }

        // Return true if the fields match:
        return (x == p.x) && (y == p.y);
    }

    public override int GetHashCode()
    {
        return x ^ y;
    }
}

Java имеет очень похожие механизмы. Эти равняется () , метод является частью Объект класс, и Ваш класс перегружает его, если Вы хотите этот тип функциональности.

причиной, перегружающейся '==', может быть плохая идея для объектов, то, что, обычно, Вы все еще хотите быть в состоянии сделать, "они тот же указатель" сравнения. Для них обычно полагаются, например, вставляя элемент в список, где никакие дубликаты не позволяются, и часть Вашего материала платформы не может работать, если этот оператор перегружается нестандартным способом.

23
ответ дан Matt J 26 November 2019 в 21:21
поделиться

Реализация равенства в.NET правильно, эффективно и без дублирования кода трудна. А именно, для ссылочных типов с семантикой значения (т.е. неизменные типы, которые рассматривают equvialence как равенство ), необходимо реализовать System.IEquatable<T> интерфейс , и необходимо реализовать все различные операции (Equals, GetHashCode и ==, !=).

Как пример, here’s класс, реализовывая равенство значения:

class Point : IEquatable<Point> {
    public int X { get; }
    public int Y { get; }

    public Point(int x = 0, int y = 0) { X = x; Y = y; }

    public bool Equals(Point other) {
        if (other is null) return false;
        return X.Equals(other.X) && Y.Equals(other.Y);
    }

    public override bool Equals(object obj) => Equals(obj as Point);

    public static bool operator ==(Point lhs, Point rhs) => object.Equals(lhs, rhs);

    public static bool operator !=(Point lhs, Point rhs) => ! (lhs == rhs);

    public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode();
}

единственные движущиеся детали в вышеупомянутом коде являются полужирными частями: вторая строка в Equals(Point other) и GetHashCode() метод. Другой код должен остаться неизменным.

Для классов ссылки, которые не представляют неизменные значения, не реализуйте операторы == и !=. Вместо этого используйте их значение значения по умолчанию, которое должно сравнить объектные идентификационные данные.

код намеренно приравнивает даже объекты типа производного класса. Часто, это не могло бы быть желательно, потому что равенство между базовым классом и производными классами не четко определено. К сожалению.NET и инструкции по кодированию не очень ясны здесь. Код, который Resharper создает, отправленный в другом ответе , восприимчив к нежелательному поведению в таких случаях, потому что Equals(object x) и Equals(SecurableResourcePermission x) будет обработка этот случай по-другому.

для изменения этого поведения, дополнительная проверка типа должна быть вставлена в со строгим контролем типов Equals метод выше:

public bool Equals(Point other) {
    if (other is null) return false;
    if (other.GetType() != GetType()) return false;
    return X.Equals(other.X) && Y.Equals(other.Y);
}
26
ответ дан Konrad Rudolph 26 November 2019 в 21:21
поделиться

Та статья просто рекомендует против переопределения оператора равенства (для ссылочных типов), не против переопределения Равняется. Необходимо переопределить, Равняется в объекте (ссылка или значение), если проверки равенства будут означать что-то большее чем проверки ссылки. Если Вы хотите интерфейс, можно также реализовать IEquatable (используемый универсальными наборами). При реализации IEquatable, однако, необходимо также переопределить, равняется, как состояния раздела комментариев IEquatable:

, Если Вы реализуете IEquatable< T>, необходимо также переопределить реализации базового класса Объекта. Равняется (Объект) и GetHashCode так, чтобы их поведение согласовывалось с тем из IEquatable< T>.Equals метод. Если Вы действительно переопределяете Объект. Равняется (Объект), Вашу переопределенную реализацию также называют в вызовах к помехам, Равняется (Система. Объект, Система. Объект) метод на Вашем классе. Это гарантирует, что все вызовы Равняются последовательным результатам возврата метода.

В отношении того, необходимо ли реализовать, Равняется и/или оператор равенства:

От Реализация Равняется Методу

, наиболее ссылочные типы не должны перегружать оператор равенства, даже если они переопределяют, Равняется.

От Инструкции для Реализации Равняются и Оператор равенства (==)

, Переопределение Равняется методу каждый раз, когда Вы реализуете оператор равенства (==) и заставляете их сделать то же самое.

Это только говорит, что необходимо переопределить, Равняется каждый раз, когда Вы реализуете оператор равенства. Это делает не , говорят, что необходимо переопределить оператор равенства, когда Вы переопределяете, Равняется.

3
ответ дан bdukes 26 November 2019 в 21:21
поделиться

Для сложных объектов, которые приведут к определенным сравнениям, затем реализовывая IComparable и определяя сравнение в Сравнить методах, хорошая реализация.

, Например, у нас есть объекты "Механизма", где единственной разницей может быть регистрационный номер, и мы используем это для сравнения, чтобы гарантировать, что математическое ожидание, возвращенное в тестировании, является тем, которое мы хотим.

2
ответ дан Paul Shannon 26 November 2019 в 21:21
поделиться

Я склонен использовать то, что автоматически делает Resharper. например, это автосоздало это для одного из моих ссылочных типов:

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    return obj.GetType() == typeof(SecurableResourcePermission) && Equals((SecurableResourcePermission)obj);
}

public bool Equals(SecurableResourcePermission obj)
{
    if (ReferenceEquals(null, obj)) return false;
    if (ReferenceEquals(this, obj)) return true;
    return obj.ResourceUid == ResourceUid && Equals(obj.ActionCode, ActionCode) && Equals(obj.AllowDeny, AllowDeny);
}

public override int GetHashCode()
{
    unchecked
    {
        int result = (int)ResourceUid;
        result = (result * 397) ^ (ActionCode != null ? ActionCode.GetHashCode() : 0);
        result = (result * 397) ^ AllowDeny.GetHashCode();
        return result;
    }
}

, Если Вы хотите переопределить == и все еще сделать касательно проверок, можно все еще использовать Object.ReferenceEquals.

1
ответ дан nawfal 26 November 2019 в 21:21
поделиться

Вы ловите исключения, которые вы хотите затем отфильтровать более тщательно, чтобы затем вы могли передумать, решив не справиться с ними и отбросить их?

Если вы хотите быть очень осторожным с этим, это не очень хорошая идея. Лучше никогда не ловить исключение в первую очередь. Причина в том, что данный обработчик try / catch не должен принимать решение о запуске вложенных блоков finally для исключений, которые он не ожидает увидеть. Например, если существует NullReferenceException , вероятно, очень плохая идея продолжать выполнение любого кода, поскольку это, вероятно, вызовет другое подобное исключение. И, наконец, блоки - это просто код, как и любой другой код. Как только исключение поймано впервые, любые блоки finally в стеке под try / catch будут выполнены, к тому времени, когда будет слишком поздно - может быть сгенерировано другое исключение, и это означает, что исходное исключение потеряно.

Это подразумевает (в C #), что вы должны тщательно выписать отдельный обработчик catch для всех типов исключений, которые вы хотите перехватить. Это также означает, что вы можете фильтровать только по типу исключения. Иногда это очень трудный совет для подражания.

Должна быть возможность фильтровать исключения другими способами, но в C # это не так. Тем не менее, это возможно в VB.NET, и сам BCL использует это, имея небольшой код, написанный на VB.NET, так что он может фильтровать исключения более удобным способом.

Вот подробное объяснение, с пример кода VB.NET из блога команды CLR.

Событие начинается и заканчивается в течение недели.

Событие начинается в течение недели.

Событие начинается до недели, но заканчивается в течение недели.

Неделя начинается во время события.

Событие начинается в течение недели. неделю, но заканчивается после недели

Событие начинается в течение недели.

Событие начинается до недели, а также заканчивается после недели

Неделя начинается во время события.

Обратите внимание, что МЕЖДУ в вышеприведенных выражениях используется просто для краткости.

Строгое выражение выглядит следующим образом:

(event.start >= week.start AND event.start < week.end)
OR
(week.start >= event.start AND week.start < event.end)

, при условии, что week.end является week.start + INTERVAL 7 ДЕНЬ .

I. е. если ваша неделя начинается с вс, 0:00:00 , то она должна заканчиваться на следующее солнце, 0:00:00 (не на сб, 0:00: 00 )

Это выражение выглядит более сложным, чем обычно используемое:

event.start < week.end AND event.end > week.start

, = для ValueTypes и неизменных ссылочных типов.


Обоснование

IEquatable

Интерфейс System.IEquatable используется для сравнения двух экземпляров объекта на равенство. Объекты сравниваются на основе логики, реализованной в классе. Сравнение приводит к логическому значению, указывающему, отличаются ли объекты. Это отличается от интерфейса System.IComparable, который возвращает целое число, указывающее, как отличаются значения объекта.

Интерфейс IEquatable объявляет два метода, которые должны быть переопределены. Метод Equals содержит реализацию, которая выполняет фактическое сравнение и возвращает true, если значения объекта равны, или false, если они не равны. Метод GetHashCode должен возвращать уникальное хеш-значение, которое можно использовать для уникальной идентификации идентичных объектов, которые содержат разные значения. Тип используемого алгоритма хеширования зависит от реализации.

Метод IEquatable.Equals

  • Вы должны реализовать IEquatable для ваших объектов, чтобы обрабатывать вероятность того, что они будут храниться в массиве или универсальной коллекции.
  • Если вы реализуете IEquatable, вам также следует переопределить реализации базового класса Object .Equals (Object) и GetHashCode, чтобы их поведение соответствовало поведению метода IEquatable.Equals

Рекомендации по переопределению Equals () и Operator == (Руководство по программированию в C #)

  • x.Equals (x) возвращает true .
  • x.Equals (y) возвращает то же значение, что и y.Equals (x)
  • , если (x.Equals (y) && y.Equals (z)) возвращает true, тогда x.Equals (z) возвращает true.
  • Последовательные вызовы x. Равные (y) возвращают одно и то же значение, если объекты, на которые ссылаются x и y, не изменены.
  • x. Equals (null) возвращает false (только для типов значений, не допускающих значения NULL. Для получения дополнительной информации см. Nullable Types (Руководство по программированию в C #) .)
  • Новая реализация Equals не должна генерировать исключения.
  • Рекомендуется, чтобы любой класс, который переопределяет Equals, также переопределял Object.GetHashCode.
  • Рекомендуется, чтобы в дополнение к реализации Equals (объект), любой класс также реализовывал Equals (тип) для своего собственного типа, чтобы повысить производительность.

По умолчанию оператор == проверяет равенство ссылок, определяя, указывают ли две ссылки на один и тот же объект. Следовательно, ссылочные типы не должны реализовывать оператор == для получения этой функциональности. Когда тип является неизменным, то есть данные, содержащиеся в экземпляре, не могут быть изменены, Оператор перегрузки == для сравнения равенства значений вместо ссылочного равенства может быть полезен, поскольку в качестве неизменяемых объектов их можно считать одинаковыми, если они имеют одинаковое значение. Не рекомендуется переопределять оператор == в неизменяемых типах.

  • Перегруженный оператор == реализации не должны вызывать исключения.
  • Любой тип, перегружающий оператор ==, также должен перегружать оператор! =.

== Оператор (C # Reference)

  • Для предопределенных типов значений оператор равенства (==) возвращает true, если значения его операндов равны, в противном случае - false.
  • Для ссылочных типов, кроме строки, = = возвращает true, если два его операнда ссылаются на один и тот же объект.
  • Для строкового типа == сравнивает значения строк.
  • При проверке на нулевое значение с использованием == сравнений в вашем операторе == переопределяет, убедитесь, что вы используете оператор базового класса объекта. Если вы этого не сделаете, произойдет бесконечная рекурсия, что приведет к переполнению стека.

Метод Object.Equals (Object)

Если ваш язык программирования поддерживает перегрузку операторов и если вы решите перегрузить оператор равенства для данного типа, то Тип должен переопределить метод Equals. Такие реализации метода Equals должны возвращать те же результаты, что и оператор равенства

Следующие рекомендации предназначены для реализации типа значения :

  • Рассмотрите возможность переопределения Equals для повышения производительности по сравнению с тем, который предоставляется по умолчанию реализация Equals для ValueType.
  • Если вы переопределяете Equals и язык поддерживает перегрузку операторов, вы должны перегрузить оператор равенства для вашего типа значения.

Следующие рекомендации предназначены для реализации ссылочного типа :

  • Рассмотрим переопределение Equals для ссылочного типа, если семантика типа основана на том факте, что тип представляет некоторое значение (я).
  • Большинство ссылочных типов не должны перегружать оператор равенства, даже если они переопределяют Equals. Однако, если вы реализуете ссылочный тип, который должен иметь семантику значений, например тип комплексного числа, вы должны переопределить оператор равенства.

Дополнительные ошибки

16
ответ дан Community 26 November 2019 в 21:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: