I'm using a 3rd party library which has an event\event handler, which I'd like to convert to an observable. The event handler delegate wants a struct, with the in keyword. This causes an exception:
System.ArgumentException - Cannot bind to the target method because its signature is not compatible with that of the delegate type.
from
System.Private.CoreLib.dll!System.Reflection.RuntimeMethodInfo.CreateDelegateInternal(System.Type delegateType, object firstArgument, System.DelegateBindingFlags bindingFlags) Line 556 C#
System.Private.CoreLib.dll!System.Reflection.RuntimeMethodInfo.CreateDelegate(System.Type delegateType, object target) Line 534 C#
System.Reactive.dll!System.Reactive.ReflectionUtils.CreateDelegate<Test.TheDelegate>(object o, System.Reflection.MethodInfo method) Line 18 C#
If I remove the in from the arguments, It works as expected. I'm at a loss to understand why? Is even possible to do?
using System;
using System.Diagnostics;
using System.Reactive.Linq;
Test test = new();
Start();
test.TriggerEvent();
test.TriggerEvent();
void Start()
{
Debug.WriteLine("Start");
var o = Observable.FromEvent<Test.TheDelegate, SomeStruct>
(h => test.TheEvent = h,
h => test.TheEvent -= h)
.Subscribe(OnNext);
}
void OnNext(SomeStruct s) => Debug.WriteLine("OnNext");
public class Test
{
public delegate void TheDelegate(in SomeStruct o);
// Without the in (or ref) keyword, no exception
//public delegate void TheDelegate(SomeStruct o);
public event TheDelegate? TheEvent;
public void TriggerEvent()
{
SomeStruct s = new();
TheEvent?.Invoke(s);
Debug.WriteLine("Event Fired");
}
}
public struct SomeStruct
{ }
CodePudding user response:
I was able to reproduce the ArgumentException
at run-time. To solve it, you could use the Observable.FromEvent
overload that has an extra conversion
parameter:
public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(
Func<Action<TEventArgs>, TDelegate> conversion,
Action<TDelegate> addHandler,
Action<TDelegate> removeHandler);
Usage:
var observable = Observable.FromEvent<Test.TheDelegate, SomeStruct>
(
action => (in SomeStruct arg) => action(arg),
h => test.TheEvent = h,
h => test.TheEvent -= h
);
var subscription = observable.Subscribe(OnNext);
The conversion
converts an Action<SomeStruct>
to a Test.TheDelegate
. Contrary to the simpler overload, the validity of the conversion is checked at compile-time.