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



我正在使用反射调用一个带有参数的方法(确切地说是动态的(。当方法的参数和传递的参数在编译时匹配时,调用没有问题;但是,如果我使用基类型(在签名中,在运行时类型总是匹配(,如果运行时参数匹配,则会发生事件(在运行时(。

以下示例:

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

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

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

然后我得到了一个例外:

与"ActualWorker"匹配的最佳重载方法。DoWork(人('有一些无效的参数。

我在运行时检查过,当异常上升时(在DoWorkAbs中(:_actualWorker是actualWorkerargs是个人

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

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

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

那到底发生了什么?

-----编辑:itegrated一个工作示例

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();
}

}

问题是编译器会记住动态绑定参数的编译时类型。args的编译时类型是AbstractBaseClass,所以这就是动态查找过程中使用的类型。您只在调用的目标上是动态的,而不是在参数中。

如果希望参数中的绑定也是动态的,则需要单独执行。例如,这不会引发异常,因为重载解决方案";知道";参数类型也是动态的:

public void DoWorkAbs(AbstractBaseClass args)
{
dynamic target = _actualWorker;
dynamic dynamicArgs = args;
target.DoWork(dynamicArgs);
}

不过,这个问题可以更简单地证明,不需要泛型或抽象类:

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);
}
}

关于如何处理,如果可能的话,我强烈建议您尽量避免在这里进行动态键入。

最新更新