Task.Run中的动态

时间:2014-12-29 14:25:04

标签: c# .net dynamic async-await

我在一个不相关的类中有一个同名的长任务。我试图使用动态的常用方法使用此代码。 我收到了以下错误

  

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException未被用户代码处理    Message =无法将类型'void'隐式转换为'object'

我试图将代码隔离到以下

class Program
{
    static void Main(string[] args)
    {
        MainAsync();
        Console.ReadKey();
    }
    static async void MainAsync()
    {
        var classA = new ClassA();
        var classB = new ClassB();
        await RunTask1(classA);
        await RunTask1(classB);
        await RunTask(classA);
        await RunTask(classB);
    }
    static async Task RunTask(dynamic val)
    {
        await Task.Run(() => val.CommonLongRunningTask());
    }
    static async Task RunTask1(ClassA val)
    {
        await Task.Run(() => val.CommonLongRunningTask());
    }
    static async Task RunTask1(ClassB val)
    {
        await Task.Run(() => val.CommonLongRunningTask());
    }
}
internal class ClassA
{
    public void CommonLongRunningTask()
    {
        Console.WriteLine("Class A CommonLongRunningTask");
    }
}
internal class ClassB
{
    public void CommonLongRunningTask()
    {
        Console.WriteLine("Class B CommonLongRunningTask");
    }
}

如果我传递对象本身(RunTask1)而不是动态它可以工作。我试图了解当我在动态传递时发生了什么。

4 个答案:

答案 0 :(得分:6)

我还没能用语言跟踪它,但看起来你不能拥有带动态表达的lambda表达式。 更新:涉及dynamic的表达式始终为dynamic类型,无论是否有无效方法调用,请参阅语言方面更新

语句lambda起作用:

private static async Task RunTask(dynamic val)
{
    await Task.Run(() =>
    {
        val.CommonLongRunningTask();
    });
}

更新

有效地,这里发生的是编译器遇到这个时:

() => val.CommonLongRunningTask()

它将其解释为相当于:

() => {return val.CommonLongRunningTask();}

...因为它在编译时并不知道你在val上调用的任何内容都不会返回任何内容。在运行时,它遇到val.CommonLongRunningTask()类型的表达式void但不能return表达甚至最小公分母object,因此会抛出异常消息Cannot implicitly convert type 'void' to 'object'

如果您按如下方式重新编写RunTask,则会有同样的例外情况:

    private static async Task RunTask(dynamic val)
    {
        await Task.Run(() =>
        {
            return val.CommonLongRunningTask();
        });
    }

语言方面

经过一番研究/讨论之后,C#规范第7.5.2节详细说明了为什么会这样。基本上任何涉及动态的东西本身都是动态类型(因为在编译时编译器无法知道事物是如何绑定的)因此它会查看" val.CommonLongRunningTask()"作为返回动态的东西(因此当它意识到它在运行时是无效的时候会出现运行时错误)。

答案 1 :(得分:1)

为您的常用方法创建一个界面:

public interface ICommonTask
{
    void CommonLongRunningTask();
}

在两个类中实现它并改为使用它:

static async Task RunTask(ICommonTask val)

class ClassA : ICommonTask

class ClassB : ICommonTask

答案 2 :(得分:1)

问题在于:

() => val.CommonLongRunningTask()

转化为某种东西:

delegate
{
    return val.CommonLongRunningTask();
}

哪个会在运行时失败,因为您绑定的CommonLongRunningTask方法没有返回类型。

答案 3 :(得分:0)

我只是遇到了同样的问题,并且为了回应彼得的回答为什么它发生了,我提出了一个解决方案,让动态void方法的Task.Run无论如何都能正常工作。< / p>

重写:

() => val.CommonLongRunningTask()

作为

() => ForceDynamicExpressionBackToVoid(() => val.CommonLongRunningTask())

使用以下方法:

private void ForceDynamicExpressionBackToVoid(Action @dynamic)
{
    @dynamic.Invoke();
}

需要注意的是,您必须知道在运行时最终得到的任何CommonLongRunningTask都会保证返回void,否则您将获得另一个异常。