Создать Observable из нестандартного события (без EventArgs / EventHandler) - PullRequest
0 голосов
/ 26 сентября 2018

Я хотел бы создать Observable для события, определенного следующим образом:

public event Func<Exception, Task> Closed;

Текущий код, который у меня есть, таков:

Observable.FromEvent<Func<Exception, Task>, Unit>(h => hub.Closed += h, h=> hub.Closed -= h); 

Он компилируется нормально, но выдаетэто исключение времени выполнения:

System.ArgumentException: 'Невозможно связать с целевым методом, поскольку его подпись или прозрачность безопасности не совместимы с таковой для типа делегата.'

Я чувствую, что делаю это неправильно.Я не привык создавать наблюдаемые из событий, которые не следуют шаблону 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;
    }
}

Ответы [ 3 ]

0 голосов
/ 26 сентября 2018

Делает ли что-то подобное свое дело?

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() - полезный запасной вариант для необычных случаев, подобных этому.

Кстати, очень странно иметь событиес невозвратным возвращающим делегатом, так как код, который вызывает событие, увидит только значение последнего обработчика, который будет запущен - если только он не вызовет событие каким-либо нестандартным способом.Но, поскольку это библиотечный код, это не в ваших руках!

0 голосов
/ 26 сентября 2018

Вам нужна перегрузка преобразования.Я закрываю каждый раз, когда просматриваю эту вещь:

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 нужна эта функция преобразования, потому что она должна подписаться на событие с соответствующим делегатом и каким-то образом подключить ваш код / ​​код сантехнического приемника.,В этом случае 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;
}
0 голосов
/ 26 сентября 2018

Попробуйте следующее, не используя нужную подпись, а попробуйте:

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)
        {
        }
}
...