如果您有一个使用派生类型重载的方法,则在运行时调用的方法取决于您的变量的类型,即使基础对象实际上是派生类型:
class Program
{
static void Main(string[] args)
{
BaseClass theBaseObject = new BaseClass
{
Foo = "FOO"
};
DerivedClass theDerivedObject = new DerivedClass
{
Foo = "FOO",
Bar = "BAR"
};
Processor processor = new Processor();
Console.WriteLine(processor.Compose(theBaseObject));
Console.WriteLine(processor.Compose(theDerivedObject));
Console.WriteLine(processor.Compose((BaseClass) theDerivedObject));
}
}
public class Processor
{
public string Compose(BaseClass item)
{
return item.Foo;
}
public string Compose(DerivedClass item)
{
return Compose((BaseClass)item) + item.Bar;
}
}
public class BaseClass
{
public string Foo { get; set; }
}
public class DerivedClass : BaseClass
{
public string Bar { get; set; }
}
实际输出:
FOO
FOOBAR
FOO
我想找到一种方法来改变这种行为,以便为给定的参数调用最具体的方法。
期望的输出:
FOO
FOOBAR
FOOBAR // because theDerivedObject is an instance of DerivedClass
这将允许对一堆项目进行特定处理,所有项目都来自基础。
这可能在c#中吗?
编辑:
澄清 - 在实践中不会有明确的演员表,因为这些项目可能会出现在混合类型的集合列表中:
使用没有显式强制转换的列表的示例:
foreach (BaseClass item in new []{ theBaseObject, theDerivedObject })
{
Console.WriteLine(processor.Compose(item));
}
实际输出:
FOO
FOO
期望的输出:
FOO
FOOBAR
显然演员阵容仍在发生 - 但要删除它并不容易。
答案 0 :(得分:8)
这段代码让我想起了Haddocks' Eyes诗:
但我在考虑一个计划 染一个绿色的胡须,
并且总是使用如此大的风扇
他们无法看到。
首先,您的代码创建了一个子类,但随后它将对象强制转换回基类,因此编译器无法看到实际类型!您的示例中的重载在编译时解决,因此将调用Compose(BaseClass item)
。
你可以通过隐藏所有重载并暴露一个采用基类并执行动态转换的方法来解决问题并让.NET动态地解决重载:
public class Processor {
public string Compose(BaseClass item) {
return ComposeImpl((dynamic)item);
}
private string ComposeImpl(BaseClass item) {
return item.Foo;
}
private string ComposeImpl(DerivedClass item) {
return ComposeImpl((BaseClass)item) + item.Bar;
}
}
“神奇”就在这一行:
return ComposeImpl((dynamic)item);
item
被强制转换为dynamic
,告诉系统必须根据ComposeImpl
对象的运行时类型选择BaseClass
的实际重载
答案 1 :(得分:2)
首先,您获得结果的原因是您获得的结果是因为您明确声明theDerivedObject as DerivedClass
因此它将DerivedClass
作为参数并使用(BaseClass) theDerivedObject)
你低音地告诉编译器(纠正我,如果编译器错误)或程序采用参数BaseClass
的方法
您要做的是以下选项之一
第1名:
public class Processor
{
public string Compose(BaseClass item)
{
if (item is DerivedClass)
return Compose((DerivedClass) item);
return item.Foo;
}
public string Compose(DerivedClass item)
{
return item.Foo + item.Bar;
}
}
2号
public class Processor
{
public string Compose(BaseClass item)
{
if (item is DerivedClass)
{
var derived = (DerivedClass) item;
return derived.Foo + derived.Bar;
}
return item.Foo;
}
}
或者您可能想要覆盖ToString()
public class Processor
{
public string Compose(BaseClass item)
{
var @class = item as DerivedClass;
return @class?.ToString() ?? item.ToString();
}
}
public class BaseClass
{
public string Foo { get; set; }
public override string ToString()
{
return Foo;
}
}
public class DerivedClass : BaseClass
{
public string Bar { get; set; }
public override string ToString()
{
return Foo + Bar;
}
}
如果您不使用C#6.0,那么处理器将是以下
public class Processor
{
public string Compose(BaseClass item)
{
var @class = item as DerivedClass;
if (@class != null)
{
return @class.ToString();
}
return item.ToString();
}
}
答案 2 :(得分:2)
public class Processor
{
public string Compose(BaseClass item)
{
return item.Compose();
}
}
public class BaseClass
{
public string Foo { get; set; }
public virtual string Compose()
{
return Foo;
}
}
public class DerivedClass : BaseClass
{
public string Bar { get; set; }
public override string Compose()
{
return base.Compose() + Bar;
}
}
答案 3 :(得分:2)
难怪你得到了你得到的东西。你重载Compose
方法。您为基类声明了两个签名,另一个用于派生。然后你多次调用该方法:
processor.Compose(theBaseObject)
来电Compose(BaseClass item)
processor.Compose(theDerivedObject)
来电Compose(DerivedClass item)
。这里发生了几种方法可能适合于调用。编译器选择层次结构中具有最接近基类的方法,该方法本身为DerivedClass
。processor.Compose((BaseClass) theDerivedObject))
致电Compose(BaseClass item)
,因为您已经告诉他这样做了。它没有选择的选择。您可能会对虚拟成员覆盖类感兴趣。见
public class BaseClass
{
public string Foo { get; set; }
public virtual string GetComposeResult() { return Foo; }
}
public class DerivedClass : BaseClass
{
public string Bar { get; set; }
public override string GetComposeResult() { return Foo + Bar; }
}
在这种情况下,使用以下代码可以达到预期效果。
public class Processor
{
public string Compose(BaseClass item) { return item.GetComposeResult(); }
}