重载方法还是不重载方法?

4
这里有两种方法(killZombie)处理只有一个参数(string)或多个参数(string[])的情况。因为它们做的事情是相同的,我又写了一个名为“killAZombie”的方法,被其他两个方法使用。问题在于,“killAZombie”方法的命名...有点奇怪。其他人会遇到这个问题吗?最好的解决方法是什么,如何将我的“KillAZombie”方法重命名为其他更清晰区分“killZombie”的名称?
public void killZombie(string zombieLocation){
    killAZombie(zombieLocation);
}

public void killZombie(string[] zombieLocations){
    foreach(string zombieLocation in zombieLocations){
        killAZombie(zombieLocation);
    }
}

public void killAZombie(string zombieLocation){
    //Kills a zombie at specified location
}

我认为解决这个问题的另一种方式是,不要重载"killZombie"方法,而是创建两个不同的方法,如下:

public void killZombie(string zombieLocation){
    //Kills a zombie at specified location
}

public void killZombies(string[] zombieLocations){
    foreach(string zombieLocation in zombieLocations){
        killZombie(zombieLocation);
    }
}

这样我们只有两种更易于理解的方法,但这样方法就不会被重载。在我看来,拥有重载方法似乎是一件好事(这意味着方法更少,更简洁),所以我对这个解决方案也不确定。我很想听听如何最好地解决这个问题,谢谢!
补充:
我的方法实际上需要4个参数,因此参数将位于末尾。params变量是最重要的变量,因此将其作为最后一个参数放置以使params起作用似乎有点笨拙。我的担忧是否足以将方法分成KillZombie和KillZombies,还是params仍然是正确的处理方式?

2
+1 用于函数和变量名称 :) - Serkan Hekimoglu
1
Muaaaah,脑子zzzzzzzzzzzzz 流口水 - Christian
6个回答

6

以下是一些想法。

首先,对于公共方法,C#的约定是将它们大写:“KillZombie”,而不是“killZombie”。

如果您愿意,您可以只使用一个方法来完成此操作。下面是一个接受一个或多个位置的方法。调用者只需提供一个列表:KillZombies(location1, location2, location3);

private void KillOneZombie(string location) { ... }
public void KillZombies(string location, params string[] additionalLocations)
{
    KillOneZombie(location);
    if (additionalLocations == null) return;
    foreach(string additionalLocation in additionalLocations)
        KillOneZombie(additionalLocation);
}

如果您确实想要有两种方法,请考虑使用一个接收 IEnumerable<string> 而不是数组的方法;这样,调用者就可以传递列表、查询、数组等任何内容。
第二个命名模式更为标准:KillZombie 和 KillZombies。 params 变量是最重要的变量,因此将其作为最后一个参数放置以使 params 生效似乎有些笨拙。我的担忧是否足够合理,以至于需要将方法分成 KillZombie 和 KillZombies,或者 params 仍然是正确的做法?
我会考虑您希望如何使用该方法。例如,请考虑以下情况:
Console.WriteLine("User: {0} Score: {1}", user[i].Name, scores[i]);

在这里,我们明确希望"params"将被用于支持调用者的可变数量的参数。但实际上很少有人这样做:

object[] results = new object[] { user[i].Name, scores[i] };
Console.WriteLine("User: {0} Score: {1}", results);

即使这是完全合法的,但如果您期望您的方法像Console.WriteLine一样被使用,其中会传递不同数量的参数,但参数数量在编译时已知,则请使用params。

如果您期望它将与第二种模式一起使用——某人有一个位置数组——则不要使用params;制作两个方法,KillZombie和KillZombies,并且其中一个方法需要接受一个字符串的IEnumerable。


我对使用params有一些顾虑,你能否评论一下我对原帖的补充说明?谢谢。 - sooprise
additionalLocations еЏЇиѓЅдёє null еђ—пјџ - Jeffrey L Whitledge
1
@Jeffrey:是的。始终可以使用可转换为数组的表达式调用带有 "params" 的方法,如果这样做,则在重载决策期间,“普通”调用将优先于“扩展数组”调用。有关详细信息,请参阅规范中的重载解析部分。 - Eric Lippert
@Eric:有一件事我一直在想,我认为你会知道,那就是为什么params使用数组而不是IEnumerable?这是因为性能的原因吗?因为如果使用IEnumerable,那么数组也会自动工作,对吧?我觉得这应该是你们设计这个功能时考虑到的问题。我之所以问这个问题,是因为这将允许在同一个方法中传递更多灵活的参数。 - Joan Venge
3
@Joan: 见解独到。是的,每个参与者都希望在C# 1.0时代就已经有了IEnumerable<T>。如果我们有了它,那么params将使用IEnumerable<T>而不是可变数组;99.99%的时间你只是读取它。我们多年来一直在未来特性列表中列出“可枚举的params”,但它并没有被优先考虑到实际实现中。我们曾考虑为LINQ进行此操作,因为这意味着您可以在未添加ToArray的情况下传递“params”的查询。但这并不重要到足以证明其日程风险的正当性。 - Eric Lippert
@Eric:谢谢回复。我没意识到params是来自C# 1.0的。这很有道理。我会尝试在connect.microsoft.com上找到它并投票支持。希望它能在未来的C#版本中实现 :O - Joan Venge

4
在这种情况下,你可能更喜欢选择后面那个选项(因为函数的命名暗示它是在操作单个“僵尸”)。
但是,你也可以了解一下params关键字,以便知道你的选择。例如,如果你的函数仅命名为Kill(并且在这种情况下有意义),你可以这样做:
public void Kill(params string[] zombieNames)
{
    foreach(string name in zombieNames)
    {

    }
}

你可以用很多方式来称呼它:

Kill("Zoey");
Kill("Francis", "Zoey");

string[] survivors = { "Zoey", "Francis", "Bill", "Louis" };

Kill(names);
< p > (当然,前提是你的生还者都变成了僵尸!)

此外,C#代码在风格上通常使用帕斯卡命名法来命名函数(KillAZombie而不是killAZombie)。

添加说明的编辑

是的,参数排序虽然在技术上没有影响,但在API设计中是一个重要考虑因素,所以如果你将采用“不太重要”的参数,那么你可能必须放弃params

话虽如此,我仍然坚持我的原始建议:由于函数的名称(KillZombieKill相比),为了使您的名称与参数一致,我建议保留两个版本。我还建议允许用户指定IEnumerable<string>而不是数组。这将允许开发人员使用任何实现IEnumerable<string>的东西传递名称,包括字符串数组。


你能对我在原帖中的补充发表一下评论吗?谢谢。 - sooprise
非常好,谢谢。我会这样做的。另外,我添加了IEnumerable<string>(我以前不知道这是什么,但现在我知道了,并将来会使用它。非常感谢!:D - sooprise

3
在这种情况下,我可能会选择你的第二个建议。 KillZombie 杀死一个僵尸; KillZombies 杀死多个僵尸。
另一个选项是使用一个带有params参数的方法:
KillZombies("foo");           // kill a single zombie
KillZombies("foo", "bar");    // kill multiple zombies

// ...

public void KillZombies(params string[] zombieLocations)
{
    foreach (string zombieLocation in zombieLocations)
    {
        // kills a zombie at specified location
    }
}

(注意,标准的C#命名约定是使用大写字母K的/。)

1
对我来说,params 似乎是最好的解决方案,你同意吗? - sooprise
你能否对我在原帖中的补充发表评论吗?谢谢。 - sooprise
@sooprise:我想这取决于其他参数是什么。你能举个例子吗?如果将zombieLocations作为最后一个params参数感觉不对,那么也许你需要回到两种方法的解决方案(并让KillZombies多次调用KillZombie)。 - LukeH

2

首先,不仅仅有这两种选择。

特别是,您可以在不使用额外方法的情况下使用第一种方法。

public void KillZombie(string zombieLocation){
    // Implement zombie killing logic here.
}

public void KillZombie(string[] zombieLocations){
    foreach(string zombieLocation in zombieLocations)
        KillZombie(zombieLocation);
}

但在这种情况下,我建议使用两种不同的方法。尽管它们执行类似的操作,但一个接受多个僵尸,而另一个只接受单个僵尸。方法名称应该反映这一点。

类似地,.NET List 类有类似的方法AddAddRange


我认为这就是我在第二个代码块中所拥有的?当你写下这条评论时,我进行了一次你可能没有看到的编辑? - sooprise
@sooprise 在你的第二个代码块中,你的方法有不同的名称。 - Konrad Rudolph
我明白了,所以如果你使用正确的参数,就可以从killZombies(string[])中调用killZombies(string)?我之前不太确定,但这似乎是个好主意。 - sooprise
@sooprise 你绝对可以。 - Konrad Rudolph

0

使用这个怎么样:

public void killZombies(string zombieLocation, params string[] zombieLocations){
    killZombie(zombieLocation);
    if(zombieLocations != null) {
        foreach(string zombieLocation in zombieLocations){
            killZombie(zombieLocation);
        }
    }
}

你可以传递一个或多个僵尸。

[编辑] 如评论所述,此更新禁止杀死无僵尸。


您也可以不传递僵尸参数,但这可能并不理想。 - Eric Lippert
@Eric Lippert:这可以通过检查zombieLocations.Length来解决。 - Steve B
@Steve B 他可能想表达的意思是 killZombies(emptyArray) 没有太多意义。 - Christian
你能否对我在原帖中的补充发表评论吗?谢谢。 - sooprise
修改为仅在僵尸进程上禁止杀死。 - Steve B

0

你的例子很遗憾是错误的 - 你也可以使用 params 数组来允许像 KillZombies(location1, location2, location3) 这样的调用。Params 数组允许不确定数量的参数。

话虽如此,这通常是为了更容易使用。如果你有三个重载因为它们都被使用了,那么拥有它们就没有问题,对吧?

看看不同的 String.Format 方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接