方法重载。 你能过度使用吗?

定义几种不同的filter返回相同形状的数据的方法时,更好的做法是什么? 显式方法名称或重载方法?

例如。 如果我有一些产品,我从数据库拉

明确的方式:

public List<Product> GetProduct(int productId) { // return a List } public List<Product> GetProductByCategory(Category category) { // return a List } public List<Product> GetProductByName(string Name ) { // return a List } 

重载方式:

 public List<Product> GetProducts() { // return a List of all products } public List<Product> GetProducts(Category category) { // return a List by Category } public List<Product> GetProducts(string searchString ) { // return a List by search string } 

我意识到你可能会遇到类似签名的问题,但如果你传递的是对象而不是基types(string,int,char,DateTime等),那么这个问题就不会那么严重了。 所以… 重载一个方法来减less你拥有的方法的数量和清晰度是一个不错的主意, 或者 每个以不同方式过滤数据的方法都有不同的方法名称

是的,超载很容易被滥用。

我发现,确定过载是否合理的关键是考虑受众 – 而不是编译器,而是维护程序员,他们将在几周/几个月/年内出现,并且需要了解代码是什么试图实现。

一个简单的方法名称,如GetProducts()是清晰可理解的,但它确实留下了很多未说明的。

在许多情况下,如果传递给GetProducts()的参数名称良好,维护人员将能够确定重载是什么 – 但这依赖于在使用点的良好命名规则,而这是无法执行的。 你可以执行的是他们调用的方法的名字。

我遵循的指导方针是只有在方法可以互换的情况下才会重载 – 如果它们做同样的事情。 这样,我不介意我的类的消费者调用哪个版本,因为它们是等价的。

为了说明,我很乐意为一个DeleteFile()方法使用重载:

 void DeleteFile(string filePath); void DeleteFile(FileInfo file); void DeleteFile(DirectoryInfo directory, string fileName); 

但是,对于您的示例,我会使用单独的名称:

 public IList<Product> GetProductById(int productId) {...} public IList<Product> GetProductByCategory(Category category) {...} public IList<Product> GetProductByName(string Name ) {...} 

拥有全名使代码更清晰的维护人员(谁可能是我)。 它避免了签名冲突的问题:

 // No collisions, even though both methods take int parameters public IList<Employee> GetEmployeesBySupervisor(int supervisorId); public IList<Employee> GetEmployeesByDepartment(int departmentId); 

也有机会为每个目的引入重载:

 // Examples for GetEmployees public IList<Employee> GetEmployeesBySupervisor(int supervisorId); public IList<Employee> GetEmployeesBySupervisor(Supervisor supervisor); public IList<Employee> GetEmployeesBySupervisor(Person supervisor); public IList<Employee> GetEmployeesByDepartment(int departmentId); public IList<Employee> GetEmployeesByDepartment(Department department); // Examples for GetProduct public IList<Product> GetProductById(int productId) {...} public IList<Product> GetProductById(params int[] productId) {...} public IList<Product> GetProductByCategory(Category category) {...} public IList<Product> GetProductByCategory(IEnumerable<Category> category) {...} public IList<Product> GetProductByCategory(params Category[] category) {...} 

代码的读取比写入的要多得多 – 即使你在初始签入源代码控制之后再也不会回到代码中,那么当你编写代码的时候,你仍然会读这行代码几十次后面的代码。

最后,除非你正在编写一次性代码,否则你需要允许其他人从其他语言调用你的代码。 看来大多数商业系统最终都会停留在生产之中,而不是按date使用。 可能是2016年消耗类的代码最终会写成VB.NET,C#6.0,F#或者还没有发明的全新的东西。 这可能是该语言不支持重载。

据我所知,你不会有更less的方法,只有更less的名字。 我通常更喜欢重载的命名方法系统,但是我认为只要您对代码进行评论和编写文档(在任何情况下都应该这样做),就不会有太大区别。

你能过度使用吗? 嗯,是的,这是真的。

但是,您提供的示例是何时使用方法重载的完美示例。 他们都执行相同的function,为什么给他们不同的名字,只是因为你传递不同的types给他们。

主要的规则是做最清楚,最容易理解的事情。 不要使用超载只是为了光滑或聪明,只要有意义就这样做。 其他开发人员也可能正在处理这些代码。 你想让他们尽可能简单地理解代码,并且能够在不实施错误的情况下实现更改。

我喜欢重载我的方法,以便以后在智能感知中,我没有百万个相同的方法。 对我来说,看起来更符合逻辑,而不是让它重复命名十次。

您可能会考虑的一件事是,您不能将重载的方法作为WCF Web服务中的操作契约来公开。 所以,如果你认为你可能需要这样做,这将是使用不同的方法名称的一个参数。

对于不同的方法名称的另一个说法是,使用智能感知可能更容易发现它们。

但是这两种select都有优点和缺点 – 所有的devise都是权衡的。

您可能需要一些项目范围的标准。 就我个人而言,我发现重载的方法更容易阅读。 如果你有IDE的支持,去吧。

重载的目的是为了减轻使用你的代码的人的学习曲线…并允许你使用命名scheme来告诉用户这个方法是干什么的。

如果您有十种不同的方法可以返回一组员工,那么会生成十个不同的姓名(特别是如果他们以不同的字母开头!)会导致他们在用户的智能感知下拉列表中显示为多个条目,从而延长下拉,并隐藏所有返回一个员工集合的十个方法的集合之间的区别,以及您的class级中的其他方法…

考虑一下.Net框架为构造函数和索引器强制执行的内容……它们都被迫使用相同的名称,并且只能通过重载它们来创build倍数…

如果你超载他们,他们将全部显示为一个,他们不同的签名和评论。

如果它们执行不同或不相关的function,则不应该重载两种方法…

至于当你想用type中的相同签名重载两个方法时可能存在的混淆

 public List<Employee> GetEmployees(int supervisorId); public List<Employee> GetEmployees(int departmentId); // Not Allowed !! 

那么你可以创build单独的types作为违规核心types的包装来区分签名。

  public struct EmployeeId { private int empId; public int EmployeeId { get { return empId; } set { empId = value; } } public EmployeeId(int employeId) { empId = employeeId; } } public struct DepartmentId { // analogous content } // Now it's fine, as the parameters are defined as distinct types... public List<Employee> GetEmployees(EmployeeId supervisorId); public List<Employee> GetEmployees(DepartmentId departmentId); 

当你在方法的参数上只有细微的差异时,我已经看到过载被滥用。 例如:

 public List<Product> GetProduct(int productId) { // return a List } public List<Product> GetProduct(int productId, int ownerId ) { // return a List } public List<Product> GetProduct(int productId, int vendorId, boolean printInvoice) { // return a List } 

在我的小例子中,第二个int参数应该是所有者还是客户ID很快就变得不清楚了。

简要地看一下这个框架应该让你相信,无数重载是一种被接受的事态。 面对无数的重载,可用性的重载devise直接由Microsoft框架devise指南(Kwalina和Abrams,2006)的第5.1.1节来解决。 这部分内容简要介绍如下:

  • 尝试使用描述性参数名称来指示较短重载所使用的默认值。

  • 避免在过载中任意改变参数名称。

  • 避免重载成员中的参数sorting不一致。

  • 只做最长的过载虚拟(如果需要可扩展性)。 较短的重载应该简单地通过一个更长的过载。

  • 不要使用refout参数来超载成员。

  • DO允许null传递给可选参数。

  • 使用成员重载而不是使用默认参数定义成员。

是的,你可以过度使用它,但是这里有另一个概念,可以帮助控制它的使用…

如果你使用的是.Net 3.5+,并且需要应用多个filter,那么最好使用IQueryable和链接即

 GetQuery<Type>().ApplyCategoryFilter(category).ApplyProductNameFilter(productName); 

这样,无论您需要,您都可以重复使用过滤逻辑。

 public static IQueryable<T> ApplyXYZFilter(this IQueryable<T> query, string filter) { return query.Where(XYZ => XYZ == filter); } 

另一种select是使用Query对象来构build“WHERE子句”。 所以你只会有这样一个方法:

 public List<Product> GetProducts(Query query) 

Query对象包含以面向对象的方式表示的条件。 GetProducts通过“parsing”Query对象来获取查询。

http://martinfowler.com/eaaCatalog/queryObject.html

你可以尽可能多地使用Overloading。 从最佳实践的angular度来看,如果您试图对数据执行相同的“操作”(整体),则build议您使用重载。 例如getProduct()

另外,如果您看到Java API,则重载无处不在。 你不会find比这更大的认可。

重载是可取的多态行为。 它帮助人类程序员记住方法名称。 如果显式与types参数是多余的,那么它是不好的。 如果types参数并不意味着这个方法正在做什么,那么显式开始有意义。

在你的例子中,getProductByName是显式可能有意义的唯一情况,因为你可能想通过其他string获取产品。 这个问题是由原始types的模糊性引起的; getProduct(Name n)在某些情况下可能是更好的重载解决scheme。

是的,你可以过度使用它。 在你的例子中,似乎第一个和第三个可能会返回一个单一的项目,其中第二个将返回几个。 如果这是正确的,那么我会调用第一个和第三个GetProduct和第二个GetProducts或GetProductList

如果不是这种情况,并且所有三个都返回几个(例如,如果您传递了productID 5,它会返回productid中的任何5个项目,或者返回其名称中包含string参数的任何项目),那么我将调用所有三个GetProducts或GetProductList并覆盖所有这些。

无论如何,这个名字应该反映这个函数做了什么,所以当它返回一个Products列表的时候调用GetProduct(单数)并不是一个好的函数名字。 IMNSHO

我是“明确的”方式的总粉丝:给每个function一个不同的名字。 我甚至重构了一些过去有很多Add(...)函数的代码, AddRecord(const Record&)AddCell(const Cell&)等等。

我认为这有助于避免一些混淆,无意中(至less在C ++中)强制转换和编译器警告,并且提高了清晰度。

也许在某些情况下,你需要另一个策略。 我还没有遇到过。

怎么样

 public IList<Product> GetProducts() { /* Return all. */} public IList<Product> GetProductBy(int productId) {...} public IList<Product> GetProductBy(Category category) {...} public IList<Product> GetProductBy(string Name ) {...} 

等等?