通过动态类型调用 C# 方法引发异常

Ska*_*ary 1 c# inheritance casting

我正在使用反射调用带参数的方法(准确地说是动态的)。编译时方法参数与传入参数匹配时调用没有问题;但是如果我使用基本类型(在签名中,在运行时类型总是匹配),如果运行时参数匹配,我会得到错误(在运行时)。

下面的例子:

public class WorkerAbstraction 
{
    object _actualWorker  = null;

    public WorkerAbstraction (object actualWorker)
    {
         _actualWorker = actualWorker;
    }

    public void DoWorkAbs ( AbstractBaseClass args )
    {
         ((dynamic)_actualWorker).DoWork (args); //--> here is where the exception is rised
    }
}

public class ActualWorker<T> where T : AbstractBaseClass 
{
    public void DoWork ( T args )
    {
         ...
    }
}

public class Person : AbstractBaseClass { ... }
Run Code Online (Sandbox Code Playgroud)

现在,如果我以这种方式测试上面的代码:

WorkerAbstraction wab = new WorkerAbstraction ( new ActualWorker<Person> () );
Person person = new Person();

wab.DoWorkAbs (person);
Run Code Online (Sandbox Code Playgroud)

然后我得到那个例外:

'ActualWorker.DoWork(Person)' 的最佳重载方法匹配有一些无效参数。

我在运行时检查过,当异常出现时(在 DoWorkAbs 中):_actualWorker 是 ActualWorker args 是 Person

然后我修改了代码并再次检查:

    public void DoWorkAbs ( AbstractBaseClass args )
    {
         ((dynamic)_actualWorker).DoWork ((Person)args); //--> that explicit cast solve the problem
    }
Run Code Online (Sandbox Code Playgroud)

我不明白的是问题的根源。好的,这种方法不是 100% 安全的,但在我的情况下,场景对象和参数在运行时都是正确的。

发生什么了?

-----编辑:迭代一个工作示例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestReflection
{
    class Program
    {
         static void Main(string[] args)
         {
              Person person = new Person() { Name = "Michelle" };
              WorkerAbstraction wab = new WorkerAbstraction(new ActualWorker<Person>());


        wab.DoWorkAbs(person);
        Console.ReadKey();
    }
}

public class WorkerAbstraction
{
    object _actualWorker = null;

    public WorkerAbstraction(object actualWorker)
    {
        _actualWorker = actualWorker;
    }

    public void DoWorkAbs(AbstractBaseClass args)
    {
        ((dynamic)_actualWorker).DoWork(args); // --> raise exception
        //((dynamic)_actualWorker).DoWork((Person)args); // --> working fine
    }
}

public class ActualWorker<T> where T : AbstractBaseClass
{
    public void DoWork(T args)
    {
        args.SayHello();
    }
}

public class Person : AbstractBaseClass
{
    public Person()
    {
    }

    public string Name { get; set; }

    public override void SayHello()
    {
        Console.WriteLine ($"{this.Name} say hello.");
    }
}

public abstract class AbstractBaseClass 
{
    public abstract void SayHello();
}
Run Code Online (Sandbox Code Playgroud)

}

Jon*_*eet 7

问题在于编译器会记住动态绑定参数的编译时类型。的编译时类型argsAbstractBaseClass,因此这就是动态查找期间使用的类型。你只是在调用的目标上是动态的,而不是在参数中。

如果您希望参数中的绑定也是动态的,则需要单独执行此操作。例如,这不会引发异常,因为重载决议“知道”参数类型也是动态的:

public void DoWorkAbs(AbstractBaseClass args)
{
    dynamic target = _actualWorker;
    dynamic dynamicArgs = args;
    target.DoWork(dynamicArgs);
}
Run Code Online (Sandbox Code Playgroud)

这个问题可以更简单地演示,没有泛型或抽象类:

public class Test
{
    public void M(string x)
    {
    }
    
    static void Main()
    {
        dynamic target = new Test();
        string s = "text";
        // Succeeds; it's looking for M(string) (or a compatible method)
        // at execution time, based on the compile-time type of s
        target.M(s);
        
        // Succeeds; it will look for M(string) (or a compatible method)
        // at execution time, based on the execution-time type of the
        // value of d.
        dynamic d = "text";
        target.M(d);
                        
        object o = "text";
        // Fails; it's looking for M(object) (or a compatible method)
        // at execution time, based on the compile-time type of o.
        target.M(o);
    }
}
Run Code Online (Sandbox Code Playgroud)

就您如何处理这个问题而言,如果可能的话,我强烈建议您尽量避免在此处使用动态类型。