Skip to content

Instantly share code, notes, and snippets.

@orangy
Last active September 30, 2019 09:45
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save orangy/11178911 to your computer and use it in GitHub Desktop.
Save orangy/11178911 to your computer and use it in GitHub Desktop.
C#-style events in Kotlin
class Event<T> {
private val handlers = arrayListOf<(Event<T>.(T) -> Unit)>()
fun plusAssign(handler: Event<T>.(T) -> Unit) { handlers.add(handler) }
fun invoke(value: T) { for (handler in handlers) handler(value) }
}
val e = Event<String>() // define event
fun main(args : Array<String>) {
e += { println(it) } // subscribe
e("sdfsdf") // invoke
}
@niondir
Copy link

niondir commented Jul 22, 2016

I like the idea but it is not possible in Kotlin to specify the visibility like the event keyword in C# does, is it?

Meaning I want to be able to call e.invoke() only from inside the defining class.

fun main(args: Array<String>) {
        val duck = Duck();
        duck.onTalk += {println("Duck: " + it)}

        duck.onTalk("I was hacked :(")
        duck.poke()
}

class Event<T> {
    private val handlers = arrayListOf<(Event<T>.(T) -> Unit)>()
    operator fun plusAssign(handler: Event<T>.(T) -> Unit) { handlers.add(handler) }
    operator fun invoke(value: T) { for (handler in handlers) handler(value) }
}


class Duck {
    val onTalk = Event<String>()

    fun poke() {
        onTalk("Quaak!")
    }
}

Prints:

Duck: I was hacked :(
Duck: Quaak!

@0nko
Copy link

0nko commented Jul 29, 2017

You could split up the event like so:

fun main(args: Array<String>) {
    val duck = Duck()
    duck.talk += { said -> println("Duck: $said") }

    duck.talk("I was hacked :(") // won't work anymore
    duck.poke()
}

class Event<T>(val eventHandler: EventHandler<T>) {
    operator fun plusAssign(handler: (T) -> Unit) { eventHandler.handlers.add(handler) }
}

class EventHandler<T> {
    val handlers = arrayListOf<((T) -> Unit)>()
    operator fun invoke(value: T) { for (handler in handlers) handler(value) }
}

class Duck {
    private val onTalk = EventHandler<String>()
    val talk = Event<String>(onTalk)

    fun poke() {
        onTalk("Quaak!")
    }
}

@safopet
Copy link

safopet commented Apr 12, 2018

If use interface for event?

class Delegate<TS, TA> : Event<TS, TA> {
    val invocationList = arrayListOf<(TS,TA) -> Unit>()

    override operator fun plusAssign(m: (TS,TA) -> Unit) {
        invocationList.add(m)
    }

    override operator fun minusAssign(m: (TS,TA) -> Unit) {
        invocationList.remove(m)
    }

    operator fun invoke(source: TS, arg:TA) {
        for (m in invocationList)
            m(source, arg)
    }
}

interface Event<TS, TA> {
    operator fun plusAssign(m: (TS,TA) -> Unit)
    operator fun minusAssign(m: (TS,TA) -> Unit)
}

class Duck {
    val onTalk : Event<Duck, String> = Delegate()

    fun poke() {
        val handler = onTalk as Delegate<Duck, String>
        handler(this, "Quaak!")
    }
}

fun main(args: Array<String>) {
    val duck = Duck()
    duck.onTalk += fun(s: Duck, a: String ) { println("${s.javaClass.name}: $a")}

    duck.poke()
}

@MyErpSoft
Copy link

onTalk is null when Duck init at C#, but kotlin not support ref keyword.

class Delegate<TS, TA> : Event<TS, TA> {
    private var invocationList: MutableList<(TS, TA) -> Unit>? = null

    override operator fun plusAssign(m: (TS, TA) -> Unit) {
        val list = invocationList ?: mutableListOf<(TS, TA) -> Unit>().apply { invocationList = this }
        list.add(m)
    }

    override operator fun minusAssign(m: (TS, TA) -> Unit) {
        val list = invocationList
        if (list != null) {
            list.remove(m)
            if (list.isEmpty()) {
                invocationList = null
            }
        }
    }

    operator fun invoke(source: TS, arg: TA) {
        val list = invocationList
        if (list != null) {
            for (m in list)
                m(source, arg)
        }
    }
}

interface Event<out TS, out TA> {
    operator fun plusAssign(m: (TS,TA) -> Unit)
    operator fun minusAssign(m: (TS,TA) -> Unit)
}

class Duck {
    private val onTalk = Delegate<Duck, String>()
    val talking : Event<Duck,String> get() = onTalk

    fun poke() {
        onTalk(this, "Quaak!")
    }
}

fun main(args: Array<String>) {
    val duck = Duck()
    duck.talking += fun(s: Duck, a: String ) { println("${s.javaClass.name}: $a")}

    duck.poke()
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment