5

一段时间以来,我一直在使用 .NET 4 以有限的方式使用可选参数。我最近思考了可选参数是如何实现的,我突然想到了一些东西。

举个例子:

public interface ITest
{
    void Go(string val = "Interface");
}

实现这一点的唯一要求是实现最大形式的签名void Go (string val),实际上我们不需要实现可选参数,因为编译器会处理该连接(尽管使用可选参数的选项在使用时不可用直接的具体类)。

话虽如此,为了在两个地方都提供功能,并使其在实现中可发现,可以在接口和实现中实现可选声明。事实上,生产力工具 ReSharper 会在实现接口时自动将此可选声明拉到具体类型。这似乎是合乎逻辑的事情。然而...

是什么阻止我们在具体类型和接口中使用不同的值?这在今天早上敲响了我的警钟,好像有人进去更改该值,却忘记将其保留在覆盖/接口的继​​承中,行为将完全不同,具体取决于您访问对象的方式。调试起来可能非常令人沮丧。

试试这个 LINQpad 脚本:

void Main()
{
    IA iface = new A();
    A cls = new A();

    iface.Go();
    cls.Go();
}

interface IA
{
    void Go(string val = "Interface");
}

class A : IA
{
    public void Go(string val = "Class") { val.Dump(); }
}

输出将是:

Interface
Class

这让我想到了我的实际问题:

问题:

我们可以用什么(如果有的话?)方法来保护它,而不会失去使用具体类中的可选参数变体的能力,因此它对编码器来说是可发现/可读的?

有没有人遇到过这个问题?你是怎么解决的?是否有任何最佳实践可以帮助防止这个问题在多开发人员大规模代码库中变得普遍?

4

2 回答 2

3

没有什么能阻止你这样做。使用可选参数将值烘焙到方法调用中。因此,正如您所看到的,调用IA.Go()A.Go()为您提供不同的值,因为它们被编译为IA.Go("Interface")A.Go("Class").

绕过它的方法(意味着在实现中只提供“默认”参数)是有一个重载而不是可选参数:

interface IA
{
    void Go();
    void Go(string val);
}

public class A : IA
{
    public void Go()
    {
        Go("Class");
    }
    public void Go(string val) { val.Dump(); }
}

其他一些选项:

  • 添加一个检查该条件的单元测试。但是,您必须为每个实现都这样做。
  • 研究创建自定义代码分析规则
于 2012-10-17T18:30:23.797 回答
1

我认为这是 C# 的本质,你必须忍受它。看下面的例子。在没有可选参数的情况下是否相同,即行为因您访问对象的方式而异。

class Program
{
    static void Main(string[] args)
    {
        IA iface = new A();
        A cls = new A();

        iface.Test();
        cls.Test();

        Console.ReadKey();
    }
}

interface IA
{
    void Test();
}

class A : IA
{
    public void Test()
    {
        Console.WriteLine("Class");
    }

    void IA.Test()
    {
        Console.WriteLine("Interface");
    }
}
于 2012-10-17T18:24:41.113 回答