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

С#: Возвращение 'this' для вложенности метода?

У меня есть класс, который я должен вызывать один или два метода много раз друг за другом. В настоящее время методы возвращают void. Я думал, было бы лучше, если бы он вернулся this, чтобы методы могли быть вложенными? или это очень важно? или если это плохо, было бы лучше, если бы он вернул новый объект того же типа? Или что вы думаете? В качестве примера я создал три версии класса сумматора:

// Regular
class Adder
{
    public Adder() { Number = 0; }

    public int Number { get; private set; }

    public void Add(int i) { Number += i; }
    public void Remove(int i) { Number -= i; }
}

// Returning this
class Adder
{
    public Adder() { Number = 0; }

    public int Number { get; private set; }

    public Adder Add(int i) { Number += i; return this; }
    public Adder Remove(int i) { Number -= i; return this; }
}

// Returning new
class Adder
{
    public Adder() : this(0) { }
    private Adder(int i) { Number = i; }

    public int Number { get; private set; }

    public Adder Add(int i) { return new Adder(Number + i); }
    public Adder Remove(int i) { return new Adder(Number - i); }
}

Первый может быть использован следующим образом:

    var a = new Adder();
    a.Add(4);
    a.Remove(1);
    a.Add(7);
    a.Remove(3);

Другие два могут быть использованы следующим образом:

    var a = new Adder()
        .Add(4)
        .Remove(1)
        .Add(7)
        .Remove(3);

Если единственное отличие состоит в том, что a в первом случае это new Adder(), а во втором - результат последнего метода.

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

Третий работает подобно многим другим методам, таким как многие методы String и IEnumerable методы расширения. Я полагаю, что у нее есть положительная сторона в том, что вы можете делать такие вещи, как var a = new Adder(); var b = a.Add(5);, а затем иметь один, который был 0, и тот, который был 5. Но в то же время не так ли дорого создавать новые объекты все время? И когда первый объект умрет? Когда первый метод возвращает вид? Или?

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

4b9b3361

Ответ 2

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

еще одна идея сделать интерфейс, такой как сумматоры, более простым в использовании:

public Adder Add(params int[] i) { /* ... */ }
public Adder Remove(params int[] i) { /* ... */ }

Adder adder = new Adder()
  .Add(1, 2, 3)
  .Remove(3, 4);

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

Ответ 3

Цепочки - это хорошая вещь, чтобы иметь и ядро ​​в некоторых фреймворках (например, расширения Linq и jQuery используют его сильно).

Создаете ли вы новый объект или return this зависит от того, как вы ожидаете, что ваш исходный объект будет вести себя:

var a = new Adder();

var b = a.Add(4)
         .Remove(1)
         .Add(7)
         .Remove(3);

//now - should a==b ?

Цепочка в jQuery изменит ваш исходный объект - он вернул это. Это ожидаемое поведение - в противном случае будет клонировать элементы пользовательского интерфейса.

Цепочки в Linq оставят неизмененную исходную коллекцию. Это тоже ожидаемое поведение - каждая связанная функция является фильтром или преобразованием, а исходная коллекция часто неизменно.

Какой шаблон лучше подходит для того, что вы делаете?

Ответ 4

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

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

Ответ 5

  • В вашем конкретном случае перегрузка арифметических операторов, вероятно, будет лучшим решением.

  • Возвращение this (Свободный интерфейс) является обычной практикой для создания выражений - модульное тестирование и насмешливые фреймворки используют это много. Fluent Hibernate - еще один пример.

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

Ответ 6

Если вы называете это Adder, я бы вернулся с этим. Тем не менее, это довольно странно, если класс Adder содержит ответ.

Возможно, вы захотите сделать что-то вроде MyNumber и создать метод Add().

В идеале (IMHO), который не изменит число, которое хранится внутри вашего экземпляра, но создайте новый экземпляр с новым значением, которое вы вернете:

class MyNumber
{
    ...

    MyNumber Add( int i )
    {
        return new MyNumber( this.Value + i );
    }
}

Ответ 7

Я думаю, что для простых интерфейсов "свободный" интерфейс очень полезен, особенно потому, что его очень просто реализовать. Значение свободного интерфейса заключается в том, что он устраняет много постороннего пуха, который мешает пониманию. Разработка такого интерфейса может занять много времени, особенно когда интерфейс начинает участвовать. Вам следует беспокоиться о том, как использование интерфейса "читает"; На мой взгляд, наиболее привлекательным для такого интерфейса является то, как он передает намерение программиста, а не количество символов, которое он сохраняет.

Чтобы ответить на ваш конкретный вопрос, мне нравится стиль "вернуть этот". Мое типичное использование свободного интерфейса - это определение набора параметров. То есть я создаю экземпляр класса, а затем использую быстрые методы в экземпляре для определения желаемого поведения объекта. Если у меня есть опция yes/no (скажем, для ведения журнала), я стараюсь не иметь метод setLogging (bool state), а два метода "WithLogging" и "WithoutLogging". Это несколько больше, но ясность конечного результата очень полезна.

Ответ 8

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

var a = новый Adder()         .Add(4);

var b = a.Remove(1);

var c = a.Add(7)         .Remove(3);

В этом случае оба b и c имеют состояние, зафиксированное в как отправную точку. Я наткнулся на эту идиому, когда читал о шаблоне для создания объектов тестового домена в Растущее объектно-ориентированное программное обеспечение, руководствуясь испытаниями Стива Фримена; Nat Pryce.

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