简体   繁体   English

带默认参数值的c#方法没有参数会产生过载?

[英]c# method with default parameter value does not generate overload without parameter?

Recently, I wanted to add an optional parameter to an extension method. 最近,我想在扩展方法中添加一个可选参数。 The original method looked like this: 原始方法看起来像这样:

public static class Extensions {
    public static bool Foo(this IFoo target) {
        target.DoIt(true);
    }
}

This is obviously a simplified version but let's go from that. 这显然是一个简化版本,但让我们继续。

What I did is: 我做的是:

public static class Extensions {
    public static bool Foo(this IFoo target, bool flag = true) {
        target.DoIt(flag);
    }
}

All I did is introduce an optional parameter with a default value, right? 我所做的就是引入一个带有默认值的可选参数,对吧? What I was expecting was the compiler to generate the overloaded method, without the flag. 我期待的是编译器生成重载方法,没有标志。 This partially happened. 这部分发生了。 Any code that I recompiled was able to compile and execute without any problems, even without the parameter like this: 我重新编译的任何代码都能够编译和执行而没有任何问题,即使没有像这样的参数:

...
IFoo foo = new FooBar();
foo.Foo();
...

However, any code that was built against the previous version of Foo() did not work, throwing the following exception: 但是,任何针对以前版本的Foo()构建的代码都不起作用,抛出以下异常:

Unhandled Exception: System.MissingMethodException: Method not found: 'Boolean Models.Class1.Foo()'.
at DefaultParamsTests.Program.Main(String[] args)

This is obviously a problem for us as we do have a public API that our customers do leverage and this would be a breaking change. 这对我们来说显然是一个问题,因为我们有一个公共API,我们的客户可以利用它,这将是一个突破性的变化。

The solution is to explicitly create an overload: 解决方案是明确创建一个重载:

public static class Extensions {
    public static bool Foo(this IFoo target) {
        target.DoIt(true);
    }

    public static bool Foo(this IFoo target, bool ) {
        target.DoIt(true);
    }
}

However, Resharper does suggest that I could introduce an optional parameter for method foo. 但是,Resharper建议我可以为方法foo引入一个可选参数。

resharper引入可选参数

If I do follow the refactoring it basically does what I showed above. 如果我遵循重构它基本上做了我上面显示的。 However, that won't work for existing code. 但是,这对现有代码不起作用。

resharper重构

I have looked at the generated IL using both Reflector and dotPeek. 我使用Reflector和dotPeek查看了生成的IL。 Neither is showing the generation of the overload. 两者都没有显示过载的产生。

What am I missing? 我错过了什么?

In the case of default parameters, the actual call site is re-written to use the default value. 在默认参数的情况下,重写实际的调用站点以使用默认值。 This means an overload isn't generated at all, your calling code is modified! 这意味着根本不会产生过载,您的调用代码会被修改!

public void fun(int x = 3) { }

// in another file
fun();   // compiler re-writes to fun(3);
fun(7);  // fun(7) as expected

// actual code generated by the c# compiler
fun(3);
fun(7);

If you use optional parameters in C# you need to re-compile all callers when the function changes. 如果在C#中使用可选参数,则需要在function更改时重新编译所有调用方。 For this reason it is strongly discouraged to put these kind of methods on a public interface (eg called by other's code). 出于这个原因, 强烈建议不要将这些方法放在公共接口上(例如,由其他代码调用)。 Using them inside your own linked projects is fine, since they should all compile at the same time 在你自己的链接项目中使用它们很好,因为它们应该同时编译

Optional parameters in methods in C# are resolved at compile-time at the call site. C#中方法中的可选参数在编译时在调用站点解析。 Your calling code that omits the parameter expands to include the parameter, calling the two-parameter version. 省略参数的调用代码将扩展为包含参数,调用双参数版本。 This is the opposite of what you are presuming happens, with creating an overload. 这与你想象的相反,创造了一个过载。

The key here is that the compiler changes the caller of the method to include the default value. 这里的关键是编译器更改方法的调用者以包含默认值。

Take a look at the NEW code in dotPeek. 看看dotPeek中的新代码。

NOTE: You should delete the pdb files when using dotPeek on your own files! 注意:在自己的文件上使用dotPeek时应删除pdb文件!

Here is an extensions class with two methods (decompiled with dotPeek). 这是一个扩展类,有两个方法(用dotPeek反编译)。 One with a default and one without 一个有默认,一个没有

public static class Extensions
{
    public static bool Foo(this IFoo target)
    {
        return target.DoIt(true);
    }

    public static bool Foo(this IFoo2 target, bool doit = false)
    {
        return target.DoIt2(doit);
    }
}

Everything looks fine there. 那里的一切看起来很好。 Now look at the object calling the extension method (decompiled with dotPeek). 现在看一下调用扩展方法的对象(用dotPeek反编译)。

public class Bar
{
    public Bar(IFoo foo)
    {
        Extensions.Foo(foo);
    }

    public Bar(IFoo2 foo)
    {
        Extensions.Foo(foo, false);
    }
}

Notice that the caller to the IFoo2 extension method actually contains the value false to the method Foo of class Extensions. 请注意,IFoo2扩展方法的调用者实际上包含类Extensions的方法Foo的值false。 So your method Extensons.Foo now has two parameters in it. 所以你的方法Extensons.Foo现在有两个参数。 The first method the object and the second being the "default" parameter. 第一种方法是对象,第二种方法是“默认”参数。 So any code that was created with the first version will now fail because there is no method with only one parameter. 因此,使用第一个版本创建的任何代码现在都将失败,因为没有只有一个参数的方法。

Disable resharper around the methods and you won't see it pester you anymore :) 禁用方法周围的resharper,你不会再看到它纠缠你了:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM