Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#重写方法上的可选参数_C#_.net_Overriding_Optional Parameters - Fatal编程技术网

C#重写方法上的可选参数

C#重写方法上的可选参数,c#,.net,overriding,optional-parameters,C#,.net,Overriding,Optional Parameters,在.NET Framework中,重写该方法时,似乎存在可选参数问题。以下代码的输出为: “bbb” “aaa” . 但我期望的结果是: “bbb” “bbb” .有解决办法吗。我知道可以用方法重载来解决这个问题,但我想知道原因是什么。代码在Mono中也可以正常工作 class Program { class AAA { public virtual void MyMethod(string s = "aaa") { Con

在.NET Framework中,重写该方法时,似乎存在可选参数问题。以下代码的输出为: “bbb” “aaa” . 但我期望的结果是: “bbb” “bbb” .有解决办法吗。我知道可以用方法重载来解决这个问题,但我想知道原因是什么。代码在Mono中也可以正常工作

class Program
{
    class AAA
    {
        public virtual void MyMethod(string s = "aaa")
        {
            Console.WriteLine(s);
        }

        public virtual void MyMethod2()
        {
            MyMethod();
        }
    }

    class BBB : AAA
    {
        public override void MyMethod(string s = "bbb")
        {
            base.MyMethod(s);
        }

        public override void MyMethod2()
        {
            MyMethod();
        }
    }

    static void Main(string[] args)
    {
        BBB asd = new BBB();
        asd.MyMethod();
        asd.MyMethod2();
    }
}
您是否尝试过:

 public override void MyMethod2()
    {
        this.MyMethod();
    }

因此,您实际上告诉您的程序使用覆盖的方法。

您可以通过调用:

this.MyMethod();
(在
MyMethod2()
中)

它是否是一个bug是很棘手的;不过,这看起来确实不一致。Resharper警告您不要更改覆盖中的默认值(如果有帮助);p当然,resharper也会告诉您
这个。
是多余的,并提供给您删除它。。。这改变了行为-因此resharper也不是完美的

我同意你的说法,它看起来确实是一个编译器错误。我需要仔细地看一看,以确定。。。你需要埃里克的时候他在哪里


编辑:

这里的关键点是语言规范;让我们看看§7.5.3:

例如,方法调用的候选集不包括标记为override的方法(§7.4),如果派生类中的任何方法适用(§7.6.5.1),则基类中的方法不是候选方法

(事实上,§7.4明确忽略了
覆盖
方法)

这里有些冲突。。。。它指出,如果派生类中存在适用的方法,则不使用基方法,这将导致我们使用派生方法,但同时,它表示不考虑标记为
override
的方法

但是,§7.5.1.1规定:

对于类中定义的虚拟方法和索引器,参数列表从函数成员的最特定声明或重写中选取,从接收方的静态类型开始,搜索其基类

然后§7.5.1.2解释了调用时如何评估值:

在函数成员调用的运行时处理过程中(§7.5.4),参数列表的表达式或变量引用按从左到右的顺序计算,如下所示:

…(剪断)

当从具有相应可选参数的函数成员中省略参数时,将隐式传递函数成员声明的默认参数。因为这些参数总是常量,所以它们的计算不会影响其余参数的计算顺序

这明确强调了它正在查看参数列表,该列表先前在§7.5.1.1中定义为来自最具体的声明或覆盖。这似乎是合理的,即§7.5.1.2中提到的“方法声明”,因此传递的值应该从最派生到静态类型


这表明:csc有一个bug,它应该使用派生的版本(“bbb bbb”),除非它被限制(通过
base.
,或强制转换为基类型)查看基方法声明(§7.6.8)。

这可能是由于不明确,编译器优先考虑基类/超类。下面对类BBB的代码进行了更改,添加了对
this
关键字的引用,给出了输出“BBB BBB”:

class BBB : AAA
{
    public override void MyMethod(string s = "bbb")
    {
        base.MyMethod(s);
    }

    public override void MyMethod2()
    {
        this.MyMethod(); //added this keyword here
    }
}
它暗示的一点是,无论何时调用类的当前实例上的属性或方法,都应该始终使用
this
关键字作为最佳实践

如果base和child方法中的这种歧义甚至没有引起编译器警告(如果不是错误的话),我会担心,但如果它引起了警告,那么我想这是看不见的

==================================================================

<强>编辑:< /强>考虑以下链接中的示例摘录:

陷阱:可选参数值是编译时 在使用可选参数时,有一件事,而且只有一件事需要记住。如果您牢记这一点,您可能会很好地理解并避免其使用中的任何潜在陷阱: 有一件事是这样的:可选参数是编译时,语法糖

陷阱:注意继承和接口实现中的默认参数

现在,第二个潜在陷阱与继承和接口实现有关。我将用一个谜题来说明:

   1: public interface ITag 
   2: {
   3:     void WriteTag(string tagName = "ITag");
   4: } 
   5:  
   6: public class BaseTag : ITag 
   7: {
   8:     public virtual void WriteTag(string tagName = "BaseTag") { Console.WriteLine(tagName); }
   9: } 
  10:  
  11: public class SubTag : BaseTag 
  12: {
  13:     public override void WriteTag(string tagName = "SubTag") { Console.WriteLine(tagName); }
  14: } 
  15:  
  16: public static class Program 
  17: {
  18:     public static void Main() 
  19:     {
  20:         SubTag subTag = new SubTag();
  21:         BaseTag subByBaseTag = subTag;
  22:         ITag subByInterfaceTag = subTag; 
  23:  
  24:         // what happens here?
  25:         subTag.WriteTag();       
  26:         subByBaseTag.WriteTag(); 
  27:         subByInterfaceTag.WriteTag(); 
  28:     }
  29: } 
会发生什么?即使每种情况下的对象都是标记为“SubTag”的SubTag,您也会得到:

1:子标签 2:基本标签 3:ITag

但请记住确保:

不要在默认参数席中插入新的默认参数,这可能导致不可预测的行为,可能不必引发语法错误——添加到列表的末尾或创建新方法。 在继承层次结构和接口中使用默认参数时要格外小心——根据预期使用情况选择最合适的级别来添加默认值


============================================================================

我想这是因为这些默认值在编译时是固定的。如果您使用reflector,您将在BBB中看到MyMethod2的以下内容

public override void MyMethod2()
{
    this.MyMethod("aaa");
}

这里值得注意的一点是,每次都会调用重写的版本。将覆盖更改为:

public override void MyMethod(string s = "bbb")
{
  Console.Write("derived: ");
  base.MyMethod(s);
}
输出为:

derived: bbb
derived: aaa
类中的方法可以执行以下一个或两个操作:

void myfunc(int optional){ /* Some code here*/ } //Function implementation
void myfunc(){ myfunc(5); } //Default arguments implementation
myfunc(); //Call using the default arguments
  • 它定义了一个供其他代码调用的接口
  • 它定义了调用时要执行的实现
  • 它可能不会同时做这两件事,因为抽象方法只做前者

    BBB
    中,调用
    MyMethod()
    调用
    AAA
    中定义的方法

    using System;
    
    class Base
    {
        public virtual void M(int x)
        {
            // This isn't called
        }   
    }
    
    class Derived : Base
    {
        public override void M(int x = 5)
        {
            Console.WriteLine("Derived.M: {0}", x);
        }
    
        public void RunTests()
        {
            M();      // Prints Derived.M: 0
        }
    
        static void Main()
        {
            new Derived().RunTests();
        }
    }
    
    void myfunc(int optional = 5){ /* Some code here*/ } //Function implementation
    myfunc(); //Call using the default arguments
    
    void myfunc(int optional){ /* Some code here*/ } //Function implementation
    void myfunc(){ myfunc(5); } //Default arguments implementation
    myfunc(); //Call using the default arguments
    
    void myfunc(int optional){ /* Some code here*/ } //Function implementation
    myfunc(5); //Call and embed default arguments
    
    class bassClass{ public virtual void someMethod()}
    class subClass :bassClass{ public override void someMethod()} //Legal
    //The following is illegal, although it would be called as someMethod();
    //class subClass:bassClass{ public override void someMethod(int optional = 5)} 
    
    void myfunc(int optional = 5){ /* Some code here*/ } //Function with default
    void myfunc(){ /* Some code here*/ } //No arguments
    myfunc(); //Call which one?, the one with no arguments!