Может ли цепочка методов C# быть "слишком длинной"?

не с точки зрения читаемости, естественно, так как вы всегда можете организовать отдельные методы в отдельные строки. Скорее, опасно ли по какой-либо причине связывать чрезмерно большое количество методов вместе? Я использую метод chaining в первую очередь для экономии места при объявлении отдельных одноразовых переменных и традиционно использую методы return вместо методов, которые изменяют вызывающего. За исключением струнных методов, которые я безжалостно цепляю. В любом случае, я иногда беспокоюсь о влияние использования исключительно длинных цепочек методов все в одной линии.

предположим, мне нужно обновить значение одного элемента на основе чьего-то имени пользователя. К сожалению, самый короткий метод для получения правильного пользователя выглядит примерно следующим образом.

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 ответов


нет никаких технических ограничений на то, насколько длинной может быть цепочка методов.

однако три области, которые могут стать проблематичными, являются отладка, обработка исключений и утилизация ресурсов.

отладка осложняется тем же фактом, что делает цепочку настолько элегантной - отсутствием промежуточных временных переменных. К сожалению, без временных переменных, проверка промежуточных результатов при отладке становится больно.

обработка исключений осложняется тем, что вы не можете изолировать исключения, возникающие из одного метода против другого. Обычно это не проблема, если вы не можете сделать что - то значимое в ответ на исключение-просто позвольте ему распространяться по цепочке вызовов. Однако, если позже вы поймете, что вам нужна обработка исключений, вам нужно рефакторировать синтаксис цепочки, чтобы иметь возможность вставлять соответствующие обработчики try/catch.

подобно исключению обращение - это случай детерминированного распоряжения ресурсами. Самый простой способ в C# для этой цели using() - к сожалению, синтаксис цепочки исключает это. Если вы вызываете методы, возвращающие одноразовые объекты, вероятно, рекомендуется избегать синтаксиса цепочки, чтобы вы могли быть хорошим "гражданином кода" и как можно раньше распоряжаться этими ресурсами.

синтаксис цепочки методов часто используется в свободно APIs, где это позволяет синтаксис вашего код, чтобы более точно отразить последовательность операций, которые вы намереваетесь. LINQ является одним из примеров в .NET, где часто встречается синтаксис fluent/chaining.


читабельность является большой проблемой, но часто это не проблема вообще.

вы также можете описать синтаксис запроса 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 - это тот, который вы добавляете со специальной обработкой (исключения, блокировки, использование и т. д.).


это считается код запах, а не другим. В любое время вы видите следующее:

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

вы действительно должны думать: "чего я действительно хочу?"Вместо этого, возможно, ваш метод должен содержать вызов функции:

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

который затем делегирует вниз, среди классов. Потом, если что-то меняется в одном из ваших классов в более позднем обновлении, вы увидите, где именно это происходит и можете изменить его в одном месте.


единственный консерн, который вы должны рассмотреть относительно этого (если yor игнорирует читаемость), - это обработка ресурсов и GC.. Вопросы, которые вы должны задать.

  • являются ли вызовы, которые я делаю, возвращая объекты, которые должны быть удалены?
  • Я использую много вещей внутри одной области, а не меньшие области, на которые GC может быть более реактивным? (GC планируется запускать каждый раз, когда вы покидаете область, хотя планирование и когда оно фактически выполняется-это две разные вещи :- p).

но на самом деле.. погода вы вызываете один метод на строку или строите их все вместе, и все они оказываются одинаковыми в IL (или почти одинаковыми).

Джош