如何解决如何将委托事件从 C# 转换为需要 2 个输入和 1 个输出参数的 Kotlin?
我正在将一个库从 c# 移植到 kotlin,并且在一个类中我有一个委托事件,它有 2 个输入参数和一个输出参数
public delegate bool MyDelegate(int para1,int param2)
public event MyDelegate IsFinished
if(IsFinished){
//do somethings
}
所有这些,在 kotlin 中,我该如何翻译?
实际上,我试图创建一个模拟事件的类
class EventTwoReturnBoolean<T,U> {
private val observers = mutableSetof<(T,U) -> Boolean>()
operator fun plusAssign(observer: (T,U) -> Boolean) {
observers.add(observer)
}
operator fun minusAssign(observer: (T,U) -> Boolean) {
observers.remove(observer)
}
operator fun invoke(value: T,value2: U) : Boolean {
var bool : Boolean = true
for (observer in observers){
bool = observer(value,value2)
}
return bool
}
}
解决方法
C# 内置了对 EventHandler 模式的支持。这是 Observer pattern 的变体,它允许主题(EventHandler 术语中的publisher)具有多个观察状态(EventHandler 术语中的事件)。此外,它还显式地将观察者(EventHandler 术语中的subscriber)事件处理程序 方法的参数提取到单独的实体中,同时将引用传递给发布者
在 C# 中,它在语言(event
和 delegate
关键字)和 stdlib 级别(EventHandler
和 EventArgs
类)上实现,而 Kotlin 不提供此功能。但是,可以在 Kotlin 中重新创建完全相同的 API(通过 +=
/-=
订阅/取消订阅,通过 ()
引发事件)。
实际上,您正在尝试实现 EventHandler 模式的一些变体(其中委托返回一个值)。我将展示如何在 Kotlin 中实现经典的 EventHandler 模式,它可能会帮助你实现你的。
首先,我们需要定义相同的基本类:
interface EventArgs
typealias Observer<T> = (sender: Any,eventArgs: T) -> Unit
//In C# EventHandler subscription/unsubscription is thread-safe,so we need to keep it this way in Kotlin too
//Variant 1 - pure Kotlin,thread-safety is lock-based
class EventHandler<T : EventArgs> {
private val subscribers = mutableSetOf<Observer<T>>()
operator fun plusAssign(subscriber: Observer<T>) {
synchronized(subscribers) { subscribers.add(subscriber) }
}
operator fun minusAssign(observer: Observer<T>) {
synchronized(subscribers) { subscribers.remove(observer) }
}
operator fun invoke(publisher: Any,args: T) {
subscribers.forEach { it.invoke(publisher,args) }
}
}
//Variant 2 - JVM-specific,but more multihread-performant
class EventHandler<T : EventArgs> {
private val subscribers = CopyOnWriteArraySet<Observer<T>>()
operator fun plusAssign(subscriber: Observer<T>) {
subscribers.add(subscriber)
}
operator fun minusAssign(observer: Observer<T>) {
subscribers.remove(observer)
}
operator fun invoke(publisher: Any,args) }
}
}
这里是微软官方的“基于 EventHandler 模式发布事件”编程指南的基本 example 直接翻译成 Kotlin:
// Define a class to hold custom event info
class CustomEventArgs(var message: String) : EventArgs
// Class that publishes an event
open class Publisher {
// Declare the event using EventHandler<T>
val raiseCustomEvent = EventHandler<CustomEventArgs>()
fun doSomething() {
// Write some code that does something useful here
// then raise the event. You can also raise an event
// before you execute a block of code.
onRaiseCustomEvent(CustomEventArgs("Event triggered"))
}
// Wrap event invocations inside a protected method
// to allow derived classes to override the event invocation behavior
protected fun onRaiseCustomEvent(e: CustomEventArgs) {
//In this Kotlin implementation raiseCustomEvent is always non-null,even if there are no subscribers,// so all that actions to avoid NPE in C# implementation are redundant here
// Format the string to send inside the CustomEventArgs parameter
e.message += " at ${Instant.now()}" // pure Kotlin alternative - `Clock.System.now()` using https://github.com/Kotlin/kotlinx-datetime library
// Call to raise the event.
raiseCustomEvent(this,e)
}
}
//Class that subscribes to an event
class Subscriber(val id: String,pub: Publisher) {
init {
// Subscribe to the event
pub.raiseCustomEvent += ::handleCustomEvent
}
// Define what actions to take when the event is raised.
fun handleCustomEvent(sender: Any,e: CustomEventArgs) {
println("$id received this message: ${e.message}");
}
}
fun main() {
val pub = Publisher()
val sub1 = Subscriber("sub1",pub)
val sub2 = Subscriber("sub2",pub)
// Call the method that raises the event
pub.doSomething()
// Keep the console window open
println("Press any key to continue...")
readLine()
}
这里的主要缺陷是 invoke
的 EventHandler
方法可以在 Kotlin 中从任何地方调用(在 C# 中它只能从声明它的类中调用):
fun main() {
val pub = Publisher()
pub.raiseCustomEvent(pub,CustomEventArgs("Oops!"))
}
为了履行这个契约,EventHandler
声明需要改变(invoke
方法需要变成 protected
和类本身 - abstract
,因为直接实例化它不会现在有意义):
abstract class EventHandler<T : EventArgs> {
private val subscribers = mutableSetOf<Observer<T>>()
operator fun plusAssign(subscriber: Observer<T>) {
synchronized(subscribers) { subscribers.add(subscriber) }
}
operator fun minusAssign(observer: Observer<T>) {
synchronized(subscribers) { subscribers.remove(observer) }
}
protected operator fun invoke(publisher: Any,args) }
}
}
事件声明将成为样板:
open class Publisher {
// Declare the event using EventHandler<T>
//- val raiseCustomEvent = EventHandler<CustomEventArgs>()
//Declare backing property,extending EventHandler<T> and thus having access to its protected members
private val _raiseCustomEvent = object : EventHandler<CustomEventArgs>() {
operator fun invoke(args: CustomEventArgs) = super.invoke(this@Publisher,args)
}
// Declare the event referencing backing property
val raiseCustomEvent get() = _raiseCustomEvent
//Define public invoke operator extension in the scope of the Publisher class
//Can't access raiseCustomEvent directly - it will lead to recursive problem (StackOverflowError in runtime)
// that's why we need backing property
operator fun EventHandler<CustomEventArgs>.invoke(args: CustomEventArgs) = _raiseCustomEvent.invoke(args)
}
虽然事件引发会变得更清晰:
//- raiseCustomEvent(this,e)
raiseCustomEvent(e)
Now 事件只能在 Publisher
的范围内引发:
fun main() {
val pub = Publisher()
pub.raiseCustomEvent(pub,CustomEventArgs("Oops!")) //Won't compile
pub.raiseCustomEvent(CustomEventArgs("Oops!")) //Won't compile too
}
也许,可以通过基于注释处理的代码生成来减少样板,但这超出了我的回答范围
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。