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

时间:2008-10-29 20:06:34

标签: c# java methods overloading

在定义多个使用不同过滤器返回相同形状数据的方法时,有什么更好的做法?显式方法名称或重载方法?

例如。如果我有一些产品而且我从数据库中提取

明确的方式:

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 }

我意识到你可能会遇到类似签名的问题,但是如果你传递的是对象而不是基类型(string,int,char,DateTime等),那么这将是一个问题。问题。所以...... 重载一个方法来减少你拥有的方法数量是个好主意,为了清晰起见,应该每个方法以不同的方式过滤数据具有不同的方法名称

16 个答案:

答案 0 :(得分:44)

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

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

像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) {...}

代码读取的次数比写入的多得多 - 即使您在初始检查源代码控制后再也没有回到代码中,您仍然会在几十次读取该行代码时你编写了下面的代码。

最后,除非您正在编写一次性代码,否则您需要允许其他人使用其他语言调用您的代码。似乎大多数业务系统最终都会在日期之前保持生产状态。可能是2016年消耗你的类的代码最终是用VB.NET,C#6.0,F#或者尚未发明的全新内容编写的。可能是语言不支持重载。

答案 1 :(得分:16)

据我所知,你不会有更少的方法,只有更少的名字。我通常更喜欢重载的命名方法系统,但我认为只要你很好地评论和记录你的代码(在任何一种情况下都应该这样做),它并没有太大的区别。

答案 2 :(得分:15)

你可以过度使用吗?嗯,是的,那是真的。

但是,您提供的示例是何时使用方法重载的完美示例。它们都执行相同的功能,为什么只是因为你将不同的类型传递给它们而给它们不同的名称。

主要规则是做最清楚,最容易理解的事情。不要使用重载只是为了光滑或聪明,在有意义的时候去做。其他开发人员也可能正在处理此代码。您希望让它们尽可能简单地获取并理解代码,并能够在不实现错误的情况下实现更改。

答案 3 :(得分:11)

我喜欢重载我的方法,以便以后在intellisense中我没有相同的方法。对我来说,让它过度负载似乎更合乎逻辑,而不是让它以不同的方式命名十几次。

答案 4 :(得分:5)

您可以考虑的一件事是您不能将重载的方法公开为WCF Web服务中的操作契约。因此,如果您认为您可能需要这样做,那么使用不同的方法名称将是一个争论。

不同方法名称的另一个论点是使用intellisense可以更容易地发现它们。

但两种选择都有利弊 - 所有的设计都需要权衡。

答案 5 :(得分:4)

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

答案 6 :(得分:4)

重载的目的是为了简化某人使用您的代码的学习曲线......并允许您使用命名方案来告知用户该方法的用途。

如果您有十种不同的方法都返回一组员工,那么生成十个不同的名称(特别是如果它们以不同的字母开头!)会导致它们在用户的intellisense下拉列表中显示为多个条目,从而扩展下拉的长度,以及隐藏所有返回员工集合的十个方法的集合之间的区别,以及您班级中的其他任何方法......

考虑一下.Net框架已经强制执行的内容,比如构造函数和索引器......它们都被强制使用相同的名称,并且只能通过重载它们来创建多个...

如果你超载它们,它们将全部显示为一个,它们的不同签名和注释都在一边。

如果两个方法执行不同或不相关的函数,则不应重载这两个方法......

当您希望按类型重载具有相同签名的两个方法时可能存在的混淆 如在

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

您可以创建单独的类型作为违规核心类型的包装,以区分签名。

  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);

答案 7 :(得分:2)

另一种选择是使用Query对象来构建“WHERE子句”。所以你只有一个像这样的方法:

public List<Product> GetProducts(Query query)

Query对象包含以面向对象的方式表达的condidition。 GetProducts通过“解析”Query对象来获取查询。

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

答案 8 :(得分:1)

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

如果您使用.Net 3.5+并且需要应用多个过滤器,那么最好使用IQueryable和链接,即

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

通过这种方式,您可以在需要时重复使用过滤逻辑。

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

答案 9 :(得分:1)

当你在方法的参数中只有细微的差别时,我看到过度使用过度使用了。例如:

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。

答案 10 :(得分:1)

对框架的简要介绍应该说服你,许多重载是一种公认​​的事态。面对无数的重载,Microsoft框架设计指南的第5.1.1节(Kwalina和Abrams,2006)直接解决了可用性重载的设计。以下是该部分的简要介绍:

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

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

  • AVOID 在重载成员的参数排序方面存在不一致。

  • DO 仅使最长的过载虚拟(如果需要可扩展性)。更短的过载应该简单地调用更长的过载。

  • 请勿使用refout参数来重载成员。

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

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

答案 11 :(得分:0)

您可以根据需要使用重载。 从最佳实践的角度来看,如果您尝试对数据执行相同的“操作”(整体),建议您使用重载。例如。 getProduct()

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

答案 12 :(得分:0)

重载是理想的多态行为。它有助于人类程序员记住方法名称。如果explicit对于type参数是多余的,那么它就是坏的。如果type参数并不意味着该方法正在做什么,那么显式开始有意义。

在您的示例中,getProductByName是唯一可能有意义的情况,因为您可能希望通过其他字符串获取产品。这个问题是由原始类型的模糊性造成的;在某些情况下,getProduct(Name n)可能是更好的重载解决方案。

答案 13 :(得分:0)

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

如果不是这种情况并且所有三个都返回几个(如果你传递了它产品ID 5,它返回productid中任何带有5的项目,或者返回名称中带有字符串参数的任何项目)然后我会调用所有三个GetProducts或GetProductList并覆盖所有这些。

无论如何,名称应该反映函数的作用,因此在返回产品列表时调用GetProduct(singular)并不能成为一个好的函数名。 IMNSHO

答案 14 :(得分:0)

我完全忠于“显式”方式:为每个功能赋予不同的名称。我甚至重构了一些过去有Add(...)个函数的代码,AddRecord(const Record&)AddCell(const Cell&)等等。

我认为这有助于避免一些混乱,无意中的演员表(至少在C ++中)和编译器警告,并且它提高了清晰度。

在某些情况下,您可能需要其他策略。我还没遇到过。

答案 15 :(得分:0)

怎么样

public IList<Product> GetProducts() { /* Return all. */}

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

等等?