C# C方法链是否可以是;太长了;?

C# C方法链是否可以是;太长了;?,c#,method-chaining,C#,Method Chaining,当然,在可读性方面不是这样,因为您总是可以将单独的方法安排到单独的行中。相反,出于任何原因,将过多的方法链接在一起是否危险?我使用方法链接主要是为了在声明单个one use变量时节省空间,并且传统上使用返回方法而不是修改调用方的方法。除了字符串方法外,我对这些方法有点无情。在任何情况下,我有时担心在一行中使用异常长的方法链的影响 假设我需要根据某人的用户名更新一个项目的值。不幸的是,检索正确用户的最短方法如下所示 SPWeb web = GetWorkflowWeb(); SPList list

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

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

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的参数都只能用于一次呼叫,因此我可以将其压缩为以下内容(这也让我摆脱了a将是多余的1):


诚然,当它全部放在一行中并且使用
int.Parse(ddlArmy.SelectedValue.CutBefore(“;#”,false))
而不是
3
时,它看起来要长得多。然而,这是这些链条的平均长度之一,我可以很容易地预见一些异常长的计数。除去可读性,对于这10+个方法链,有什么我应该担心的吗?或者使用非常长的方法链没有害处吗?

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

然而,三个可能成为问题的领域是调试、异常处理和资源处理

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

由于无法将从一种方法引发的异常与从另一种方法引发的异常隔离开来,因此异常处理变得复杂。通常情况下,如果您不能对异常做出有意义的响应,那么这不是一个问题——只是让它在调用链中向上传播。但是,如果您后来意识到需要异常处理,则必须重构链接语法,以便能够插入适当的try/catch处理程序

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


方法链接语法通常在中使用,它允许代码的语法更紧密地反映您想要的操作序列。LINQ是.NET中的一个例子,在那里经常看到FLUT/CURING语法。

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

  • 我正在进行的调用是否返回了应该处理的对象
  • 我是不是在单个范围内初始化了很多东西,而不是GC可能更容易反应的较小范围?(GC计划在每次离开作用域时运行,但计划和实际运行时间是两件不同的事情:-p)
但是真的。。无论您是每行调用一个方法,还是将它们串在一起,它们在IL中都是相同的(或者几乎相同)


Josh

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

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

一个可能的问题是,您需要在哪里引入诸如使用
锁定
之类的东西;使用fluentapi,您可能会试图简单地删除这些组件,但当抛出异常时,这可能会导致异常

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

var foo = bar.MethodA().MethodB(...).MethodC();
try {
    foo.MethodD();
} catch (SomeSpecificException) {
    //something interesting
}
或者您甚至可以通过扩展方法来保持流畅的外观:

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

其中,
MyExtensionMethodD
是您添加的带有特殊处理(异常、锁、使用等)的方法。

这被一些人认为是代码气味,而不是其他人。只要您看到以下内容:

Foo.getBar().getBlah().getItem().getName();
您应该真正思考“我真正想要什么?”而不是您的方法应该包含函数调用:

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

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

除了其他答案,您可能会遇到一个技术限制:非常长的方法链将导致提供intellisense的服务崩溃,并随后导致其主机进程崩溃(Linqpad 6、Visual Studio 2019,可能还有其他)

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


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

同意,我只是想发布相同的内容。这些都是由工具引起的技术限制。请参阅我的答案:GC不会计划在每次离开作用域时运行—它在托管堆上没有可用空间时运行。是这样吗?我知道当我测试使用distructor来管理一个单例池时(大约3年前),当我离开作用域时,不管堆大小(即几乎没有)都会调用distructor。。我知道,在GC的第一个过程中,会调用distructor(当存在时)。十不是
String getName(Int _id, String _item)
{
    return myBar.getName( _id, _item );
}