将 C# 转换为 VB,NET:具有 return 类型的委托类型的事件

Convert C# to VB,NET: Event with a delegate type that has a return type

这是用 C# 编写的原始源代码

public delegate Unit UnitResolveEventHandler(object sender, ResolveEventArgs args);

public event UnitResolveEventHandler UnitResolve;

public static Unit GetUnitByName(string name) {
    Instance.unitsByName.TryGetValue(name, out result);
    if (Instance.UnitResolve != null) {
        foreach (UnitResolveEventHandler handler in Instance.UnitResolve.GetInvocationList()) {
            result = handler(Instance, new ResolveEventArgs(name));
        }
    }
}

使用在线翻译器,我得到这个 VB.NET 代码:

Public Delegate Function UnitResolveEventHandler(sender As Object, args As ResolveEventArgs) As Unit

Public Event UnitResolve As UnitResolveEventHandler

Public Shared Function GetUnitByName(name As String) As Unit
    Instance.unitsByName.TryGetValue(name, result)
    If Instance.UnitResolve IsNot Nothing Then
        For Each handler As UnitResolveEventHandler In Instance.UnitResolve.GetInvocationList()
            result = handler(Instance, New ResolveEventArgs(name))
        Next
    End If
End Function

编译器使用此错误消息标记事件声明:

Events cannot be declared with a delegate type that has a return type.

并且 Instance.UnitResolveGetUnitByName() 方法中调用并出现以下错误消息:

Public Event UnitResolve As UnitResolveEventHandler' is an event, and cannot be called directly.

如何在不丢失功能的情况下将代码从 C# 正确转换为 VB.NET?

原来的C#源码不好;事件处理程序不应该 return 值。你必须让它不是一个事件:

Public UnitResolve As UnitResolveEventHandler

并使用 Delegate.Combine 手动添加事件处理程序:

Instance.UnitResolve = Delegate.Combine(Instance.UnitResolve, newHandler)

从事件处理程序向事件调用返回值的惯用方法是通过参数---事件参数的成员 class,或通过 ByRef委托参数。

如果您可以控制 ResolveEventArgs 和事件处理程序例程,您可以这样做:

Public Class ResolveEventArgs
    '...
    Public ReturnValue As Unit
    '...
End Class

在处理程序的主体中(假设事件参数的典型声明为 e),而不是 Return (return value):

e.ReturnValue = (return value) 'substitute for (return value) as appropriate

然后,您的 For Each 循环的主体将如下所示:

Dim args As New ResolveEventArgs(name)
handler(Instance, args)
result = args.ReturnValue

顺便说一句,原始 C# 代码存在线程安全问题。如果最后一个订阅的处理程序在 null 检查和读取调用列表之间被删除,它可能会抛出 NullReferenceException。这是否严重(或根本不值得关注)取决于它的使用地点和方式。解决这个问题的通常方法是存储到临时文件,然后在临时文件上执行空检查和调用列表。如果您使用的是最新版本的 .NET 语言,那么您可以跳过 null 检查并使用 ?. 运算符,这对于特定的线程安全问题也应该是安全的。