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

Cod*_*-EZ 7 .net c#

我有两个类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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

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

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

            secondProcess.Calculate(x: 1, y: 2);
            firstProcess.Calculate(x: 1, y: 2);
Run Code Online (Sandbox Code Playgroud)

产量

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

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

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

Jon*_*Jon 9

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

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

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

firstProcess.Calculate(1, 2);
Run Code Online (Sandbox Code Playgroud)

运行时,参数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"
Run Code Online (Sandbox Code Playgroud)

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