仅通过参数名称(而不是类型)来区分方法是否足够?


36

仅通过参数名称(而不是类型)来区分方法就足够了吗?还是更明确地命名呢?

例如T Find<T>(int id)VS T FindById<T>(int id)

是否有充分的理由更明确地命名(即添加ById)而不是仅保留参数名称?

我能想到的一个原因是方法的签名相同但含义不同。

FindByFirstName(string name)FindByLastName(string name)


4
因此,当您超载“查找包括”时,T Find<T>(string name)或者(int size)您打算如何解决不可避免的问题?
UKMonkey

3
@UKMonkey有什么不可避免的问题?
康拉德

3
在第一种情况下:如果多个条目具有相同的名称,则必须更改函数签名;这意味着人们可能会对返回的含义感到困惑;在后一种情况下,参数是相同的-因此是非法重载。您可以使用“ byX”开始命名函数,也可以为该参数创建对象,以便您可以使用相同的参数获得等效的重载。无论哪种情况,它都能很好地工作。
UKMonkey

2
@UKMonkey如果需要,可以发布一些代码示例的答案
Konrad,

3
ID可能应该是不透明的ID对象,而不仅仅是一个int。这样,可以在编译时检查您是否在代码的某些部分中未对int使用id,反之亦然。这样,您就可以拥有find(int value)find(ID id)
Bakuriu

Answers:


68

当然,有充分的理由更明确地命名它。

应该不言而喻的主要不是方法定义,而是方法的使用。并且findById(string id)find(string id)都是不言而喻的,findById("BOB")并且和之间存在巨大差异find("BOB")。在前一种情况下,您知道随机文字实际上是一个ID。在后一种情况下,您不确定-可能实际上是给定名称或完全是其他名称。


9
除非有99%的其他情况使用变量或属性名称,否则要参考:findById(id)vs find(id)。您可以选择任何一种方式。
格雷格·伯哈特

16
@GregBurghardt:对于方法及其调用者,特定值不一定用相同的方式命名。例如,考虑double Divide(int numerator, int denominator)在方法中使用:double murdersPerCapita = Divide(murderCount, citizenCount)。您不能依赖使用相同变量名的两种方法,因为在很多情况下,情况并非如此(或者是这种情况,这是不好的命名)
Flater 18'Nov

1
@Flater:给定问题中的代码(从某种持久性存储中查找内容),我想您可能会使用“ murderedCitizenId”或“ citizenId”调用此方法……我真的不认为参数或变量名是这里模棱两可。老实说,我可以选择任何一种方式。这实际上是一个很自以为是的问题。
格雷格·伯格哈特

4
@GregBurghardt:您不能从单个示例中提取全局规则。OP的问题一般而言,并非特定于给出的示例。是的,在某些情况下使用相同的名称是有意义的,但在某些情况下则没有意义。因此,这个答案最好是即使在用例子集中不必要也要保持一致性
扁平的

1
名称参数分辨混淆后的混乱已经存在,因为你需要看的方法定义,同时明确命名方法完全避免混乱具有存在的方法调用的名称。同样,您不能拥有名称和参数类型相同但参数名称不同的两个方法,这意味着在某些情况下无论如何都将需要显式名称。
Darkhogg

36

FindById()的优点

  1. 面向未来:如果你下手Find(int),后来有增加其他的方法(FindByName(string)FindByLegacyId(int)FindByCustomerId(int)FindByOrderId(int),等),我这样的人往往要花费年龄寻找 FindById(int)如果您可以并且一旦有必要就将其更改Find(int)为不是真正的问题FindById(int)- 如果 s,则将来的证明就是这些。

  2. 更容易阅读Find是完全正常的,如果呼叫貌似record = Find(customerId);FindById是读书,如果是稍微容易record = FindById(AFunction());

  3. 一致性。您可以在任何地方始终使用FindByX(int)/ FindByY(int)模式,但是Find(int X)/ Find(int Y)不可能,因为它们会发生冲突。

Find()的优点

  • 吻。Find非常简单明了,并且operator[]它是此上下文中2个最值得期待的函数名称之一。(根据上下文get,一些流行的替代方法是,lookupfetch)。
  • 根据经验,如果函数名称是一个众所周知的单词,可以准确地描述该函数的功能,请使用它。即使存在更长的多词名称,它在描述函数功能方面也会稍好一些。示例:长度 vs NumberOfElements。需要权衡取舍,如何划定界线仍在进行辩论。
  • 通常最好避免冗余。如果看一下FindById(int id),我们可以很容易地通过将其更改为来消除冗余Find(int id),但是这是一个折衷方案-我们失去了一些清晰度。

另外通过使用强类型Id,您可以同时获得两者的优点

CustomerRecord Find(Id<Customer> id) 
// Or, depending on local coding standards
CustomerRecord Find(CustomerId id) 

实现Id<>在C#中强键入ID值

此处以及上面的链接中的评论引起Id<Customer>了我要解决的多个问题:

  • 关注点1:这是泛型的滥用。 CustomerIdOrderID是不同的类型(customerId1 = customerId2;=>好,customerId1 = orderId1;=>坏),但是它们的实现几乎相同,因此我们可以使用复制粘贴或元编程来实现它们。虽然在讨论中有关于暴露或隐藏泛型的价值,但是元编程是泛型的用途。
  • 关注点2:这不会停止简单的错误/这是解决问题解决方案通过使用强类型Id消除的主要问题是对的调用中的错误参数顺序DoSomething(int customerId, int orderId, int productId)。强类型的ID还可以防止其他问题,包括被问到的一个OP。
  • 问题3:它实际上只是使代码模糊。很难确定ID是否保留在中int aVariable。容易知道ID保留在中Id<Customer> aVariable,我们甚至可以说这是客户ID。
  • 问题4:这些ID不是强类型,只是包装器。 String只是一个包装byte[]。包装或封装与强类型没有冲突。
  • 关注点5:设计过度。下面是最小的版本,虽然我不建议加operator==operator!=为好,如果你不想完全依赖于Equals

public struct Id<T>: {
    private readonly int _value ;
    public Id(int value) { _value = value; }
    public static explicit operator int(Id<T> id) { return id._value; }
}

10

考虑这种情况的另一种方法是使用语言的类型安全性。

您可以实现以下方法:

Find(FirstName name);

其中,FirstName是一个简单的对象,该对象包装了一个包含名字的字符串,这意味着该方法的作用以及调用该方法的参数都不会引起混淆。


4
不确定您对OP问题的答案是什么。您是否建议通过依赖参数的类型来使用“查找”之类的名称?还是建议仅在参数具有显式类型时才使用此类名称,并在其他位置使用诸如“ FindById”之类的更显式名称?还是建议引入显式类型以使“查找”之类的名称更可行?
布朗

2
@DocBrown我认为是后者,我喜欢它。它实际上与Peter的答案iiuc类似。据我了解,其原理有两个方面:(1)从参数类型中可以清楚看出函数的作用;(2)您不能犯类似。之类的错误,string name = "Smith"; findById(name);如果使用非描述性通用类型则可能出现这种错误。
彼得-恢复莫妮卡

5
通常,我是编译时类型安全的迷,但是请谨慎使用包装类型。如果出于类型安全的考虑而引入包装器类,则有时过多会使API复杂化。例如,winapi遇到的整个“艺术家以前称为int”问题;从长远来看,我会说大多数人只是看着无尽的DWORD LPCSTR克隆,并认为“这是一个int / string / etc”,这到了点,您花了更多的时间来准备工具,而不是实际设计代码。 。
jrh

1
@jrh是的。我引入“标称”类型(仅在名称上有所区别)的试金石是在我们的用例中通用函数/方法没有任何意义的情况下,例如,ints经常被求和,相乘等,这对于ID毫无意义。所以我要ID与区别int。通过缩小给定值可以做的事情,这可以简化API(例如,如果有ID,则只能与一起使用find,而不能与eg age或一起使用highscore)。相反,如果我们发现自己进行了很多转换,或者find为多种类型编写了相同的函数/方法(例如),则表明我们的区别太好了
Warbo

1

我将投票支持像FindByID这样的显式声明。...应该为Change构建软件。它应该是打开和关闭(SOLID)。因此,该类是开放的,以添加类似的find方法,比如说FindByName ..等。

但是FindByID已关闭,其实现已进行了单元测试。

我不会建议使用谓词的方法,这些方法在通用级别上很不错。如果基于字段(ByID),您有完全不同的方法该怎么办。


0

我很惊讶没有人建议使用以下谓词:

User Find(Predicate<User> predicate)

使用这种方法,不仅可以减少API的表面,而且可以使使用它的用户获得更多控制权。

如果这还不够,您可以随时根据需要进行扩展。


1
问题是由于它无法利用索引等优势,因此效率较低。
所罗门·乌科
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.