当然,这不是在可读性方面,因为您始终可以将单独的方法排列到单独的行中。相反,出于某种原因,将过多的方法链接在一起是否危险?我使用方法链接主要是为了节省声明单个一次性变量的空间,并且传统上使用返回方法而不是修改调用者的方法。除了字符串方法之外,我都会无情地链接这些方法。无论如何,我有时担心在一行中使用特别长的方法链的影响。
假设我需要根据某人的用户名更新一项的值。不幸的是,检索正确用户的最短方法如下所示。
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+个方法链还有什么值得担心的吗?或者使用非常长的方法链没有坏处吗?
Not in terms of readability, naturally, since you can always arrange the separate methods into separate lines. Rather, is it dangerous, for any reason, to chain an excessively large number of methods together? I use method chaining primarily to save space on declaring individual one-use variables, and traditionally using return methods instead of methods that modify the caller. Except for string methods, those I kinda chain mercilessly. In any case, I worry sometimes about the impact of using exceptionally long method chains all in one line.
Let's say I need to update the value of one item based on someone's username. Unfortunately, the shortest method to retrieve the correct user looks something like the following.
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;
Everything with a 2 or 3 lasts for only one call, so I might condense it as the following (which also lets me get rid of a would-be-superfluous 1):
SPWeb web = GetWorkflowWeb();
item["Contact"] = web.Lists["Armies"]
.GetItemById(3)
.GetItemFromLookup("Armies", "Allied Army")
.GetSPUser("Commander")
.GetAssociate("Spouse")
.Name;
Admittedly, it looks a lot longer when it is all in one line and when you have int.Parse(ddlArmy.SelectedValue.CutBefore(";#", false))
instead of 3
. Nevertheless, this is one of the average lengths of these chains, and I can easily foresee some of exceptionally longer counts. Excluding readability, is there anything I should be worried about for these 10+ method chains? Or is there no harm in using really really long method chains?
发布评论
评论(5)
对于方法链的长度没有技术限制。
但是,三个可能出现问题的领域是调试、异常处理和资源处置。
使链接如此优雅的同一事实使调试变得复杂 - 缺乏中间临时变量。不幸的是,如果没有临时变量,调试时检查中间结果会变得很痛苦。
由于无法将一种方法引发的异常与另一种方法引发的异常隔离开来,因此异常处理变得很复杂。通常,如果您不能做一些有意义的事情来响应异常,那么这不是问题 - 只需让它在调用链中传播即可。但是,如果您稍后意识到需要异常处理,则必须重构链接语法以便能够插入适当的 try/catch 处理程序。
与异常处理类似的是资源的确定性处置的情况。 C# 中实现此目的的最简单方法是使用
using()
- 不幸的是,链接语法阻止了这一点。如果您正在调用返回一次性对象的方法,那么避免链接语法可能是一个好主意,这样您就可以成为一个好的“代码公民”并尽早处理这些资源。方法链接语法通常用在 Fluent API 中,它允许代码的语法更加灵活密切反映您想要的操作顺序。 LINQ 是 .NET 中经常出现流畅/链接语法的示例之一。
There are no technical limitations on how long a method chain can be.
However, three areas that can become problemattic are debugging, exception handling, and resource disposal.
Debugging is complicated by the same fact that makes chaining so elegant - the lack of intermediate temporary variables. Unfortunately, without temp variables, inspecting intermediate results when debugging becomes painful.
Exception handling is complicated by the fact that you cannot isolate exceptions raised from one method vs. another. Normally this isn't an issue if you can't do something meaningful in response to the exception - just let it propagate up the call chain. However, if you do realize at a later point that you need exception handling, you have to refactor the chaining syntax to be able to insert appropriate try/catch handlers.
Similar to exception handling is the case of deterministic disposal of resources. The easiest way in C# to accomplish this is with
using()
- unfortunately, chaining syntax precludes that. If you are calling methods that return disposable objects, it probably a good idea to avoid chaining syntax so that you can be a good "code citizen" and dispose of those resources as early as possible.Method chaining syntax is often a used in fluent APIs, where it allows the syntax of your code to more closely reflect the sequence of operations you intend. LINQ is one example in .NET where fluent/chaining syntax is often seen.
可读性是最大的问题,但通常这根本不是问题。
您还可以将 LINQ 查询语法描述为(在其之下)确切这样的设置。它只是让它看起来更漂亮;-p
一个可能问题是您需要引入诸如
using
或lock
之类的东西;使用 Fluent API,您可能会想简单地删除这些组件,但这可能会在抛出异常时导致奇怪的情况。另一种可能的想法是,您可能希望对某些调用进行更精细的异常处理;但您始终可以打破流程:
或者您甚至可以在扩展方法中执行此操作以保持流畅的外观:
其中
MyExtensionMethodD
是您通过特殊处理添加的方法(异常、锁、使用等)。Readability is the biggest concern, but often that isn't a problem at all.
You could also describe LINQ query syntax as (underneath it all) exactly such a setup. It just makes it look prettier ;-p
One possible issue is where you need to introduce things like
using
orlock
; with the fluent API you may be tempted to simply drop these components, but that might lead to oddities when an exception is thrown.The other possible thought is that you might want to have more granular exception handling around some of the calls; but you can always just break the flow:
Or you could even do that in an extension method to keep the fluent appearance:
where
MyExtensionMethodD
is the one you add with special handling (exceptions, locks, using, etc).有些人认为这是一种代码味道,而另一些人则不这么认为。每当你看到以下内容时:
你真的应该思考“我真正想要什么?”相反,也许您的方法应该包含一个函数调用:
然后在类之间向下委托。然后,如果稍后更新时您的某个类发生了某些变化,您将准确地看到发生变化的位置,并可以在一个位置进行更改。
This is considered a code smell by some and not by others. Anytime you see the following:
you should really be thinking, "what do I really want?" Instead perhaps your method should contain a function call:
which then delegates downward, amongst the classes. Then, if something changes in one of your classes at a later update, you'll see exactly where it occurs and can change it in one location.
您应该考虑的唯一问题(如果您忽略可读性)是资源处理和 GC。您应该问的问题是。
但实际上......无论您每行调用一个方法,还是将它们全部串在一起,它在 IL 中的最终结果都是相同的(或非常接近相同)。
乔什
The only consern you should consider regarding this (if yor ignoring readability) is resource handling and GC.. Questions you should ask are.
But really.. weather you call one method per line, or string them all togeather its all ends up being the same in IL (or very nearly the same).
Josh
除了其他答案之外,您还可能遇到一个技术限制:非常长的方法链将导致提供智能感知的服务崩溃,并随后导致其主机进程(Linqpad 6、Visual Studio 2019 以及可能的其他进程)崩溃。
当我将用于跟踪个人工作的脚本从基于集合的 API 转换为流畅的 API 调用链时,我发现了这一点。我发现我的代码会使我放入的任何 IDE 崩溃。研究使我发现了这个长期存在的 github 问题:https://github.com/dotnet/roslyn/issues/9795
将很长的链分解成单独的语句很容易。
In addition to the other answers, there is a technical limitation you can encounter: very long method chains will cause the services that deliver intellisense to crash and subsequently their host processes (Linqpad 6, Visual Studio 2019, and probably others).
I discovered this when I converted a script that I use to track my personal work from a collection based API to a fluent API invocation chain. I found that the code I had would crash any IDE I put it in. Research led me to this long-standing github issue: https://github.com/dotnet/roslyn/issues/9795
It's easy enough to break up really long chains into separate statements.