23

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

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

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 多个方法链有什么我应该担心的吗?还是使用非常长的方法链没有害处?

4

5 回答 5

34

方法链的长度没有技术限制。

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

调试由于使链接如此优雅的同一事实而变得复杂 - 缺少中间临时变量。不幸的是,如果没有临时变量,调试时检查中间结果会变得很痛苦。

异常处理很复杂,因为您无法将一种方法与另一种方法引发的异常隔离开来。通常,如果您不能对异常做一些有意义的事情来响应异常,这不是问题——只要让它在调用链上传播即可。但是,如果您稍后意识到需要异常处理,则必须重构链接语法以便能够插入适当的 try/catch 处理程序。

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

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

于 2010-05-17T13:04:45.060 回答
7

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

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

一个可能的问题是您需要在哪里引入诸如using或之类的东西lock;使用 fluent API,您可能会想简单地删除这些组件,但这可能会在抛出异常时导致异常。

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

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

或者你甚至可以在扩展方法中做到这一点,以保持流畅的外观:

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

您在哪里MyExtensionMethodD添加了特殊处理(异常、锁定、使用等)。

于 2010-05-17T13:06:55.780 回答
6

这被某些人认为是代码异味,而其他人则不认为这是一种代码味道。每当您看到以下内容时:

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

你真的应该想,“我真正想要什么?” 相反,也许您的方法应该包含一个函数调用:

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

然后在类之间向下委托。然后,如果您的某个类在以后的更新中发生了某些变化,您将准确地看到它发生的位置,并且可以在一个位置进行更改。

于 2010-05-17T13:08:42.690 回答
4

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

  • 我正在调用返回的对象是否应该被处理掉?
  • 我是否在单个范围内启动了很多东西,而不是 GC 可能对它更具反应性的较小范围?(GC 计划在您每次离开范围时运行,尽管调度和实际运行时间是两件不同的事情:-p)。

但实际上.. 天气你每行调用一个方法,或者将它们全部串在一起,最终在 IL 中都是相同的(或几乎相同)。

乔什

于 2010-05-17T13:05:40.413 回答
1

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

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

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

于 2021-04-10T12:27:14.180 回答