给定C#中的以下委托定义:

struct Dummy {}
delegate int ReceiveDummy(in Dummy dummy);

如何为ReceiveDummy类型分配VB.Net SUB?以下两个函数定义都失败,并出现错误BC30657:"具有不支持的返回类型或不支持的参数类型".

Shared Function MyReceiveDummy(ByVal dummy As Dummy) As Integer
Shared Function MyReceiveDummy(ByRef dummy As Dummy) As Integer
Dim receive As ReceiveDummy = New ReceiveDummy(AddressOf MyReceiveDummy)

VB.NET中有没有一个等同于C#的S"in"关键字?

推荐答案

这是extremely hacky,但事实证明(在.NET 7中)使用Delegate.CreateDelegate()从其签名实际接受ref参数的静态方法创建带有in参数的类型的委托是可能的.

例如,如果我创建(用C#):

struct Dummy {}
delegate int ReceiveDummy(in Dummy dummy);

static class DummyActions
{
    //https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/readonly-ref.md#metadata-representation-of-in-parameters
    static int ActuallyReceiveDummy(ref Dummy dummy)
    {
        return dummy.GetHashCode();
    }
    
    public static ReceiveDummy CreateReceiveDummy()
    {
        return (ReceiveDummy)
            Delegate.CreateDelegate(typeof(ReceiveDummy), 
                                    typeof(DummyActions).GetMethod(nameof(DummyActions.ActuallyReceiveDummy), 
                                                                   BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic));
    }
}

我可以做到:

var receiveMethod = DummyActions.CreateReceiveDummy();
Console.WriteLine(receiveMethod(new Dummy()));

A它起作用了!演示#1here.

翻译成VB.NET,我得到以下结果:

Friend Class DummyActions
    ' https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/readonly-ref.md#metadata-representation-of-in-parameters
    Private Shared Function ActuallyReceiveDummy(ByRef dummy As Dummy) As Integer
        ' Do whatever you want here but don't modify dummy!
        Return dummy.GetHashCode() 
    End Function

    Public Shared Function CreateReceiveDummy() As ReceiveDummy
        Return CType([Delegate].CreateDelegate(GetType(ReceiveDummy), 
            GetType(DummyActions).GetMethod(NameOf(DummyActions.ActuallyReceiveDummy), 
                BindingFlags.Static Or BindingFlags.Public Or BindingFlags.NonPublic)), ReceiveDummy)
    End Function
End Class

这使得在VB.NET中实现以下功能成为可能:

Dim receiveMethod = DummyActions.CreateReceiveDummy()
Console.WriteLine(receiveMethod(New Dummy()))

样机小提琴#2 here

备注:

  • 根据design documents for in in C# 7.2

    如果将System.Runtime.CompilerServices.IsReadOnlyAttribute应用于byref参数,则意味着该参数是in参数.

    此外,如果方法是抽象的或虚拟的,则此类参数(且仅此类参数)的签名必须为modreq[System.Runtime.InteropServices.InAttribute].

    动机:这样做是为了确保在方法覆盖/实现In参数的情况下匹配.

    同样的要求也适用于委托中的Invoke个方法.

    动机:这是为了确保现有的编译器在创建或分配委托时不能简单地忽略readonly.

    原来in C#编译器不允许你手动应用IsReadOnlyAttribute,如果你try 了,你会得到一个编译器错误Do not use 'System.Runtime.CompilerServices.IsReadOnlyAttribute'. This is reserved for compiler usage.(演示#3 here).但VB.NET编译器没有这样的限制,因此,如果您想要针对判断此属性的某个可能的Delegate.CreateDelegate()future 版本"保护"您的方法,您可以手动添加它:

    ' https://github.com/dotnet/csharplang/blob/main/proposals/csharp-7.2/readonly-ref.md#metadata-representation-of-in-parameters
    Private Shared Function ActuallyReceiveDummy(<System.Runtime.CompilerServices.IsReadOnlyAttribute()> ByRef dummy As Dummy) As Integer
        ' Do whatever you want here but don't modify dummy!
        Return dummy.GetHashCode() 
    End Function
    

    VB.NET编译器似乎不知道或以任何方式说明此属性.

    演示#4 here.

  • 整个 idea 都是围绕着框架防止in个参数被修改这一原则进行的,因此是超级老套的.因此,您需要手动注意不要以任何方式修改您的ByRef dummy as Dummy参数.

As an alternative,如果您愿意编写一点C#代码,那么按照this answerxSjoerd van Kreel102中的建议,您可以用C#编写一个adapter扩展方法,该方法从通过值获取Dummy的委托创建ReceiveDummy委托:

public delegate int ReceiveDummyByValue(Dummy dummy);

public static class DummyActions
{
    public static ReceiveDummy AsReceiveByReference(this ReceiveDummyByValue func) => (in Dummy d) => func(d);
}

然后在您的主要VB.NET代码中使用它,例如,如下所示:

Private Shared Function ActuallyReceiveDummy(ByVal d As Dummy) As Integer
    ' Return whatever is required for dummy
    Return d.GetHashCode()
End Function

Public Shared Function CreateReceiveDummy() As ReceiveDummy
    Return (New ReceiveDummyByValue(AddressOf ActuallyReceiveDummy)).AsReceiveByReference()
End Function

Csharp相关问答推荐

从C#网站调用时找不到存储过程

如何使用CsvReader获取给定列索引的列标题?

在多对多关系上不删除实体

如何在NodaTime中为Instant添加一年?

C#EF Core 8.0表现与预期不符

使用可信第三方的Iext8.Net pdf签名

如何在没有前缀和可选后缀的情况下获取Razor Page Handler方法名称?

CS1660无法将lambda表达式转换为类型INavigationBase,因为它不是委托类型

如果设置了另一个属性,则Newtonsoft JSON忽略属性

C#中浮点数的System.Text.Json序列化问题

在DoubleClick上交换DataGridViewImageColumn的图像和工具提示

记录类型';==运算符是否与实现IEquatable<;T&>;的类中的';equals&>方法执行等价比较?

链接到字典字符串.拆分为(.Key,.Value)

在C#ASP.NET内核中使用INT AS-1进行控制器场景的单元测试

C# Winforms:从对象树到TreeView的递归转换重复条目

如何在C#.NET桌面应用程序中动态更改焦点工具上的后退 colored颜色

如何在特定时间间隔运行多个后台任务?

NETSDK1201:对于面向.NET 8.0和更高版本的项目,默认情况下,指定RUNTIME标识符将不再生成自包含的应用程序

在C#中通过Matheval使用自定义公式

使用SQL Server 2022+时,我是否必须将代码从SqlConnection类对象中迁移出来?