当在派生类中定义了重载时,c#晚期绑定方法重载不起作用
本文关键字:重载 绑定 不起作用 方法 派生 定义 | 更新日期: 2023-09-27 18:11:52
我需要在运行时使用c#后期绑定特性根据对象的类型调用方法重载。当调用发生时,所有重载都在同一个类中定义时,它工作得很好。但是,当在派生类中定义重载时,它不会在运行时被绑定。
class BaseT
{}
class DerivedA : BaseT
{}
class DerivedB : BaseT
{}
class Generator
{
public void Generate(IEnumerable<BaseT> objects)
{
string str = "";
foreach (dynamic item in objects)
{
str = str + this.Generate(item); //throws an exception on second item
}
}
protected virtual string Generate(DerivedA a)
{
return " A ";
}
}
class DerivedGenertor : Generator
{
protected virtual string Generate(DerivedB b)
{
return " B ";
}
}
class Program
{
static void Main(string[] args)
{
List<BaseT> items = new List<BaseT>() {new DerivedA(), new DerivedB()};
var generator = new DerivedGenertor();
generator.Generate(items);
}
}
下面是另一个更清晰的例子:
class BaseT
{}
class DerivedA : BaseT
{}
class DerivedB : BaseT
{}
class DerivedC : BaseT
{ }
class Generator
{
public void Generate(IEnumerable<BaseT> objects)
{
string str = "";
foreach (dynamic item in objects)
{
str = str + this.Generate(item); //throws an exception on third item
}
}
public virtual string Generate(DerivedA a)
{
return " A ";
}
public virtual string Generate(DerivedC c)
{
return " C ";
}
}
class DerivedGenertor : Generator
{
public virtual string Generate(DerivedB b)
{
return " B ";
}
}
class Program
{
static void Main(string[] args)
{
List<BaseT> items = new List<BaseT>() {new DerivedA(), new DerivedC(), new DerivedB()};
dynamic generator = new DerivedGenertor();
generator.Generate(items);
}
}
您还需要将Generator
声明为动态的,以便对输入对象和被调用的方法具有动态解析。但是您必须将访问修饰符更改为public
或protected internal
才能做到这一点,因为您现在有一个外部解析的方法。
class BaseT
{ }
class DerivedA : BaseT
{ }
class DerivedB : BaseT
{ }
class Generator
{
public string Generate(IEnumerable<BaseT> objects)
{
string str = "";
dynamic generator = this;
foreach (dynamic item in objects)
{
str = str + generator.Generate(item);
}
return str;
}
protected internal virtual string Generate(DerivedA a)
{
return " A ";
}
}
class DerivedGenertor : Generator
{
protected internal virtual string Generate(DerivedB b)
{
return " B ";
}
}
class Program
{
static void Main(string[] args)
{
List<BaseT> items = new List<BaseT>() { new DerivedA(), new DerivedB() };
var generator = new DerivedGenertor();
string ret = generator.Generate(items);
}
}
你希望它如何被绑定?编译器将this.Generate(item)
调用绑定到唯一可能的候选对象:Generator.Generate(DerivedA a)
。这与绑定发生的时间无关;DerivedGenerator.Generate(DerivedB b)
不被认为是一个有效的候选,因为Generator
完全不知道它的存在,并且您通过静态类型的Generator
实例this
调用该方法(我注意到方法是protected
不是问题,即使它是public
,第二次调用也会失败)。基类为什么要知道派生类中定义的新虚方法的任何信息?
为了使此工作,您要么在基类Generator
中定义virtual Generate(DerivedB)
,并在DerivedGenerator
中覆盖,或者如果这不是一个选项,那么您在运行时使一切解决。
在您的情况下,正如Brian正确指出的那样,您需要将Generator
实例也设置为dynamic
,以便在适当的时候将Generate
调用绑定到DerivedGenerator.Generate
。否则,候选设置将仅限于Generator
知道的那些。
这将迫使你显著地重构你的代码,因为你还需要使Generate
至少为internal
或internal protected
。
另外,我应该注意到,调用dynamic.Generate(dynamic)
来让事情正常工作对我来说似乎是一个很大的代码气味,你可能在滥用c#的类型系统。我会考虑重构你的代码以获得更安全的解决方案。
我还建议您阅读Eric Lippert的精彩系列:Wizards and warriors,其中解释了一些对象层次结构如何不能用c#的类型系统很好地表达,以及您如何(但通常不应该)使用dynamic
在c#中实现双重分派,以规避它的一些限制。