使用没有 setter 的委托创建一个 var
Create a var using a delegate that does not have a setter
我正在尝试使用不提供 setValue(...)
方法的委托来创建委托 var 属性。换句话说,我需要一个可以重新分配的 属性,但只要它没有被重新分配,它就应该通过委托获得它的值。
我正在使用 xenomachina CLI arguments parser 库,它使用委托。只要我有 val
属性,它就可以正常工作。但是,在某些情况下,我需要能够在运行时动态更改这些属性,这需要一个可变的 var
。我不能在这里简单地使用 var
,因为库没有在其负责参数解析的委托中提供 setValue(...)
方法。
理想情况下,我想要这样的东西:
class Foo(parser: ArgParser) {
var myParameter by parser.flagging(
"--my-param",
help = "helptext"
)
}
由于缺少 setter.
而无法使用
到目前为止,我已经尝试使用 setter 扩展函数来扩展 Delegate
class,但在内部它也使用了 val
,所以我可以'不要改变那个。我已经尝试将委托包装到另一个委托中,但是当我这样做时,库不再识别我包装的选项。虽然我可能在那里错过了一些东西。
我不能按如下方式将值重新分配给新的变量:
private val _myParameter by parser.flagging(...)
var myParameter = _myParameter
因为这似乎使解析器感到困惑,并且一旦访问第一个委托 属性,它就会停止计算其余参数。再说也不是特别好看
如何使用不包含 setter 和 var
属性 的代表?
以下是包装 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)
用例:
var lazyVar by lazy { 1 }::getValue.toMutableProperty()
下面是包装 属性 委托提供程序的方法:
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
}
用例:
var flagging by parser.flagging(
"--my-param",
help = "helptext"
).toMutableProvider()
您可以像这样用 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
}
}
用法:
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
}
}
我正在尝试使用不提供 setValue(...)
方法的委托来创建委托 var 属性。换句话说,我需要一个可以重新分配的 属性,但只要它没有被重新分配,它就应该通过委托获得它的值。
我正在使用 xenomachina CLI arguments parser 库,它使用委托。只要我有 val
属性,它就可以正常工作。但是,在某些情况下,我需要能够在运行时动态更改这些属性,这需要一个可变的 var
。我不能在这里简单地使用 var
,因为库没有在其负责参数解析的委托中提供 setValue(...)
方法。
理想情况下,我想要这样的东西:
class Foo(parser: ArgParser) {
var myParameter by parser.flagging(
"--my-param",
help = "helptext"
)
}
由于缺少 setter.
而无法使用到目前为止,我已经尝试使用 setter 扩展函数来扩展 Delegate
class,但在内部它也使用了 val
,所以我可以'不要改变那个。我已经尝试将委托包装到另一个委托中,但是当我这样做时,库不再识别我包装的选项。虽然我可能在那里错过了一些东西。
我不能按如下方式将值重新分配给新的变量:
private val _myParameter by parser.flagging(...)
var myParameter = _myParameter
因为这似乎使解析器感到困惑,并且一旦访问第一个委托 属性,它就会停止计算其余参数。再说也不是特别好看
如何使用不包含 setter 和 var
属性 的代表?
以下是包装 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)
用例:
var lazyVar by lazy { 1 }::getValue.toMutableProperty()
下面是包装 属性 委托提供程序的方法:
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
}
用例:
var flagging by parser.flagging(
"--my-param",
help = "helptext"
).toMutableProvider()
您可以像这样用 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
}
}
用法:
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
}
}