How to make a generic delegate EndInvoke?

China☆狼群 提交于 2019-12-26 18:12:13

问题


So I have the following:

private delegate Foo1 GetFooAsync1(Foo1 foo1);
private delegate Foo2 GetFooAsync2(Foo2 foo2);
private delegate Foo3 GetFooAsync3(Foo3 foo3);
private delegate Foo4 GetFooAsync4(Foo4 foo4);

private FooAsync1 foo1;
private FooAsync2 foo2;
private FooAsync3 foo3;
private FooAsync4 foo4;

And the lists goes on and on, then inside a method I don't want to put a try catch on each EndInvoke, because sometimes it does throw an exception but it shouldn't stop the system, and continue with the other Foos.. And takes up so much space in the method if each had a try catch around it.

Is there a generic way to call end invoke? So I can return the expected result?

var result1 = foo1.EndInvoke(fooIAsyncResult);

回答1:


In order to achieve this in a generic way, you'd need to declare an extension method that overrides EndInvoke like this:

public static class DelegateExtensions
{
    public static TResult EndInvoke<TDelegate, TResult>(this TDelegate asyncCaller, IAsyncResult asyncResult) where TDelegate : System.Delegate
    {
        TResult result = default(TResult);

        try
        {
            result = asyncCaller.EndInvoke(asyncResult);
        }
        catch ( Exception ex)
        {
            LogExceptionMessageOrWhatever(ex.Message);
            throw;
        }

        return result;
    }
}

However, that procedure will generate a compiler error. Why? The System.Delegate class is a special class that cannot be used in generic constraints.

So can't you just get rid of the constraint, and use reflection to invoke the right procedure?

I suppose you could, but that defeats the purpose of using generics. A better solution would be to make your delegate generic, then rewrite the extension method to target only that delegate.

public delegate TFooResult GetFooAsync<TFooResult>();

public static class GetFooAsyncExtensions
{
    public static TFooResult EndInvoke<TFooResult>(this GetFooAsync<TFooResult> asyncCaller, IAsyncResult asyncResult)
    {
        TFooResult result = default(TFooResult);

        try
        {
            result = asyncCaller.EndInvoke(asyncResult);
        }
        catch ( Exception ex )
        {
            LogExceptionMessageOrWhatever(ex.Message);
            throw;
        }

        return result;
    }
}

Now you'd call EndInvoke like you normally would. The framework will automatically use your version.

private void Main()
{
    Foo1Result foo1 = null;

    var foo1Factory = new GetFooAsync<Foo1Result>(
        () =>
        {
            return new Foo1Result();
        });


    foo1Factory.BeginInvoke(
        callback: asyncResult =>
            {
                foo1 = foo1Factory.EndInvoke(asyncResult);
            },
            @object: null);
}


来源:https://stackoverflow.com/questions/21822569/how-to-make-a-generic-delegate-endinvoke

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!