在 C# 中为方法或构造函数提供过多的重载是一种不好的形式吗?

Is it bad form to provide excessive overloads for a method or constructor in C#?

我正在使用 C# 为 Unity 编写代码。目前,我正在处理一些较小的 classes 和结构,以快速序列化随机生成的地图。在这样做的过程中,我处理了一些构造函数,它们也将其中一些较小的 classes 和结构作为参数。

过去,在设置我的方法和构造函数时,我通常会尝试考虑合理的选项。虽然这种做法受到了质疑,但没有人设法给我任何合理的理由,说明我为什么不应该这样做.


考虑以下 class 和结构:

public class Tile
{
    public GridCoordinates position;
    public TileCode prefabID;
}

public struct GridCoordinates
{
    public int x;
    public int y;
}

public struct TileCode
{
    public int categoryID;
    public int individuaID;
}

通常,在创建构造函数时,我会涵盖所有 structint 替代方案。 Tile 看起来像这样:

public class Tile
{
    public GridCoordinates position;
    public TileCode prefabID;

    public Tile(TileCode prefabID, GridCoordinates position)
    {
        this.prefabID = new TileCode(prefabID);
        this.position = new GridCoordinates(position);
    }

    public Tile(TileCode prefabID, int xPosition, int yPosition)
    {
        this.prefabID = new TileCode(prefabID);
        position = new GridCoordinates(xPosition, yPosition);
    }

    public Tile(int typeID, int individualID, GridCoordinates position)
    {
        prefabID = new TileCode(typeID, individualID);
        this.position = new GridCoordinates(position);
    }

    public Tile(int typeID, int individualID, int xPosition, int yPosition)
    {
        prefabID = new TileCode(typeID, individualID);
        position = new GridCoordinates(xPosition, yPosition);
    }

我倾向于这样做是为了提高效率。与第一个 constructor/method 一起编写额外的 constructors/methods 花费了我微不足道的额外时间,我发现这有时在我以后希望使用 constructor/method 时派上用场一种我最初没有预料到的方式。

之前提出的唯一问题是可能造成混淆。我觉得这不是真正的问题,因为我的组织和评论清楚地区分了每个变体。


最终,我担心可能存在 其他 我的老师和同学没有意识到的问题。我目前正在考虑扩展到一个更大的项目,现在改变我的行为比以后改正要容易得多。

如果我为我的 classes 提供过多的替代构造函数或方法,我会面临什么问题?

我认为定义尽可能多的重载没有错,只要它们被清楚地记录下来。我不知道有任何相反的建议的良好做法或原则。一个明显的缺点显然是代码维护成本的增加;每当您需要更改某些内容时记录和测试。

这引起了我对您发布的代码的主要抱怨;我确实在您的代码中发现令人担忧的地方,而且如果我的代码有这么多独立的实现,我永远不会写类似的东西。如果您稍后决定更改构造函数逻辑,则必须在整个地方进行更改,这很容易出错。我将重构代码以使用构造函数链接并在一个单独的重载中实现所有构造逻辑,并且简单地 chain/delegate 所有其他构造函数到那个单独的构造函数。

综上所述,我个人的意见是您应该尽可能支持重载,其中关于您的业务领域的参数尽可能 "strongly typed" 。我的意思是:

public Foo(int i, int j, int k, int n) { ... }

非常容易出错。对 Foo(j, i, k, n) 的调用是完全有效的,一旦错误出现,就很难在大代码库中找到它,很可能在调用堆栈中。

然而签名的类型:

public Foo(Bar bar, Blah blah)

安全很多,因为类型系统和编译器可以帮助你。

您的方法的问题在于它违反了 Don't Repeat Yourself principle (also known as "DRY"). Adding "convenience constructors" that pass parameters along to dependencies increase code coupling between the two modules (specifically, control coupling),通常情况下,应该避免这种做法。

但是,在一种情况下您应该更喜欢方便的构造函数:当 GridCoordinatesTitleCode 被认为是 Title 的实现细节时会发生这种情况,因此不应公开。在这种情况下,您应该只公开最后一个构造函数,并从 class 的 public 接口中删除所有依赖 GridCoordinatesTitleCode 的构造函数。