Может ли цепочка методов C # быть "слишком длинной"? - PullRequest
21 голосов
/ 17 мая 2010

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

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

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 ]

33 голосов
/ 17 мая 2010

Нет никаких технических ограничений на длину цепочки методов.

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

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

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

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

Синтаксис цепочки методов часто используется в беглых API , где он позволяет синтаксису вашего кода более точно отражать последовательность операций, которую вы намереваетесь. LINQ - один из примеров в .NET, где часто встречается свободный синтаксис / цепочка.

7 голосов
/ 17 мая 2010

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

Вы также можете описать синтаксис запроса LINQ как (под всем этим) точно такой установки. Это просто делает его красивее; -p

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

Другая возможная мысль заключается в том, что вам может потребоваться более детальная обработка исключений для некоторых вызовов; но вы всегда можете просто прервать поток:

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

Или вы можете даже сделать это в методе расширения, чтобы сохранить свободный внешний вид:

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

где MyExtensionMethodD - это тот, который вы добавляете со специальной обработкой (исключения, блокировки, использование и т. Д.).

6 голосов
/ 17 мая 2010

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

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

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

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

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

4 голосов
/ 17 мая 2010

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

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

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

Josh

...