简体   繁体   English

使用没有 setter 的委托创建 var

[英]Create a var using a delegate that does not have a setter

I am trying to create delegate var properties with a delegate that does not provide a setValue(...) method.我正在尝试使用不提供setValue(...)方法的委托创建委托 var 属性。 In other words, I need a property that I can reassign but that should get its value via the delegate as long as it hasn't been reassigned.换句话说,我需要一个可以重新分配的属性,但只要它没有被重新分配,它就应该通过委托获取它的值。

I am using the xenomachina CLI arguments parser library, which uses delegates.我正在使用使用委托的xenomachina CLI arguments 解析器库。 This works well as long as I have val properties.只要我有val属性,这就会很好地工作。 In some cases I need to be able to change those properties dynamically at runtime, though, requiring a mutable var .在某些情况下,我需要能够在运行时动态更改这些属性,但需要一个可变的var I can't simply use a var here, as the library does not provide a setValue(...) method in its delegate responsible for the argument parsing.我不能在这里简单地使用var ,因为该库在其负责参数解析的委托中没有提供setValue(...)方法。

Ideally, I'd like something like this:理想情况下,我想要这样的东西:

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

which doesn't work due to the missing setter.由于缺少二传手,这不起作用。

So far, I've tried extending the Delegate class with a setter extension function, but internally it also uses a val , so I can't change that.到目前为止,我已经尝试使用 setter 扩展 function 扩展Delegate class,但在内部它也使用val ,所以我无法更改。 I've tried wrapping the delegate into another delegate but when I do that then the library doesn't recognize the options I've wrapped anymore.我尝试将委托包装到另一个委托中,但是当我这样做时,库将无法识别我已经包装的选项。 Although I may have missed something there.虽然我可能在那里错过了一些东西。 I can't just re-assign the value to a new var as follows:我不能只是将值重新分配给新的 var,如下所示:

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

since that seems to confuse the parser and it stops evaluating the rest of the parameters as soon as the first delegate property is accessed.因为这似乎使解析器感到困惑,并且一旦访问第一个委托属性,它就会停止评估参数的 rest。 Besides, it is not particularly pretty.此外,它不是特别漂亮。

How do you use delegates that don't include a setter in combination with a var property?您如何使用不包含 setter 和var属性的委托?

Here is how you can wrap a ReadOnlyProperty to make it work the way you want:以下是如何包装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)

Use case:用例:

var lazyVar by lazy { 1 }::getValue.toMutableProperty()

And here is how you can wrap a property delegate provider:以下是包装属性委托提供者的方法:

class MutableProvider<in R, T>(
    private val provider: (R, KProperty<*>) -> (R, KProperty<*>) -> T
) {
    operator fun provideDelegate(thisRef: R, prop: KProperty<*>): MutableProperty<R, T> =
        provider(thisRef, prop).toMutableProperty()
}

fun <T> ArgParser.Delegate<T>.toMutableProvider() = MutableProvider { thisRef: Any?, prop ->
    provideDelegate(thisRef, prop)::getValue
}

Use case:用例:

var flagging by parser.flagging(
    "--my-param",
    help = "helptext"
).toMutableProvider()

You could wrap your delegate with a class like this:您可以使用 class 包装您的委托,如下所示:

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
    }
}

Usage:用法:

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

If you need nullability:如果您需要可空性:

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
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM