8

我有两个类 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 的意外结果。.Net 如何处理这种情况?为什么.net优先考虑命名参数?

4

2 回答 2

10

方法调用的参数绑定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"

故事的寓意:命名参数和默认参数很方便,但它们(必然)实现的方式让您容易遇到不愉快的意外。当它们提供真正有形的好处时使用它们,而不是尽可能地使用它们。

于 2013-08-29T10:02:43.417 回答
0

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

因此,在您的示例中,x指的是第一个参数和y第二个参数。

于 2013-08-29T10:05:01.940 回答