Подтвердить что ты не робот

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

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

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

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+? Или нет никакого вреда в использовании действительно очень длинных цепочек методов?

4b9b3361

Ответ 1

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

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

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

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

Ответ 3

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

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

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

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

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

Ответ 4

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

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

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

Джош