Grails связывает параметры запроса с перечислением

Реализация равенства в.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);
}

11
задан Community 23 May 2017 в 12:15
поделиться

2 ответа

Я нашел решение, которым я очень доволен.

Шаг 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)
12
ответ дан 3 December 2019 в 04:52
поделиться

Таким образом, привязка данных по умолчанию связывается на имя Enum, а не отдельно определяемое свойство Enum. Вы можете либо создать свой собственный PropertyEditor , как вы упомянули, либо выполнить обходной путь, подобный этому:

class MyCommand {
 String ratingId
    Rating getRating() {
     return Rating.getEnumFromId(this.ratingId)
    }
    static constraints = {
     ratingId(validator:{val, obj -> Rating.getEnumFromId(val) != null })
    }
}
11
ответ дан 3 December 2019 в 04:52
поделиться
Другие вопросы по тегам:

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