Создайте переменную, используя делегат, у которого нет установщика - PullRequest
0 голосов
/ 16 октября 2019

Я пытаюсь создать свойства var делегата с делегатом, который не предоставляет метод setValue(...). Другими словами, мне нужно свойство, которое я могу переназначить, но оно должно получить свое значение через делегат, если оно не было переназначено.

Я использую библиотеку синтаксического анализатора CLI xenomachina , в которой используются делегаты. Это работает хорошо, пока у меня есть val свойства. В некоторых случаях мне нужно иметь возможность динамически изменять эти свойства во время выполнения, хотя для этого требуется изменяемый var. Я не могу просто использовать var здесь, так как библиотека не предоставляет в своем делегате метод setValue(...), отвечающий за разбор аргументов.

В идеале мне бы хотелось что-то вроде этого:

class Foo(parser: ArgParser) {
    var myParameter by parser.flagging(
        "--my-param",
        help = "helptext"
    )
}

, который не работает из-за отсутствия сеттера.

До сих пор я пытался расширить класс Delegate с помощью функции расширения сеттера, но внутренне он также использует val, поэтому я не могу это изменить. Я попытался обернуть делегата в другой, но когда я это сделаю, библиотека больше не распознает параметры, которые я обернула. Хотя, возможно, я что-то там упустил. Я не могу просто переназначить значение новой переменной следующим образом:

private val _myParameter by parser.flagging(...)
var myParameter = _myParameter

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

Как вы используете делегаты, которые не включают в себя установщик в сочетании со свойством var?

Ответы [ 2 ]

0 голосов
/ 16 октября 2019

Вы можете заключить своего делегата в класс, подобный следующему:

class DefaultDelegate<T>(private val default: Delegate<T>){
    private var _value: T? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T? = 
        _value?: default.value

    operator fun setValue(thisRef: Nothing?, property: KProperty<*>, value: T?) {
        _value = value
    }
}

Использование:

class Foo(parser: ArgParser) {
    var myParameter: Boolean? by DefaultDelegate(parser.flagging(
        "--my-param",
        help = "helptext"
    ))
}

Если вам нужна обнуляемость:

class DefaultDelegate<T>(private val default: Delegate<T>){
    private var modified = false
    private var _value: T? = null

    operator fun getValue(thisRef: Any?, property: KProperty<*>): T? = 
        if (modified) _value else default.value

    operator fun setValue(thisRef: Nothing?, property: KProperty<*>, value: T?) {
        _value = value
        modified = true
    }
}
0 голосов
/ 16 октября 2019

Вот как вы можете обернуть ReadOnlyProperty, чтобы заставить его работать так, как вы хотите:

class MutableProperty<in R, T>(
    // `(R, KProperty<*>) -> T` is accepted here instead of `ReadOnlyProperty<R, T>`,
    // to enable wrapping of properties which are based on extension function and don't
    // implement `ReadOnlyProperty<R, T>`
    wrapped: (R, KProperty<*>) -> T
) : ReadWriteProperty<R, T> {
    private var wrapped: ((R, KProperty<*>) -> T)? = wrapped // null when field is assigned
    private var field: T? = null

    @Suppress("UNCHECKED_CAST") // field is T if wrapped is null
    override fun getValue(thisRef: R, property: KProperty<*>) =
        if (wrapped == null) field as T
        else wrapped!!(thisRef, property)

    override fun setValue(thisRef: R, property: KProperty<*>, value: T) {
        field = value
        wrapped = null
    }
}

fun <R, T> ReadOnlyProperty<R, T>.toMutableProperty() = MutableProperty(this::getValue)

fun <R, T> ((R, KProperty<*>) -> T).toMutableProperty() = MutableProperty(this)

fun <T> ArgParser.Delegate<T>.toMutableProperty() = MutableProperty(this::getValue)

Варианты использования:

var lazyVar by lazy { 1 }::getValue.toMutableProperty()
var varParam by parser.flagging(
    "--my-param",
    help = "helptext"
).toMutableProperty()
...