28

不确定这是否是一个愚蠢的问题,但我刚刚注意到这一点:

public interface IActivityDao : IDao<Activity>
{
    IList<Activity> GetAllSinceSequence(long sequence, int count);
}

public class ActivityDao : AbstractNHibernateDao<Core.Domain.Activity>, IActivityDao
{        
    public IList<Activity> GetAllSinceSequence(long sequence, int maxRecords)
    {

    }
}

在我的实现中,我将第二个参数称为“maxRecords”。然而,在界面中,它被定义为“计数”。编译器仍然考虑实现的接口,这很好,但可能会导致一些歧义。显然,我应该重命名其中一个参数以匹配另一个。

在重命名之前我玩了一会儿,发现了一些有趣的东西。我不允许将我的接口声明为:

public interface IActivityDao : IDao<Activity>
{
    IList<Activity> GetAllSinceSequence(long, int);
}

这仅仅是编译器对 C# 语义的过度保护吗?除了使代码更具可读性之外,接口方法中的参数名称还有什么用途?在我看来,如果参数名称不是强制执行的,它会引起歧义。

4

6 回答 6

46

为了实现的清晰和参考,接口声明中需要参数名称。如果有人在使用您的接口,则方法参数的名称应该是自记录的,因此接口的使用者了解要传递给方法的内容(例如,通过 IntelliSense 查看方法描述时)

是的,当您实现接口时,您可以随意命名参数。

于 2012-09-04T23:45:54.980 回答
10

历史。这可以追溯到 .NET 的早期阶段,也就是 COM 统治世界的时候。能够与 COM 互操作在当时非常重要,没有人会为了采用全新的编程风格而抛弃一切。

这使得 COM 互操作通常在 .NET 中得到强烈支持。除了需要为接口方法命名参数外,类型库也需要它们。

有趣的极端案例永远是 C++/CLI 语言。它采用了许多 C++ 语法规则,包括在声明中省略参数名称的能力。换句话说,这是合法的:

    public interface class IFoo
    {
        void bar(int, long, double);
    };

类型库导出器生成此声明:

    HRESULT bar(
                    [in] long p1, 
                    [in] long p2, 
                    [in] double p3);

如果您在 C# 类中实现接口,则结果非常相似,由 IntelliSense 自动生成:

class FooImpl : cpptemp36.IFoo {
    public void foo(int __p1, int __p2, double __p3) {
        throw new NotImplementedException();
    }
}

这让任何人都不高兴。

于 2012-09-05T00:19:57.907 回答
2

让我问你,.net 框架中是否还有其他任何地方允许你定义没有参数名称的方法签名?

归根结底,一切皆有可能,但大多数事情都是有原因的,在这种情况下,我想这是框架和编译器设计的限制,这真的很重要吗?

毕竟,您正在定义一份使用合同,人们会期望它们真的存在。

于 2012-09-04T23:47:55.477 回答
2

在代码质量的新世界中,实现级别的参数名称必须与接口级别相同。

在 sonarqube 中,有一条规则,例如“参数名称应与基本声明和其他部分定义匹配”,并将其称为“不合规代码”

interface IFoo
{
  void Bar(int i);
}

class Foo : IFoo
{
  void Bar(int z) // Noncompliant, parameter name should be i
  {
  }
}

有趣的是它指的是这个不包括的文件interfacehttps ://wiki.sei.cmu.edu/confluence/display/c/DCL40-C.+Do+not+create+incompatible+declarations+of+the +相同+功能+或+对象

我个人喜欢这样的代码:

public interface IExtractor<TInput, TOutput>{
    TOutput Extract(TInput input);
}

public class CustomerExtractor : IExtractor<Order, Customer>
{
    public Customer Extract(Order order)
    {
        // ...
    }
}

但是该工具迫使我使用以下声明它是一个关键问题:

    public Customer Extract(Order input)
    {
        // ...
    }

input与本例中的含义不同order

为了摆脱噪音,

  • 如果不涉及 DI,则对此类类使用静态实用程序类(Map、Extract、Combine,...)
  • 如果涉及 DI,请通过添加辅助变量来欺骗工具
    public Customer Extract(Order input)
    {
       var order = input;

       // use order from here on
    }
于 2020-05-18T17:33:39.617 回答
1

我想这是由于 C# 中的命名参数功能。即,您需要能够按名称指定参数,而不仅仅是按默认顺序:

IActivityDao dao;
dao.GetAllSinceSequence(count: 1, sequence: 2);

当然,如果将对象转换为您的实例,则参数名称会有所不同。

var concreteDao = (ActivityDao) dao;
concreteDao.GetAllSinceSequence(maxRecords: 1, sequence: 2);
于 2012-09-04T23:45:23.657 回答
1

许多语言(如 C# 和 VB)支持方法的命名参数和选项参数。如果没有接口中的参数名称,就不可能使用命名参数和可选参数。命名参数也有助于读者理解接口的意图和功能。

于 2012-09-04T23:48:52.097 回答