Реализация равенства в.NET правильно, эффективно и без дублирования кода трудна. А именно, для ссылочных типов с семантикой значения (т.е. неизменные типы, которые рассматривают equvialence как равенство ), необходимо реализовать System.IEquatable
интерфейс , и необходимо реализовать все различные операции (Equals
, GetHashCode
и ==
, !=
).
Как пример, here’s класс, реализовывая равенство значения:
class Point : IEquatable {
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);
}
Я нашел решение, которым я очень доволен.
Шаг 1: Создайте реализацию PropertyEditorSupport для преобразования текста в / из соответствующего Enum
public class EnumEditor extends PropertyEditorSupport {
private Class<? extends Enum<?>> clazz
public EnumEditor(Class<? extends Enum<?>> clazz) {
this.clazz = clazz
}
public String getAsText() {
return value?.id
}
public void setAsText(String text) {
value = clazz.getEnumFromId(text)
}
}
Шаг 2: Определите класс, который регистрирует EnumEditor как преобразователь для различных классов перечисления. Чтобы изменить список классов перечисления, которые могут быть привязаны по идентификатору, просто измените BINDABLE_ENUMS
public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
private static final String REQUIRED_METHOD_NAME = 'getEnumFromId'
// Add any enums that you want to bind to by ID into this list
private static final BINDABLE_ENUMS = [Rating, SomeOtherEnum, SomeOtherEnum2]
public void registerCustomEditors(PropertyEditorRegistry registry) {
BINDABLE_ENUMS.each {enumClass ->
registerEnum(registry, enumClass)
}
}
/**
* Register an enum to be bound by ID from a request parameter
* @param registry Registry of types eligible for data binding
* @param enumClass Class of the enum
*/
private registerEnum(PropertyEditorRegistry registry, Class<? extends Enum<?>> enumClass) {
boolean hasRequiredMethod = enumClass.metaClass.methods.any {MetaMethod method ->
method.isStatic() && method.name == REQUIRED_METHOD_NAME && method.parameterTypes.size() == 1
}
if (!hasRequiredMethod) {
throw new MissingMethodException(REQUIRED_METHOD_NAME, enumClass, [String].toArray())
}
registry.registerCustomEditor(enumClass, new EnumEditor(enumClass))
}
}
Шаг 3: Сообщите Spring о приведенном выше реестре, определив следующий компонент Spring в grails-app / conf /spring/resources.grooovy
customPropertyEditorRegistrar(CustomPropertyEditorRegistrar)
Таким образом, привязка данных по умолчанию связывается на имя Enum, а не отдельно определяемое свойство Enum. Вы можете либо создать свой собственный PropertyEditor , как вы упомянули, либо выполнить обходной путь, подобный этому:
class MyCommand {
String ratingId
Rating getRating() {
return Rating.getEnumFromId(this.ratingId)
}
static constraints = {
ratingId(validator:{val, obj -> Rating.getEnumFromId(val) != null })
}
}