精华内容
下载资源
问答
  • 主要为大家详细介绍了C#中委托(Delegates)的使用方法,感兴趣的朋友可以参考一下
  • 介绍 在 C++ 中通过一个全局函数来绑定到对象的成员函数是很有用的,这个特性也存在于其他语言中,例如 C#的委派。在 C++ 中相当于成员函数指针,但是 并没有提供相应的特性。在这篇文章中,我想提出一个简单的 C++ ...
  • 委托,是一种比较常见的设计模式,通常采用接口或者抽象类的方式来实现,在Java代码中,一般使用接口来进行封装,而在kotlin中,可以通过委托机制来实现更加方便的委托模式。Kotlin中...

    委托,是一种比较常见的设计模式,通常采用接口或者抽象类的方式来实现,在Java代码中,一般使用接口来进行封装,而在kotlin中,可以通过委托机制来实现更加方便的委托模式。

    Kotlin中的委托分为两种——类委托与属性委托,其中属性委托,是Kotlin非常强大的一个语法糖,借助这个功能,我们可以消除很多重复的模板代码,将Kotlin的代码榨干到极致。

    类委托

    下面我们先通过一个简单的例子来了解下什么是类委托,以及类委托的具体作用。

    类委托入门

    在一般的业务开发中,我们经常会遇到这样的场景——一个业务功能,有多种实现,通过接口来封装具体的业务方法,通过实现接口来完成不同实现,这样的场景有很多,使用Kotlin来实现这一功能,步骤如下。

    第一步:创建接口约束,抽象业务场景。例如下面这个数据持久化的例子,我们通过接口定义了三个数据操作方法。

    interface IDataPersistence {
        fun addData()
        fun delData()
        fun queryData()
    }
    

    第二步:创建委托的实现,实现约束接口。数据持久化有多种不同的实现方式,下面这就是简单的两种,一种是通过SQL进行持久化,另一种是通过SharedPreferences进行持久化。

    class SQL : IDataPersistence {
        override fun addData() {
            Log.d("xys", "addData with SQL")
        }
    
        override fun delData() {
            Log.d("xys", "delData with SQL")
        }
    
        override fun queryData() {
            Log.d("xys", "queryData with SQL")
        }
    }
    
    class SharedPreferences : IDataPersistence {
        override fun addData() {
            Log.d("xys", "addData with SharedPreferences")
        }
    
        override fun delData() {
            Log.d("xys", "delData with SharedPreferences")
        }
    
        override fun queryData() {
            Log.d("xys", "queryData with SharedPreferences")
        }
    }
    

    第三步:调用约束接口,即业务方调用,但不用考虑具体的实现。类委托的语法格式是,<类>:<约束接口> by <实现类的实例>,即通过by关键字,将接口的实现,委托给一个具体的实例来作为自己的实现。

    class MyDB(private val delegate: IDataPersistence) : IDataPersistence by delegate
    

    使用方式与Java代码通过接口来实现基本一致,即在类初始化的时候,传入具体的实现类即可。

    // val myDB = MyDB(SQL())
    val myDB = MyDB(SharedPreferences())
    myDB.addData()
    myDB.delData()
    myDB.queryData()
    

    在Kotlin的类委托机制中,调用方和业务实现方,都需要实现约束接口,调用方只需要传入不同类型的业务实现方式,即可通过约束调用具体的实现。这一点看上去好像并没有比Java方便多少,但是在Kotlin中,在某些简单的场景下,实际上是可以省略掉实现类的,直接通过对委托实现的重写来实现委托接口,代码如下所示。

    class MyDB(private val delegate: IDataPersistence) : IDataPersistence by delegate {
        override fun addData() {}
    
        override fun delData() {}
    
        override fun queryData() {}
    }
    

    再简单一点,如果你不用传入多种不同的实例,可以在构造方法中去掉默认参数,直接在by关键字后面添加具体的接口实现,还是上面的例子,代码如下所示。

    class MyDB : IDataPersistence by SQL()
    
    调用:
    MyDB().addData()
    

    通过委托,可以在不影响继承(MyDB可以继承其它类)的情况下,通过委托,使用指定接口中的方法。

    类委托的原理

    通过反编译Kotlin实现的代码,可以很方便的了解Kotlin内部是如何通过Java代码来实现委托机制的。


    实际上就是在调用者内部创建一个实现者的变量,在实现的接口方法中,变量调用该方法,从而实现调用,一切都只是语法糖而已,Kotlin帮你简化了代码。

    类委托的使用场景

    通过类委托机制,可以很方便的实现多态。这是类委托最重要的使用场景,通过接口定义来实现多态性,同时使用by关键字来简化Java中接口实现的冗余代码,下面的这个简单的例子,就是一个最好的说明。

    class RedSquare : Shape by Square(), Color by Red() {
        fun draw() {
            print("draw Square with Red")
        }
    }
    

    另外,委托还可以用于在不修改原来代码及架构的基础上,对原有功能扩展或者修改。例如我们要对MutableList类拓展一个函数,如果是Java代码,或者不使用委托的Kotlin代码,你必须实现List接口中的所有函数,虽然你未作修改,只是单纯的传递调用,但是需要为这个拓展写很多无用的代码,而使用委托,则完全不用处理这些冗余,代码如下所示。

    class NewList(private val list: MutableList<String>) : MutableList<String> by list {
        fun newFunction() {}
    }
    

    Kotlin会自动在编译时帮你添加其它接口方法的默认实现。

    属性委托

    属性委托指的是一个类的某个属性值不是在类中直接进行定义,而是将其委托给一个代理类,从而实现对该类的属性统一管理,属性委托的一般格式如下所示。

    val/var <属性名>: <类型> by <表达式>
    

    在前面的讲解中,类委托,委托的是接口中指定的方法,而属性委托,则委托的是属性的get、set方法,属性委托实际上就是将get、set方法的逻辑委托给一个单独的类来进行实现(对于val属性来说,委托的是getValue方法,对于var属性来说,委托的是setValue和getValue方法)。

    属性委托在那些需要对属性的get、set方法复用逻辑的场景下,是非常方便的,下面通过一个简单的例子来演示下属性委托机制。

    首先,我们定义一个var属性,并将其委托给MyDelegate类,即将get和set方法进行了交接托管,因此,MyDelegate类需要重写getValue和setValue方法,为其提供新的返回值和逻辑,代码如下所示。

    var delegateProp by MyDelegate()
    
    class MyDelegate {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
            return "MyDelegate get $thisRef ${property.name}"
        }
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            Log.d("xys", "MyDelegate set $value $thisRef ${property.name}")
        }
    }
    
    调用:
    Log.d("xys", delegateProp)
    delegateProp = "abc"
    
    out:
    com.yw.demo D/xys: MyDelegate get com.yw.demo.MainActivity@595c528 delegateProp
    com.yw.demo D/xys: MyDelegate set abc com.yw.demo.MainActivity@595c528 delegateProp
    

    这样处理之后,我们在使用delegateProp这个属性的时候,就会自动拓展MyDelegate中的处理。

    不过呢,这样写起来太麻烦,MyDelegate中的方法都需要手动来实现,所以Kotlin提供了两个接口来帮助开发者实现。


    所以上面的代码可以简写成下面这样。

    class MyDelegate : ReadWriteProperty<Any, String> {
        override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
            Log.d("xys", "MyDelegate set $value $thisRef ${property.name}")
        }
    
        override fun getValue(thisRef: Any, property: KProperty<*>): String {
            return "MyDelegate get $thisRef ${property.name}"
        }
    }
    

    属性委托使用场景

    那么这东西有什么用呢,下面举个例子。

    逻辑封装

    例如对参数进行encode的操作。

    object Prop {
        var encodeProp: String by EncodeProperty("init")
    }
    
    class EncodeProperty(var value: String) : ReadWriteProperty<Prop, String> {
        override fun getValue(thisRef: Prop, property: KProperty<*>): String {
            return "get encode prop output $value"
        }
    
        override fun setValue(thisRef: Prop, property: KProperty<*>, value: String) {
            this.value = value
            Log.d("xys", "save encode prop $value")
        }
    }
    
    调用:
    Prop.encodeProp = "xuyisheng"
    Log.d("xys", Prop.encodeProp)
    

    参数依然是那个参数变量,但是对它的处理被外包出去,交给了EncodeProperty来进行处理,这里的实现就是业务需要的encode操作,将来如果encode操作有改动,那么只需要修改EncodeProperty即可。也就是说,我们将encode的具体逻辑进行了封装,这样便于拓展和维护。

    消除模板代码

    再来看下面这个例子,Person类中有两个属性,分别修改了set方法,用于添加一些逻辑,代码如下所示。

    class Person {
        var firstName: String = ""
            set(value) {
                field = value.toLowerCase()
            }
        var lastname: String = ""
            set(value) {
                field = value.toLowerCase()
            }
    }
    
    调用:
    val person = Person()
    person.firstName = "XU"
    person.lastname = "YISHENG"
    println("${person.firstName} ${person.lastname}")
    

    但是这里的两个属性的set方法,要处理的逻辑基本是一样的,即对字母做小写,所以我们对这个操作进行抽取,设置一个委托,代码如下所示。

    class FormatDelegate : ReadWriteProperty<Any?, String> {
        private var formattedString: String = ""
    
        override fun getValue(thisRef: Any?, property: KProperty<*>): String {
            return formattedString
        }
    
        override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            formattedString = value.toLowerCase()
        }
    }
    

    这个委托做的事情,和在前面的代码中set的逻辑是一样的。那么这个时候,就可以对Person类进行改造了,代码如下所示。

    class Person {
        var firstName: String by FormatDelegate()
        var lastname: String by FormatDelegate()
    }
    

    这样就将同样的set操作的逻辑,封装在了FormatDelegate中,从而实现了模板代码的消除。

    抽象属性委托的一般步骤

    从上面的例子我们可以发现,其实只要是对属性的get、set方法有操作的地方,几乎都可以使用属性委托来简化,对于这种操作,开发者一般会经历下面几个过程。

    • 青铜:抽取公共函数,在处理时对属性进行调用

    • 黄金:重新属性的get、set函数,将逻辑封装

    • 王者:使用属性委托,将逻辑抽取出来

    下面再通过一个实例,来演示下这个步骤。我们以Fragment的启动方式为例来讲解,经常有写类似的代码来处理Fragment的启动。

    const val PARAM1 = "param1"
    const val PARAM2 = "param2"
    
    class DemoFragment : Fragment() {
        private var param1: Int? = null
        private var param2: String? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            arguments?.let { args ->
                param1 = args.getInt(PARAM1)
                param2 = args.getString(PARAM2)
            }
        }
    
        companion object {
            fun newInstance(param1: Int, param2: String): DemoFragment =
                DemoFragment().apply {
                    arguments = Bundle().apply {
                        putInt(PARAM1, param1)
                        putString(PARAM2, param2)
                    }
                }
        }
    }
    

    首先,我们可以通过Kotlin的set、get函数进行改写,将arguments的填充,放到属性的get、set函数内部,代码如下所示。

    class DemoFragment : Fragment() {
        private var param1: Int?
            get() = arguments?.getInt(PARAM1)
            set(value) {
                value?.let {
                    arguments?.putInt(PARAM1, it)
                }
            }
    
        private var param2: String?
            get() = arguments?.getString(PARAM2)
            set(value) {
                arguments?.putString(PARAM2, value)
            }
    
        companion object {
            fun newInstance(param1: Int, param2: String): DemoFragment =
                DemoFragment().apply {
                    this.param1 = param1
                    this.param2 = param2
                }
        }
    }
    

    但是我们还是要为每个属性写重复的代码,特别是当属性很多的时候,每个属性都要写重复的put、get函数,所以,下面使用委托对这个逻辑再进行一次封装,代码如下所示。

    class DemoFragment : Fragment() {
        private var param1: Int by FragmentArgumentDelegate()
    
        private var param2: String by FragmentArgumentDelegate()
    
        companion object {
            fun newInstance(param1: Int, param2: String): DemoFragment =
                DemoFragment().apply {
                    this.param1 = param1
                    this.param2 = param2
                }
        }
    }
    
    @Suppress("UNCHECKED_CAST")
    class FragmentArgumentDelegate<T : Any> : ReadWriteProperty<Fragment, T> {
        override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
            val key = property.name
            return thisRef.arguments?.get(key) as T
        }
    
        override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) {
            val arguments = thisRef.arguments
            val key = property.name
            arguments?.put(key, value)
        }
    }
    
    fun <T> Bundle.put(key: String, value: T) {
        when (value) {
            is Boolean -> putBoolean(key, value)
            is String -> putString(key, value)
            is Int -> putInt(key, value)
            is Short -> putShort(key, value)
            is Long -> putLong(key, value)
            is Byte -> putByte(key, value)
            is ByteArray -> putByteArray(key, value)
            is Char -> putChar(key, value)
            is CharArray -> putCharArray(key, value)
            is CharSequence -> putCharSequence(key, value)
            is Float -> putFloat(key, value)
            is Bundle -> putBundle(key, value)
            is Parcelable -> putParcelable(key, value)
            is Serializable -> putSerializable(key, value)
            else -> throw IllegalStateException("Type of property $key is not supported")
        }
    }
    

    这里要注意的是,Bundle没有提供单个属性的put拓展,所以我们需要自己实现一个。

    通过上面的这些操作,就将Fragment参数传递的代码简化到了只有一行,其它任何的Fragment传参,都可以使用这个委托。

    委托操作实例

    最后,再介绍一些官方推荐的委托使用场景。

    内置委托函数

    Kotlin系统库提供了很多有用的委托,这些都内置在Delegate库中。

    延迟属性lazy

    属性委托,可以是一个表达式,借助这个特性,可以实现属性的延迟加载,即在第一次访问的时候进行初始化。

    private val lazyProp: String by lazy {
        Log.d("xys", "表达式只会执行一次")
        "执行后赋值给lazyProp"
    }
    
    Log.d("xys", lazyProp)
    Log.d("xys", lazyProp)
    
    out:
    D/xys: 表达式只会执行一次
    D/xys: 执行后赋值给lazyProp
    D/xys: 执行后赋值给lazyProp
    

    要注意的是,lazy表达式中的代码,只会在第一次初始化的时候调用,之后就不会调用了,所以这里log只打印了一次。

    观察属性observable

    Delegates.observable可以非常方便的帮助我们实现观察者模式,代码如下所示。

    var observableProp: String by Delegates.observable("init value 0") { property, oldValue, newValue ->
        Log.d("xys", "change: $property: $oldValue -> $newValue ")
    }
    
    Log.d("xys", observableProp)
    observableProp = "change value"
    

    当属性值发生改变的时候,就会通知出来。

    借助观察属性,可以很方便的实现时间差的判断,例如连续back退出的功能,代码如下所示。

    private var backPressedTime by Delegates.observable(0L) { pre, old, new ->
        if (new - old < 2000) {
            finish()
        } else {
            Toast.makeText(this, "再按一次返回退出", Toast.LENGTH_SHORT).show()
        }
    }
    
    override fun onBackPressed() {
        backPressedTime = System.currentTimeMillis()
    }
    

    条件观察属性vetoable

    vetoable 与 observable一样,可以观察属性值的变化,不同的是,vetoable可以通过处理器函数来决定属性值是否生效,代码如下所示。

    var vetoableProp: Int by Delegates.vetoable(0){
        _, oldValue, newValue ->
        // 如果新的值大于旧值,则生效
        newValue > oldValue
    }
    

    SharedPreferences操作简化

    前面我们提到了,只要是涉及到get、set方法的使用的场景,几乎都可以使用委托来进行优化,再拓展一下,凡是对属性有进行读写操作的,都可以使用委托来进行优化,例如我们在Android中比较常用的SharedPreferences操作,大部分情况下,都会抽取工具类,类似下面这样进行调用。

    PreferencesUtil.getInstance().putBoolean(XXXXX, false);
    

    下面通过委托,我们可以将一个普通属性的读写进行代理,代理到通过SP读写,这样我们在代码中对这个属性的读写,实际上是将其代理到SP中,代码如下所示。

    @Suppress("UNCHECKED_CAST")
    class PreferenceDelegate<T>(private val context: Context, private val propName: String, private val defaultValue: T) : ReadWriteProperty<Any, T> {
    
        private val sharedPreferences: SharedPreferences by lazy { context.getSharedPreferences("SP_NAME", Context.MODE_PRIVATE) }
    
        override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
            value?.let { putSPValue(propName, value) }
        }
    
        override fun getValue(thisRef: Any, property: KProperty<*>): T {
            return getSPValue(propName, defaultValue) ?: defaultValue
        }
    
        private fun <T> getSPValue(name: String, defaultValue: T): T? = with(sharedPreferences) {
            val result = when (defaultValue) {
                is String -> getString(name, defaultValue)
                is Int -> getInt(name, defaultValue)
                is Long -> getLong(name, defaultValue)
                is Float -> getFloat(name, defaultValue)
                is Boolean -> getBoolean(name, defaultValue)
                else -> null
            }
            result as T
        }
    
        private fun <T> putSPValue(name: String, value: T) = with(sharedPreferences.edit()) {
            when (value) {
                is Long -> putLong(name, value)
                is String -> putString(name, value)
                is Int -> putInt(name, value)
                is Boolean -> putBoolean(name, value)
                is Float -> putFloat(name, value)
                else -> null
            }
        }?.apply()
    }
    
    使用:
    var valueInSP: String by PreferenceDelegate(this, "test", "init")
    
    Log.d("xys", valueInSP)
    valueInSP = "new value"
    Log.d("xys", valueInSP)
    
    out:
    D/xys: init
    D/xys: new value
    

    通过上面的操作,我们在使用SharedPreferences的时候,只需要对某个要操作的属性使用by进行标记,将其委托给PreferenceDelegate即可,这样表面上好像是在操作一个String,但实际上,已经是对SharedPreferences的操作了。

    在下面这个lib中,对很多场景下的委托进行了封装,大家可以参考下它的实现。

    https://github.com/fengzhizi715/SAF-Object-Delegate

    向大家推荐下我的网站 https://xuyisheng.top/  点击原文一键直达

    专注 Android-Kotlin-Flutter 欢迎大家访问

    展开全文
  • Delegates之前,首先必须了解下kotlin的委托机制,这个委托机制还是很不错的,可以将一个对象的构造和设置值,都给委托给其它 委托机制 关键词by,是不是很眼熟,这不是我们经常用过的by lazy{} ,没错懒加载就是用...

    简介

    说Delegates之前,首先必须了解下kotlin的委托机制,这个委托机制还是很不错的,可以将一个对象的构造和设置值,都给委托给其它

    委托机制

    关键词by,是不是很眼熟,这不是我们经常用过的by lazy{} ,没错懒加载就是用委托实现的

    自定义委托

    考大家一个问题valvar有啥区别吗?val是不可变的变量,必须要在初始化化的时候赋值。var是可变的变量。有没有考虑过为啥呢?

    // 其实每个变量都有一个隐藏的方法,如果是val变量修饰,只有get方法
        val name: String = ""
            get() {
               return field
            }
    
    // var 有get方法和set方法,当你给name2进行赋值的时候,set方法就会调用
        var name2: String = ""
            get() {
             return field
            }
            set(value) {
    
            }
    

    来尝试来一个定义一个委托:

    这里描述下,我现在是val定义变量,只需要ReadOnlyProperty(只需要要委托getValue方法),我定义了一个value变量记录值(初始化内容123),当使用这个变量的读取这个值的时候,就会调用我的委托方法

        val name: String by object : ReadOnlyProperty<Any?, String> {
            var value: String = "123"
            override fun getValue(thisRef: Any?, property: KProperty<*>): String {
                println("取这个[${property.name}]属性赋值:$value")
                // 返回这个记录值
                return value
            }
        }
    // 同理var的委托定义,如果我这样写,当使用这个变量的读取这个值的时候,就会调用我的委托方法。当我设置值的时候就会调用我的set方法,会传入一个value方法。
    var name2: String by object : ReadWriteProperty<Any?, String> {
            var value: String = "123"
            override fun getValue(thisRef: Any?, property: KProperty<*>): String {
                println("取这个[${property.name}]属性赋值:$value")
                // 返回这个记录值
                return value
            }
    
            override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
                println("为这个[${property.name}]属性赋值:$value")
                // 记录下当前这个值
                this.value = "我是从新构造:$value"
            }
        }
    

    这里有必要说下 thisRef 是个什么鬼?就是这个变量对应在那个对象中

    曲线救国委托实现多继承

    interface A {
        fun call()
    }
    
    class B() : A {
        override fun call() {
            println("我调用call了")
        }
    }
    
    open class D() {
        fun core() {
            println("我调用core了")
        }
    }
    
    class C() : D(), A by B(){
        fun test(){
            // todo 我可以直接掉call 变向多继承(这个在协程的  协程上下文与调度器(5)--协程作用域(重点) 有用到)
            call()
        }
    }
    

    Delegates

    Delegates.notNull

     // 使用很简单
     val name:String by Delegates.notNull<String>()
     // 源码分析
      // 第一句没啥的看下,NotNullVar
      public fun <T : Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
      
      // 是不是看出来了,在取值的时候,如果是null就会抛异常
      private class NotNullVar<T : Any>() : ReadWriteProperty<Any?, T> {
        private var value: T? = null
    
        public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
        }
    
        public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            this.value = value
        }
    }
    

    Delegates.observable

    大家记住这个,用它可以完成双击back退出

     val name: String by Delegates.observable("初始化值") { pre, old, new ->
            log("以前的值$old 新设置的值$new")
        }
    

    看下源码:
    ObservableProperty是关键,主要看委托的getValue方法和setValue方法

    	// 这里没啥好说的,主要看下 ObservableProperty对象
       public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
                ReadWriteProperty<Any?, T> =
            object : ObservableProperty<T>(initialValue) {
            	// 注意这里重写了afterChange方法
            	// 这里是调用onChange方法,就是我们传入的lambda表达式
                override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
            }
    
    public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
        private var value = initialValue
    
        protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true
    
        protected open fun afterChange(property: KProperty<*>, oldValue: T, newValue: T): Unit {}
    	
        public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return value
        }
    	// 主要是这里,
        public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        	// 记录上一次的值
            val oldValue = this.value
            // beforeChange默认返回true,先不管
            if (!beforeChange(property, oldValue, value)) {
                return
            }
            // 为this.value设置新的值,为getValue做准备
            this.value = value
            // 调用afterChange方法,被重写了,所以会调用到onChange,是不是很简单
            afterChange(property, oldValue, value)
        }
    }
    

    Delegates.vetoable

    这个能在设置值的时候,加逻辑判断,如果不满足便不更新值

        val name: String by Delegates.vetoable("初始化值") { pre, old, new ->
            // 如果设置的新值是a开头的就设置新值,返回ture就修改,false就不修改
            new.startsWith("a")
        }
    

    源码分析:
    其实这里和observable差不多,但是注意重写的是beforeChange方法

        public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
                ReadWriteProperty<Any?, T> =
            object : ObservableProperty<T>(initialValue) {
            	// 注意这里重写的是beforeChange方法
                override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
            }
    

    接下来就和以前差不多了

    public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
        private var value = initialValue
    
        protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true
    
        protected open fun afterChange(property: KProperty<*>, oldValue: T, newValue: T): Unit {}
    	
        public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return value
        }
    	// 主要是这里,
        public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        	// 记录上一次的值
            val oldValue = this.value
            // 主要看这里
            // beforeChange写了,如果我们的lamabda表达式返回的ture就不会return,就会调用后面的赋值方法
            if (!beforeChange(property, oldValue, value)) {
                return
            }
            // 为this.value设置新的值,为getValue做准备
            this.value = value
            // 调用afterChange方法,被重写了,所以会调用到onChange,是不是很简单
            afterChange(property, oldValue, value)
        }
    }
    

    实践完成双击back退出

    其实很简单,就是使用observable的特性

      private var backPressedTime by Delegates.observable(0L) { pre, old, new ->
            // 2次的时间间隔小于2秒就退出了
            if (new - old < 2000) {
                finish()
            } else {
                drawerLayout?.snack("再按返回鍵退出")
            }
        }
    	
    	// 从新写back方法
       override fun onBackPressed() {
       		// 直接赋值就可以啦,是不是很简单呀
            backPressedTime = System.currentTimeMillis()
        }
    
    展开全文
  • 包含一个kotlin属性委托的集合,用于管理android意图和捆绑附加组件。
  • delegates:代表-源码

    2021-04-25 19:58:29
    (或将Gemfile gem 'delegates'添加到您的Gemfile )。 然后: class Employee < Struct . new ( :name , :department , :address ) # ... extend Delegates delegate :city , :street , to : :address # ....
  • 需要把BindRaw换成BindUObject

     

    需要把BindRaw换成BindUObject

    展开全文
  • Delegates.7z

    2020-03-18 10:11:42
    QT中代理Delegates使用实例代码,可运行,多种编辑方式,有下列框,日期、QSpinBox,界面显示一看就懂!代码无buge,如果对你有帮助请给好评,有问题可以私信联系我
  • Learn-Events-Delegates-And-Lambdas 来自 PluralSight C# 事件、委托和 Lambda 模块 1 - 事件、委托和事件处理程序 事件 - 发送给订阅者的通知 (Button_Click) 鼠标按下触发点击事件。 多个对象可以通过订阅事件...
  • Delegates.notNull() (api reference) is based on delegated properties and was the original, and later came lateinit (Late Initialized Properties). Neither cover all possible use cases and neither

    首先:

    The two models are similar, and one predates the other. Delegates.notNull() (api reference) is based on delegated properties and was the original, and later came lateinit (Late Initialized Properties). Neither cover all possible use cases and neither should be used unless you can control the lifecycle of the class and know for sure that they will be initialized before being used.
    
    If the backing field might be set directly, or your library cannot work with a delegate then you should use lateinit and typically it is the default for most people when using with dependency injection. From the docs:
    
    Normally, properties declared as having a non-null type must be initialized in the constructor. However, fairly often this is not convenient. For example, properties can be initialized through dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.
    
    If the type you are using is not supported by lateinit (does not support primitive types) you are then forced to use the delegate.
    
    The (lateinit) modifier can only be used on var properties declared inside the body of a class (not in the primary constructor), and only when the property does not have a custom getter or setter. The type of the property must be non-null, and it must not be a primitive type.
    
    You might also want to read the discussion topic "Improving lateinit".
    

    译文

    这两个模型是相似的,Delegates.notNull()(api参考)基于委托属性,是原始的,后来是lateinit(后期初始化属性)。不覆盖所有可以能的用例,除非你能够控制类的生命周期,并确保它们在使用前初始化。

    如果可以以直接设置备份字段,或者者你的库不能使用委托,那么你应该使用lateinit。来自文档的:

    通常,声明为非空类型的属性必须在构造函数中初始化。然而,这通常是不方便的。例如可以通过依赖项注入或者单元测试的设置方法来初始化属性。在这种情况下,不能在构造函数中提供非空初始值设定项,但是在引用类中的属性时仍然要避免null检查。

    如果lateinit(不支持基元类型)不支持你正在使用的类型,则强制你使用委托。

    (lateinit)修饰符只能在类(不在主构造函数中)内声明的var属性上使用,而且只有在该属性没有自定义集合或者设置器时。属性的类型必须是非空的,并且不能是基元类型。

    Delegates.notNull()的使用举例

    Delegate.notNull()代理主要用于可以不在构造器初始化时候初始化而是可以延迟到之后再初始化这个var修饰的属性,它和lateinit功能类似,但是也有一些不同,不过它们都需要注意的一点是属性的生命周期,开发者要做到可控,也就是一定要确保属性初始化是在属性使用之前,否则会抛出一个IllegalStateException。

    package com.mikyou.kotlin.delegate
    
    import kotlin.properties.Delegates
    
    class Teacher {
        var name: String by Delegates.notNull()
    }
    fun main(args: Array<String>) {
        val teacher = Teacher().apply { name = "Mikyou" }
        println(teacher.name)
    }

    对比分析:

    可能有的人并没有看到*notNull()*有什么大的用处,先说下大背景吧就会明白它的用处在哪了?

    大背景: 在Kotlin开发中与Java不同的是在定义和声明属性时必须要做好初始化工作,否则编译器会提示报错的,不像Java只要定义就OK了,管你是否初始化呢。我解释下这也是Kotlin优于Java地方之一,没错就是空类型安全,就是Kotlin在写代码时就让你明确一个属性是否初始化,不至于把这样的不明确定义抛到后面运行时。如果在Java你忘记了初始化,那么恭喜你在运行时你就会拿到空指针异常。

    问题来了: 大背景说完了那么问题也就来了,相比Java,Kotlin属性定义时多出了额外的属性初始化的工作。但是可能某个属性的值在开始定义的时候你并不知道,而是需要执行到后面的逻辑才能拿到。这时候解决方式大概有这么几种:

    • 方式A: 开始初始化的时给属性赋值个默认值
    • 方式B: 使用Delegates.notNull()属性代理
    • 方式C: 使用lateinit修饰属性

    以上三种方式有局限性,方式A就是很暴力直接赋默认值,对于基本类型还可以,但是对于引用类型的属性,赋值一个默认引用类型对象就感觉不太合适了。方式B适用于基本数据类型和引用类型,但是存在属性初始化必须在属性使用之前为前提条件。方式C仅仅适用于引用类型,但是也存在属性初始化必须在属性使用之前为前提条件。

    对比分析
    属性使用方式优点缺点
    方式A(初始化赋默认值)使用简单,不存在属性初始化必须在属性使用之前的问题仅仅适用于基本数据类型
    方式B(Delegates.notNull()属性代理)适用于基本数据类型和引用类型

    1、存在属性初始化必须在属性使用之前的问题;

    2、不支持外部注入工具将它直接注入Java字段中

    方式C(lateinit修饰属性)仅适用于引用类型

    1、存在属性初始化必须在属性使用之前的问题;

    2、不支持基本数据类型

    使用建议: 如果能对属性生命周期做很好把控的话,且不存在注入到外部字段需求,建议使用方式B;此外还有一个不错建议就是方式A+方式C组合,或者方式A+方式B组合。具体看实际场景需求。

    Delegates.observable()的基本使用

    Delegates.observable()主要用于监控属性值发生变更,类似于一个观察者。当属性值被修改后会往外部抛出一个变更的回调。它需要传入两个参数,一个是initValue初始化的值,另一个就是回调lamba, 回调出property, oldValue, newValue三个参数。

    package com.mikyou.kotlin.delegate
    
    import kotlin.properties.Delegates
    
    class Person{
        var address: String by Delegates.observable(initialValue = "NanJing", onChange = {property, oldValue, newValue ->
            println("property: ${property.name}  oldValue: $oldValue  newValue: $newValue")
        })
    }
    
    fun main(args: Array<String>) {
        val person = Person().apply { address = "ShangHai" }
        person.address = "BeiJing"
        person.address = "ShenZhen"
        person.address = "GuangZhou"
    }
    

    运行结果

    property: address  oldValue: NanJing  newValue: ShangHai
    property: address  oldValue: ShangHai  newValue: BeiJing
    property: address  oldValue: BeiJing  newValue: ShenZhen
    property: address  oldValue: ShenZhen  newValue: GuangZhou
    
    Process finished with exit code 0

    Delegates.vetoable()的基本使用

    Delegates.vetoable()代理主要用于监控属性值发生变更,类似于一个观察者,当属性值被修改后会往外部抛出一个变更的回调。它需要传入两个参数,一个是initValue初始化的值,另一个就是回调lamba, 回调出property, oldValue, newValue三个参数。与observable不同的是这个回调会返回一个Boolean值,来决定此次属性值是否执行修改。

    package com.mikyou.kotlin.delegate
    
    import kotlin.properties.Delegates
    
    class Person{
        var address: String by Delegates.vetoable(initialValue = "NanJing", onChange = {property, oldValue, newValue ->
            println("property: ${property.name}  oldValue: $oldValue  newValue: $newValue")
            return@vetoable newValue == "BeiJing"
        })
    }
    
    fun main(args: Array<String>) {
        val person = Person().apply { address = "NanJing" }
        person.address = "BeiJing"
        person.address = "ShangHai"
        person.address = "GuangZhou"
        println("address is ${person.address}")
    }
    展开全文
  • C# Delegates 委托

    2019-11-22 23:42:47
    C# Delegates 委托 通常我们都是把数据作为参数传递给方法:int i = int.Parse(“99”);当需要把方法传送给其他方法时就需要使用委托。 类的用法,首先声明一个类,接着实例化一个类,委托的用法和类的用法类似,...
  • Events and Delegates(事件和委托)

    千次阅读 2015-08-22 17:08:23
    Delegates are the special objects in C# which are pointers to methods. It’s an interesting and amazing feature of C#. It is something that gives a name to a method signature. These are implemented as...
  • 快速委托实施 概括 这个项目的想法是在C ++ 11/14中实现快速实现,以最快的速度在多个平台上运行。 主要代码在委托人.h和委托人.cpp中,剩下的只是为了能够测试MAME本身中的一些用法。 到目前为止状态是下一个: ...
  • The two models are similar, and one predates the ... Delegates.notNull() (api reference) is based on delegated properties and was the original, and later came lateinit (Late Initialized Properties)...
  • Delegates.notNull() (api reference) is based on delegated properties and was the original, and later came lateinit (Late Initialized Properties). Neither cover all possible use cases and neither shou...
  • 摘要  委托是C#编程一个非常重要的概念,也是一个难点。本文将系统详细讲解委托。  1....  其实,我一直思考如何讲解委托,才能把委托说得更透彻。说实话,每个人都委托都有不同的见解,因为看问题的角度不同。...
  • AdapterDelegatesSample 一个示例应用程序演示了我们如何重构代码,从使用具有继承功能的普通RecyclerView适配器到使用AdapterDelegates库。 两种实现都已包括在内,因此可以比较两种方法的复杂性。
  • 很少会接触的到这类思想类的书籍,就是要培养你的那个思想。想大佬所想,做大佬之不能及。这才是王道
  • C# delegates 应用源码

    2009-05-22 16:46:55
    C# delegates的简单应用,简单易懂
  • node-delegates 是 TJ 大神所写的一个简单的小工具,源码只有 157 行,作用在于将外部对象接受到的操作委托到内部属性进行处理,也可以理解为讲对象的内部属性暴露到外部,简化我们所需要书写的代码。 安装和使用的...
  • delegates,可以帮我们方便快捷地使用设计模式当中的委托模式(Delegation Pattern),即外层暴露的对象将请求委托给内部的其他对象进行处理。 用法 delegates 基本用法就是将内部对象的变量或者函数绑定在暴露在...
  • 学习委托非常好的资料,讲解详细,与大家分享!
  • ylbtech-Unitity-CS:Delegates

    2015-12-20 22:40:00
    ylbtech-Unitity-CS:Delegates   1.A,效果图返回顶部   Invoking delegate a: Hello, A! Invoking delegate b: Goodbye, B! Invoking delegate c: Hello, C! Goodbye, C! In....
  • const delegate = require('delegates') const request = { get acceptJSON(){ return '123' } } let proto = { get title(){ return 'my title is delegates' } }; proto.request = Object.create...
  • C#中的委托和事件在.Net Framework中的应用非常广泛,较好的理解委托和事件对很多的接触c#时间不长的人来说并不容易。
  • Gios C ++代表 c ++中成员函数的委托的幼稚单头实现。 受虚幻引擎4委托系统的影响。 对于希望这样做的任何人,我建议这两篇文章: 用法: 若要使用,请将GiosDelegates.dll添加到项目的include目录中,然后添加...
  • delegates ,它由大名鼎鼎的 TJ 所写,可以帮我们方便快捷地使用设计模式当中的 委托模式(Delegation Pattern) ,即外层暴露的对象将请求委托给内部的其他对象进行处理,当前版本是 1.0.0,周下载量约为 364 万...
  • Event handlers and delegates

    2018-05-13 15:37:15
    In that case we use AX7 events and delegates (also called ‘hooks’). There are already plenty of events (hooks) we can subscribe in AX and extend the existing functionality without overlayering or ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,980
精华内容 7,192
关键字:

delegates