正在使用“淘汰”的不良做法

时间:2010-11-23 10:42:33

标签: c# lambda moq anti-patterns

我刚刚为我编写的方法添加了 out bool参数,以便在我的UI中收到警告。我已经使用了out而不是让方法本身返回false / true,因为这意味着 DoSomething 失败/成功。我的想法是 warnUser 会指示警告实际上是什么,而不必查看方法的实现。

原始代码

public void DoSomething(int id, string input);

新代码

public void DoSomething(int id, string input, out bool warnUser);

我正在使用Moq来测试此代码,但它不支持out / ref参数,因为Lambda表达式不支持它们

测试代码

mockService.Verify(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<bool>());

所以,使用 out 参数是不好的做法,如果是,我该怎么做呢?

7 个答案:

答案 0 :(得分:9)

在void方法中使用out参数通常是个坏主意。你说你已经使用它“而不是让方法本身返回false / true,因为这意味着DoSomething失败/成功” - 我不相信这意味着什么。通常在.NET中,故障通过异常而不是真/假来表示。

out参数通常比返回值更难使用 - 特别是,你必须有一个正确类型的变量来处理,所以你不能只写:

if (DoSomething(...))
{
   // Warn user here
}

您可能需要考虑的一个替代方案是指示所需警告级别的枚举。例如:

public enum WarningLevel
{
    NoWarningRequired,
    WarnUser
}

然后该方法可以返回WarningLevel而不是bool。这会让你更清楚你的意思 - 尽管你可能想稍微重命名一下。 (很难用“DoSomething”这样的metasyntactic名称给出建议,虽然我完全理解你为什么在这里使用它。)

当然,另一种选择是您可能希望存在更多信息 - 例如警告的原因。这可以通过枚举完成,或者您可能想要完全提供更丰富的结果。

答案 1 :(得分:7)

out 非常一个有用的构造,特别是bool TryGetFoo(..., out value)这样的模式中,你想要知道“if”和单独的“什么”(和可空的不一定是一种选择)。

然而 - 在这种情况下,为什么不这样做:

public bool DoSomething(int id, string input);

并使用返回值发出信号?

答案 2 :(得分:3)

还有一些想法:

  • FxCop说:CA1021: Avoid out parameters

  • 对于私有/内部方法(实施细节)out / ref参数不是问题

  • C# 7 Tuples通常是out参数的更好替代

  • 此外,C# 7通过引入“输出变量”并允许“丢弃”out参数来改进呼叫网站上out参数的处理

答案 3 :(得分:1)

在不了解更多相关背景的情况下,这是一个非常棘手的问题。

如果DoSomething是UI层中的某个与UI相关的方法,那么也许没关系。如果DoSomething是业务层方法,那么这可能不是一个好方法,因为它意味着业务层需要了解适当的响应可能是什么,甚至可能必须知道本地化问题。

纯粹主观地说,我倾向于远离参数。我认为它们破坏了代码的流程并使其不那么清晰。

答案 4 :(得分:1)

鉴于一个很大的遗留代码方法(比如15行以上,200多行的遗留代码方法很常见),人们可能想要使用&#34;提取方法&#34;重构以清理并使代码更简洁,更易于维护。

这样的重构很可能产生一个或多个新方法,其中包含用于设置先前方法局部变量值的参数。

就个人而言,我使用这种情况作为一个强有力的指标,表明在先前的方法中隐藏了一个或多个离散类,因此我可以使用&#34;提取类&#34;,在新类中的那些参数获取调用(先前)方法可见的属性。

我认为在先前的方法中保留带有out参数的提取方法是不好的做法,跳过&#34; Extract Class&#34;的连贯的后续步骤,因为如果你跳过它,那些局部变量保留在你的先验功能,但在语义上不再属于它。

所以,在这种情况下,我认为out参数是破坏SRP的代码味道。

答案 5 :(得分:0)

我认为最好的方法是使用预期的例外。您可以创建一组定义警告的自定义异常,并在调用方法中捕获它们。

public class MyWarningException : Exception() {}

...

try
{ 
     obj.DoSomething(id,input);
}
catch (MyWarningException ex)
{
     // do stuff
}

想法是在DoSomething实现结束时引发异常,以便流程不会在中间停止

答案 6 :(得分:0)

我认为针对您的案例的最佳解决方案是使用委托代替bool。使用这样的东西:

public void DoSomething(int id, string input, Action warnUser);

您可以传递空的lamda,而不想向用户显示警告。

相关问题