从非标准事件创建 Observable(无 EventArgs / EventHandler)
Create Observable from non-standard event (no EventArgs / EventHandler)
我想为定义如下的事件创建一个 Observable:
public event Func<Exception, Task> Closed;
我目前的代码是这样的:
Observable.FromEvent<Func<Exception, Task>, Unit>(h => hub.Closed += h, h=> hub.Closed -= h);
编译正常,但抛出运行时异常:
System.ArgumentException: 'Cannot bind to the target method because
its signature or security transparency is not compatible with that of
the delegate type.'
我觉得我做的不对。我不习惯从不遵循 EventArgs 模式的事件创建可观察对象
编辑:为了清楚起见,这是经典事件处理的完整代码:
class Program
{
static async Task Main(string[] args)
{
var hub = new HubConnectionBuilder().WithUrl("http://localhost:49791/hubs/status")
.Build();
hub.On<Status>("SendAction", status => Console.WriteLine($"Altitude: {status.Altitude:F} m"));
await hub.StartAsync();
hub.Closed += HubOnClosed;
while (true)
{
}
}
private static Task HubOnClosed(Exception arg)
{
Console.WriteLine("The connection to the hub has been closed");
return Task.CompletedTask;
}
}
请尝试以下操作,但不要使用您想要的签名,而是要尝试:
class Program
{
public delegate void ClosedEventHandler(object sender, Func<Exception, Task> e);
public ClosedEventHandler Closed { get; set; }
static void Main(string[] args)
{
Program hub = new Program();
hub.Closed = hub.SomethingToDoWhenClosed;
Observable
.FromEventPattern<ClosedEventHandler, Func<Exception, Task>>(
h => hub.Closed += h,
h => hub.Closed -= h)
.Subscribe(x =>
{
// this is hit
});
hub.Closed(hub, e => null);
}
public void SomethingToDoWhenClosed(object sender, Func<Exception, Task> e)
{
}
}
这样的事情能解决问题吗?
class Program
{
public event Func<Exception, Task> Closed;
static void Main(string[] args)
{
Program p = new Program();
IObservable<Unit> closedObservable = Observable.Create<Unit>(
observer =>
{
Func<Exception, Task> handler = ex =>
{
observer.OnNext(Unit.Default);
return Task.CompletedTask;
};
p.Closed += handler;
return () => p.Closed -= handler;
});
}
}
Observable.Create()
是针对此类异常情况的有用回退。
顺便说一句,有一个带有非 void 返回委托的事件是非常奇怪的,因为引发事件的代码只会看到 运行 最后一个处理程序的值 - 除非它引发该事件以某种非标准方式。但是,由于它是库代码,所以这不是你能控制的!
您需要转换重载。每次我看这个东西我都会关门:
IObservable<TEventArgs> Observable.FromEvent<TDelegate, TEventArgs>(
Func<Action<TEventArgs>, TDelegate> conversion,
Action<TDelegate> addHandler,
Action<TDelegate> removeHandler>
)
所以在我们的例子中,TEventArgs
是Exception
,而TDelegate
是Func<Exception, Task>
,所以你需要把Action<Exception>
转换成Func<Exception, Task>>
],换句话说:Func<Action<Exception>, Func<Exception, Task>>
。我假设转换看起来像这样:a => e => {a(e); return Task.CompletedTask; }
.
System.Reactive 需要此转换函数,因为它需要使用适当的委托订阅事件,并以某种方式挂钩您的 code/RX 管道代码。在这种情况下,a(e)
基本上是 RX 管道,然后传递异常以稍后在反应管道中处理。
完整代码:
class Program
{
static async Task Main(string[] args)
{
Program.Closed += Program.HubOnClosed;
Observable.FromEvent<Func<Exception, Task>, Exception>(
a => e => {a(e); return Task.CompletedTask; },
h => Program.Closed += h,
h => Program.Closed -= h
)
.Subscribe(e =>
{
Console.WriteLine("Rx: The connection to the hub has been closed");
});
Program.Closed.Invoke(null);
Program.Closed.Invoke(null);
}
private static Task HubOnClosed(Exception arg)
{
Console.WriteLine("The connection to the hub has been closed");
return Task.CompletedTask;
}
public static event Func<Exception, Task> Closed;
}
我想为定义如下的事件创建一个 Observable:
public event Func<Exception, Task> Closed;
我目前的代码是这样的:
Observable.FromEvent<Func<Exception, Task>, Unit>(h => hub.Closed += h, h=> hub.Closed -= h);
编译正常,但抛出运行时异常:
System.ArgumentException: 'Cannot bind to the target method because its signature or security transparency is not compatible with that of the delegate type.'
我觉得我做的不对。我不习惯从不遵循 EventArgs 模式的事件创建可观察对象
编辑:为了清楚起见,这是经典事件处理的完整代码:
class Program
{
static async Task Main(string[] args)
{
var hub = new HubConnectionBuilder().WithUrl("http://localhost:49791/hubs/status")
.Build();
hub.On<Status>("SendAction", status => Console.WriteLine($"Altitude: {status.Altitude:F} m"));
await hub.StartAsync();
hub.Closed += HubOnClosed;
while (true)
{
}
}
private static Task HubOnClosed(Exception arg)
{
Console.WriteLine("The connection to the hub has been closed");
return Task.CompletedTask;
}
}
请尝试以下操作,但不要使用您想要的签名,而是要尝试:
class Program
{
public delegate void ClosedEventHandler(object sender, Func<Exception, Task> e);
public ClosedEventHandler Closed { get; set; }
static void Main(string[] args)
{
Program hub = new Program();
hub.Closed = hub.SomethingToDoWhenClosed;
Observable
.FromEventPattern<ClosedEventHandler, Func<Exception, Task>>(
h => hub.Closed += h,
h => hub.Closed -= h)
.Subscribe(x =>
{
// this is hit
});
hub.Closed(hub, e => null);
}
public void SomethingToDoWhenClosed(object sender, Func<Exception, Task> e)
{
}
}
这样的事情能解决问题吗?
class Program
{
public event Func<Exception, Task> Closed;
static void Main(string[] args)
{
Program p = new Program();
IObservable<Unit> closedObservable = Observable.Create<Unit>(
observer =>
{
Func<Exception, Task> handler = ex =>
{
observer.OnNext(Unit.Default);
return Task.CompletedTask;
};
p.Closed += handler;
return () => p.Closed -= handler;
});
}
}
Observable.Create()
是针对此类异常情况的有用回退。
顺便说一句,有一个带有非 void 返回委托的事件是非常奇怪的,因为引发事件的代码只会看到 运行 最后一个处理程序的值 - 除非它引发该事件以某种非标准方式。但是,由于它是库代码,所以这不是你能控制的!
您需要转换重载。每次我看这个东西我都会关门:
IObservable<TEventArgs> Observable.FromEvent<TDelegate, TEventArgs>(
Func<Action<TEventArgs>, TDelegate> conversion,
Action<TDelegate> addHandler,
Action<TDelegate> removeHandler>
)
所以在我们的例子中,TEventArgs
是Exception
,而TDelegate
是Func<Exception, Task>
,所以你需要把Action<Exception>
转换成Func<Exception, Task>>
],换句话说:Func<Action<Exception>, Func<Exception, Task>>
。我假设转换看起来像这样:a => e => {a(e); return Task.CompletedTask; }
.
System.Reactive 需要此转换函数,因为它需要使用适当的委托订阅事件,并以某种方式挂钩您的 code/RX 管道代码。在这种情况下,a(e)
基本上是 RX 管道,然后传递异常以稍后在反应管道中处理。
完整代码:
class Program
{
static async Task Main(string[] args)
{
Program.Closed += Program.HubOnClosed;
Observable.FromEvent<Func<Exception, Task>, Exception>(
a => e => {a(e); return Task.CompletedTask; },
h => Program.Closed += h,
h => Program.Closed -= h
)
.Subscribe(e =>
{
Console.WriteLine("Rx: The connection to the hub has been closed");
});
Program.Closed.Invoke(null);
Program.Closed.Invoke(null);
}
private static Task HubOnClosed(Exception arg)
{
Console.WriteLine("The connection to the hub has been closed");
return Task.CompletedTask;
}
public static event Func<Exception, Task> Closed;
}