我正在从C#中的ActionScript移植/重写我们的生产系统(闪存到统一)。
我遇到了C#的奇怪行为,导致我无法实现我们的设计。
以下代码重现了该问题:
using System;
using System.Collections.Generic;
namespace DotNetTest
{
class MainClass
{
public static void Main (string[] args)
{
// first, expected behaviour
Specific s = new Specific ();
N data = new N ();
Console.WriteLine (s.foo(data));
// now I'm going to push s on a stack
Stack<Generic> stack = new Stack<Generic> ();
stack.Push (s);
// and use it again: different behaviour !
var s2 = stack.Peek ();
Console.WriteLine (s2.foo (data));
}
}
public class M
{
}
public class N : M
{
}
public class Generic
{
public String foo(M n)
{
return "I don't want this generic foo for M";
}
}
public class Specific : Generic
{
public String foo(N m)
{
return "I want this specific foo for N (child of M)";
}
}
}
当我运行此代码时,我得到以下结果
I want this specific foo for N (child of M)
I don't want this generic foo for M
在Main
我有一个变量s
。第一个Console.Write
使用
在foo
中正确重载方法Specific
。
在我将s
推入堆栈并再次弹出之后,相同
第二个Console.Write
中的函数调用意外地使用foo
来自Generic
。
显然,在这个示例中,我可以将s2
转换为Generic
以获得预期结果。
似乎.net(或我使用的单声道)取决于运行时绑定
它决定了使用什么方法。
在我们的生产系统中,铸造是不可取的。
有没有办法在第二次通话中获得预期结果?
提前致谢,
克里斯
答案 0 :(得分:2)
这是因为您没有重载方法,也没有将基本方法声明为虚拟,因此不使用虚方法分派。这意味着方法调用将在编译时绑定,因此用于调用方法的类型的引用将确定调用哪个方法,而不是引用对象的类型< / em>的
您应该在foo()
内声明Specific
的行上收到警告,因为它隐藏了父类型中的成员而您没有指定new
。注意警告!
要解决此问题,请将Generic
中的方法声明为virtual
:
public virtual String foo(M n)
并在Specific
:
public override String foo(M n)
在这种情况下,调用将在运行时绑定到此方法的特定重写,使用调用该方法的实际对象的vtable。
请注意,覆盖不能更改被覆盖方法的参数类型,因此您无法在foo()
中声明Specific
以接受N
对象。但是,如果需要,您可以指定其他重载。 foo(M)
重载可能需要查看传递对象的类型以确定如何处理它。 (或者,更好的是,如果传递foo(M)
或M
,则N
无关紧要。
可能只应接受N
的覆盖的可能实现(虽然这有点糟糕的设计):
public override String foo(M m)
{
if (m == null) { throw new ArgumentNullException("m"); }
N n = m as N;
if (n == null) { throw new ArgumentException("Must be an N instance.", "m"); }
return foo(n);
}
public virtual String foo(N n)
{
// Use the n variable
}
答案 1 :(得分:2)
如果您想致电foo
Specific
,则必须投票:
var s2 = (Specific)stack.Peek();
这会给你想要的结果。
如果您将Generic
类设为通用
public class Generic<T> where T : M
{
public virtual String foo(T n)
{
return "I don't want this generic foo for M";
}
}
然后你可以这样做:
public class Specific : Generic<N>
{
public override string foo(N n)
{
return "I want this specific foo for N (child of M)";
}
}
但是你需要更多的解决方法。