Given the following delegate definition in C#:
struct Dummy {}
delegate int ReceiveDummy(in Dummy dummy);
How can I assign a a VB.Net sub to the ReceiveDummy type? Both the following function definitions fail with error BC30657: "has a return type that is not supported or parameter types that are not supported".
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)
Is there a VB.NET equivalent to C#'s "in" keyword?
This is extremely hacky, but it turns out to be possible (in .NET 7) to use Delegate.CreateDelegate()
to create a delegate of a type with an in
parameter from a static method whose signature actually accepts a ref
parameter.
E.g. if I create (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));
}
}
I can do:
var receiveMethod = DummyActions.CreateReceiveDummy();
Console.WriteLine(receiveMethod(new Dummy()));
An it works! Demo #1 here.
Translating to VB.NET, I get the following:
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
Which makes the following possible in VB.NET:
Dim receiveMethod = DummyActions.CreateReceiveDummy()
Console.WriteLine(receiveMethod(New Dummy()))
Mockup fiddle #2 here
Notes:
According to the design documents for in
in C# 7.2
When
System.Runtime.CompilerServices.IsReadOnlyAttribute
is applied to a byref parameter, it means that the parameter is anin
parameter.In addition, if the method is abstract or virtual, then the signature of such parameters (and only such parameters) must have
modreq[System.Runtime.InteropServices.InAttribute]
.Motivation: this is done to ensure that in a case of method overriding/implementing the in parameters match.
Same requirements apply to
Invoke
methods in delegates.Motivation: this is to ensure that existing compilers cannot simply ignore
readonly
when creating or assigning delegates.
It turns out that in C# the compiler will not allow you to manually apply IsReadOnlyAttribute
, if you try you will get a compiler error Do not use 'System.Runtime.CompilerServices.IsReadOnlyAttribute'. This is reserved for compiler usage. (demo #3 here). But the VB.NET compiler makes no such restriction, so if you want to "future-proof" your method against some possible future version of Delegate.CreateDelegate()
that checks for this attribute, you could add it manually:
' 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
The VB.NET compiler does not seem to know about or account for this attribute in any way.
Demo #4 here.
This whole idea does an end-run around the principle that the framework prevents in
parameters from being modified, and so is super hacky. Thus you need to manually take care not to modify your ByRef dummy as Dummy
argument in any way.
As an alternative, If you are willing to write a little bit of C# code, then as suggested in this answer by Sjoerd van Kreel to Recording Audio using XtAudio in VB.NET, you could write an adapter extension method in C# that creates a ReceiveDummy
delegate from a delegate taking Dummy
by value:
public delegate int ReceiveDummyByValue(Dummy dummy);
public static class DummyActions
{
public static ReceiveDummy AsReceiveByReference(this ReceiveDummyByValue func) => (in Dummy d) => func(d);
}
Then in your main VB.NET code, use it e.g. as follows:
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
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments