C#方法链可以“太长”吗?

时间:2010-05-17 13:00:22

标签: c# method-chaining

当然,不是在可读性方面,因为您始终可以将单独的方法安排到单独的行中。相反,无论出于何种原因,将过多的方法链接在一起是危险的吗?我主要使用方法链来节省宣布单个一用变量的空间,传统上使用返回方法而不是修改调用者的方法。除了字符串方法,那些我有点无情地链。无论如何,我有时担心在一行中使用异常长的方法链的影响。

假设我需要根据某人的用户名更新一个项目的值。不幸的是,检索正确用户的最短方法如下所示。

SPWeb web = GetWorkflowWeb();
SPList list2 = web.Lists["Wars"];
SPListItem item2 = list2.GetItemById(3);
SPListItem item3 = item2.GetItemFromLookup("Armies", "Allied Army");
SPUser user2 = item2.GetSPUser("Commander");
SPUser user3 = user2.GetAssociate("Spouse");
string username2 = user3.Name;
item1["Contact"] = username2;

所有2或3的东西只能持续一次通话,所以我可能会将其压缩为以下(这也让我摆脱了一个多余的1):

SPWeb web = GetWorkflowWeb();
item["Contact"] = web.Lists["Armies"]
                     .GetItemById(3)
                     .GetItemFromLookup("Armies", "Allied Army")
                     .GetSPUser("Commander")
                     .GetAssociate("Spouse")
                     .Name;

不可否认,当它在一行中以及int.Parse(ddlArmy.SelectedValue.CutBefore(";#", false))而不是3时,它看起来要长得多。然而,这是这些链的平均长度之一,我可以很容易地预见到一些特别长的计数。排除可读性,对于这10个以上的方法链,我应该担心什么吗?或者使用非常长的方法链是否有害?

5 个答案:

答案 0 :(得分:33)

方法链的持续时间没有技术限制。

但是,可能会出现问题的三个方面是调试异常处理资源处理

调试很复杂,因为链接如此优雅 - 缺少中间临时变量。不幸的是,没有临时变量,在调试变得痛苦时检查中间结果。

由于您无法隔离从一种方法到另一种方法引发的异常,因此异常处理变得复杂。通常情况下,如果您无法对异常做出有意义的事情,这不是问题 - 只需让它在调用链中传播即可。但是,如果您稍后意识到需要进行异常处理,则必须重构链接语法才能插入适当的try / catch处理程序。

与异常处理类似,是确定性处置资源的情况。在C#中实现这一目标的最简单方法是使用using() - 遗憾的是,链接语法排除了这一点。如果您正在调用返回一次性对象的方法,那么最好避免链接语法,这样您就可以成为一名优秀的“代码公民”并尽早处理这些资源。

方法链接语法通常用于fluent APIs,它允许代码的语法更接近地反映您想要的操作顺序。 LINQ是.NET中的一个例子,经常可以看到流畅/链接语法。

答案 1 :(得分:7)

可读性是最大的问题,但通常这根本不是问题。

您还可以将LINQ查询语法描述为(在其下面)完全这样的设置。它只是让它看起来更漂亮;-p

一个可能的问题是您需要介绍usinglock等内容的地方。使用流畅的API,您可能只想删除这些组件,但这可能会在抛出异常时导致奇怪。

另一种可能的想法是,您可能希望围绕某些调用进行更细粒度的异常处理;但你总是可以打破流程:

var foo = bar.MethodA().MethodB(...).MethodC();
try {
    foo.MethodD();
} catch (SomeSpecificException) {
    //something interesting
}

或者您甚至可以在扩展方法中 以保持流畅的外观:

bar.MethodA().MethodB(...).MethodC().MyExtensionMethodD();

其中MyExtensionMethodD是您通过特殊处理添加的(例外,锁定,使用等)。

答案 2 :(得分:6)

这被某些人认为是代码嗅觉而不是其他人。任何时候你看到以下内容:

Foo.getBar().getBlah().getItem().getName();

你应该真的在想,“我真正想要的是什么?”相反,您的方法可能包含函数调用:

String getName(Int _id, String _item)
{
    return myBar.getName( _id, _item );
}

然后在课程中向下委派。然后,如果稍后更新某个类中的某些内容发生更改,您将确切地看到它发生的位置并可以在一个位置更改它。

答案 3 :(得分:4)

你应该考虑的唯一一个问题(如果你忽略了可读性)是资源处理和GC ..你应该问的问题是。

  • 我正在进行的调用返回应该处理的对象吗?
  • 我是否在单个范围内启动了大量内容,而不是在GC可能更具反应性的较小范围内? (GC计划在每次离开作用域时运行,虽然计划和实际运行时有两个不同的东西:-p)。

但是真的..天气你每行调用一个方法,或者将它们全部串起来以使它的所有结果在IL中相同(或者几乎相同)。

约什

答案 4 :(得分:0)

除了其他答案之外,您还可能遇到一个技术限制:很长的方法链会导致提供智能感知的服务崩溃,随后它们的主机进程(Linqpad 6、Visual Studio 2019 和其他可能)崩溃。

当我将用于跟踪个人工作的脚本从基于集合的 API 转换为流畅的 API 调用链时,我发现了这一点。我发现我拥有的代码会使我放入的任何 IDE 崩溃。研究使我发现了这个长期存在的 github 问题:https://github.com/dotnet/roslyn/issues/9795

很容易将很长的链分解成单独的语句。