Как правильно связать методы в .Net

В .Net вы можете связать методы, возвращающие значение, или использовать пустоту. Является ли один из них «правильным путем»?

Таким образом, вы могли бы сказать

1)

Foo myFoo = new Foo();
myfoo.Bars = 
  myBars.DoSomethingCool(x)
  .DoSomethingElse(y)
  .AndSomethingElse(z);

public static IList<IBar> DoSomethingCool(this IList<IBar> source, object x)
{
  IList<IBar> result = //some fn(source)
  return result;
}

В этом случае все 3 метода расширения должны возвращать IList (тип для myFoo.Bars)

или это также может быть написано как

2)

myBars.DoSomethingCool(x)
.DoSomethingElse(y)
.AndSomethingElse(z);

public static void DoSomethingCool(this IList<IBar> source, object x)
{
  //Modify source
  source = //some fn(source)
  //Don't return anything
}

в этом случае методы расширения возвращают пустоту, но выполняют ли работу с поступающим исходным объектом?

ОБНОВЛЕНИЕ Саймон был прав в своем ответе, что 2) не будет компилироваться. Вот как это можно было бы переписать:

DoSomethingCool(myBars)
.DoSomethingElse(myBars)
.AndSomethingElse(myBars);

Затем myBars будет изменяться внутри вызова каждого метода, и методы будут возвращать значение void.


person John    schedule 14.07.2011    source источник
comment
Цепочки методов усложняют отладку, модификацию и сопровождение кода.   -  person Steve Wellens    schedule 14.07.2011
comment
Я не уверен, что понимаю, как это усложняет отладку, модификацию и обслуживание. Если вы поместите цепочку на отдельные строки, вы можете установить точку останова на любом из операторов. Цепочка упрощает выражение намерения, что, как мне кажется, облегчает, а не усложняет обслуживание.   -  person John    schedule 14.07.2011
comment
Ваш второй пример не скомпилируется. DoSomethingElse() вызывается для возвращаемого результата DoSomethingCool, который является пустым.   -  person sisve    schedule 14.07.2011
comment
Цепочки плохи, потому что вы не можете пройти каждую логическую строку кода... все или ничего. Вы не можете поставить точку останова на шаге. Если шаг терпит неудачу, вы не знаете, какой это был шаг. Размещение всего кода в одной строке затрудняет разбор отдельных шагов глазами. Для иллюстрации поместите каждый шаг вашей программы в ОДНУ СТРОКУ. Вам это нравится? Намного лучше помещать каждый логический шаг в одну строку.   -  person Steve Wellens    schedule 14.07.2011
comment
Саймон, ты прав. Я должен вернуть тип, иначе я могу связать результат только один раз.   -  person John    schedule 14.07.2011
comment
Стив, я отредактировал сообщение пару часов назад, чтобы поместить операторы цепочки в отдельные строки. Возможно, вы смотрите на предыдущий код?   -  person John    schedule 14.07.2011
comment
Стив, мой плохой, я понимаю, что ты говоришь. Я думал, что вспомнил про установку точек останова на отдельных строках, но ошибся. Вы должны установить точки останова в самих методах расширения, что немного неудобно, но все же может быть сделано. Тогда возникает вопрос, что важнее: дополнительная удобочитаемость плавного стиля цепочки по сравнению с дополнительным временем, которое требуется для отладки.   -  person John    schedule 14.07.2011


Ответы (2)


В обоих случаях вы используете метод расширения, возвращаете объект, а затем используете этот объект в качестве входных данных для следующего метода в цепочке. Второй пример близок к свободному интерфейсу (но не совсем). Я думаю, что в ваших двух примерах решающее различие заключается в изменчивости. Вы хотите, чтобы исходный ввод был изменен (второй пример) или нет (первый пример)? «Правильный» ответ зависит от ситуации и контекста.

person daveaglick    schedule 14.07.2011
comment
Вот в чем вопрос: можно ли изменять исходный ввод или делать манипуляции с побочными эффектами плохо? - person John; 14.07.2011
comment
Я не знаю, что изменение с помощью методов расширения таким образом обязательно плохо, хотя лично я мог бы воздержаться от этого, чтобы избежать путаницы - как минимум я бы назвал методы, которые мутируют объект, чем-то вроде Change... или Modify .... По соглашению почти все методы расширения Linq работают с итераторами и предоставляют новую последовательность, а не модифицируют исходную. Работая и возвращая наименьший общий знаменатель (итераторы), расширения Linq могут адаптироваться к самому широкому спектру ситуаций и целевых объектов. - person daveaglick; 14.07.2011
comment
Однако следует иметь в виду, что методы расширения на самом деле ничем не отличаются от любого другого метода, первый параметр просто неявный, а не явный. Подразумевается, что, поскольку он является внешним по отношению к объекту, с которым выполняется операция, этот метод не может иметь больше доступа, чем любой другой. Если исходный объект неизменяемый, то методы в цепочке также не могут его изменить. Это означает, что вы не рискуете нарушить инкапсуляцию при любом подходе, если исходный интерфейс объекта был разработан соответствующим образом. - person daveaglick; 14.07.2011

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

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

mybars.FilterByHasHappyHour() намного лучше, чем

BigGiantUtilityClass.GetBarsWithHappyHours(myBars)
person John    schedule 11.08.2011