C#:使用可选参数覆盖方法&命名参数:意外结果

时间:2013-08-29 09:57:31

标签: c# .net

我有两个类FirstProcess和Second Process

public class FirstProcess
    {
        public virtual void Calculate(int x, int y)
        {
            Console.WriteLine("First Process  X :{0} and Y{1}", x, y);
        }
    }
    public class SecondProcess : FirstProcess
    {

        public override void Calculate(int y, int x)
        {
            Console.WriteLine("Second Process X :{0} and Y :{1}", x, y);
        }
    }

我已经调用了如下的计算方法

  var secondProcess = new SecondProcess();
            var firstProcess = (FirstProcess) secondProcess;

            secondProcess.Calculate(x: 1, y: 2);
            firstProcess.Calculate(x: 1, y: 2);

输出

第二个过程X:1和Y:2

第二个过程X:2和Y:1

我得到意外的结果,X = 2,Y = 1.How .Net处理这种情况?为什么.net优先使用命名参数?

2 个答案:

答案 0 :(得分:9)

方法调用firstProcess.Calculate(x: 1, y: 2)的参数绑定在编译时完成,但方法调度在运行时完成因为方法是virtual

为了编译方法调用,编译器会看到x: 1, y: 2并需要将此命名参数列表解析为顺序索引的参数列表,以便发出适当的IL(在右侧的堆栈上推送参数顺序,然后调用方法)。

除了命名参数列表之外,编译器还有另外一条信息:firstProcess静态类型,即FirstProcess。现在我和你们都知道在运行时这将是一个SecondProcess实例,但编译器不知道(至少在一般情况下)。因此,它会查找FirstProcess.Calculate的参数列表,并看到x是第一个参数,y是第二个参数。这使得它可以像编写

一样编译代码
firstProcess.Calculate(1, 2);

运行时,参数12被推入堆栈,虚拟调用Calculate。当然,这最终会调用SecondProcess.Calculate,但参数名称在转换到运行时后仍未存活。 SecondProcess.Calculate接受1作为其第一个参数(y)和2作为其第二个参数(x),从而导致观察到的结果。

顺便说一句,这也是使用默认参数值时会发生的情况:

public class FirstProcess
{
    public virtual void Calculate(int x = 10)
    {
        Console.WriteLine("First Process  X :{0}", x);
    }
}

public class SecondProcess : FirstProcess
{
    public override void Calculate(int x = 20)
    {
        Console.WriteLine("Second Process  X :{0}", x);
    }
}

var secondProcess = new SecondProcess();
var firstProcess = (FirstProcess) secondProcess;

secondProcess.Calculate(); // "Second Process X: 20"
firstProcess.Calculate();  // "Second Process X: 10"

故事的寓意:命名和默认参数都很方便,但它们(必然)实现的方式让你容易受到不愉快的惊喜。当它们提供真正有形的好处时使用它们,而不是在可能的情况下使用它们。

答案 1 :(得分:0)

在解析命名参数时,编译器使用被调用方法的 static 类型,而不是 dynamic 类型。

因此,在您的示例中,x引用第一个参数,y引用第二个参数。