Почему я должен избегать создания MutableTupleкласс в C #? - PullRequest
21 голосов
/ 10 мая 2011

Я большой поклонник .NET 4.0's Классы кортежей .

Все предметы в кортежах неизменны. Очевидно, есть случаи, когда это выгодно (наиболее очевидно, когда кортежи используются для представления специального значения ValueType, для которого нет объявления).

Тем не менее, у меня есть некоторые случаи использования, когда я вижу преимущество для элементов кортежа, имеющих сеттеры (за исключением параметра TRest Type в Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>). Учитывая, что у меня есть доступ к источнику и к статье Мэтта Эллиса о "Building Tuple" , кажется, что реализовать такой MutableTuple<T1,T2,TEtc> было бы довольно просто.

Microsoft явно приняла решение сделать неизменным Tuple. Есть ли причина, по которой я упускаю из виду, что я не должен создавать альтернативную реализацию с изменяемыми элементами, не являющимися кортежами?

Ответы [ 3 ]

24 голосов
/ 10 мая 2011

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

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

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

  • Правильное именование переменных
  • Возможность добавить проверку в классе, содержащем данные

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

3 голосов
/ 17 мая 2011

Я создал версию для чтения / записи для Запоздалая мысль .Однако, основываясь на отзывах API от сообщества, я решил изменить API, чтобы сделать его ненужным.Тем не менее, моя первоначальная потребность была такой же, как ваша: иметь строго типизированные значения параметров для методов и хотеть поведение, похожее на Tuple, которое было бы доступно для чтения / записи и совместимо с .NET 3.5.Вот моя реализация, за исключением поддержки TRest:

/// <summary>
/// Abstract base class for generic <see cref="Parameter<T1>"/> family of classes
/// that represent parameters passed to constructors and methods.
/// </summary>
/// <remarks>
/// This class was created due to a desire to support .NET 3.5, which does not
/// include the <see cref="Tuple"/> class providing similar capabilities
/// </remarks>
public abstract class Parameter
{}

public class Parameter<T1> : Parameter
{
    public Parameter(T1 param1)
    {
        this.Param1 = param1;
    }

    public T1 Param1 { get; set; }
}

public class Parameter<T1, T2> : Parameter<T1>
{
    public Parameter(T1 param1, T2 param2)
        : base(param1)
    {
        this.Param2 = param2;
    }

    public T2 Param2 { get; set; }
}

public class Parameter<T1, T2, T3> : Parameter<T1, T2>
{
    public Parameter(T1 param1, T2 param2, T3 param3)
        : base(param1, param2)
    {
        this.Param3 = param3;
    }

    public T3 Param3 { get; set; }
}

public class Parameter<T1, T2, T3, T4> : Parameter<T1, T2, T3>
{
    public Parameter(T1 param1, T2 param2, T3 param3, T4 param4)
        : base(param1, param2, param3)
    {
        this.Param4 = param4;
    }

    public T4 Param4 { get; set; }
}

public class Parameter<T1, T2, T3, T4, T5> : Parameter<T1, T2, T3, T4>
{
    public Parameter(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5)
        : base(param1, param2, param3, param4)
    {
        this.Param5 = param5;
    }

    public T5 Param5 { get; set; }
}

public class Parameter<T1, T2, T3, T4, T5, T6> : Parameter<T1, T2, T3, T4, T5>
{
    public Parameter(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6)
        : base(param1, param2, param3, param4, param5)
    {
        this.Param6 = param6;
    }

    public T6 Param6 { get; set; }
}

public class Parameter<T1, T2, T3, T4, T5, T6, T7> : Parameter<T1, T2, T3, T4, T5, T6>
{
    public Parameter(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7)
        : base(param1, param2, param3, param4, param5, param6)
    {
        this.Param7 = param7;
    }

    public T7 Param7 { get; set; }
}

public class Parameter<T1, T2, T3, T4, T5, T6, T7, T8> : Parameter<T1, T2, T3, T4, T5, T6, T7>
{
    public Parameter(T1 param1, T2 param2, T3 param3, T4 param4, T5 param5, T6 param6, T7 param7, T8 param8)
        : base(param1, param2, param3, param4, param5, param6, param7)
    {
        this.Param8 = param8;
    }

    public T8 Param8 { get; set; }
}

Я согласен, что лучше иметь Param1, чем Item1, поскольку это делает использование более понятным.К счастью, я смог перейти к делегатам на основе ссылок для одного сценария, где мне нужно было поведение чтения / записи.В моем случае мне пришлось избегать введения каких-либо классов в решение.Надеюсь, это поможет!

1 голос
/ 16 мая 2011

Класс Tuple, являющийся неизменным, относится к концепциям функционального программирования.В рамках этих концепций вы ожидаете, что все ваши переменные будут неизменными.Правильный способ «изменить» значения в этой области - создать копию с измененными значениями.Вы можете легко сделать это, передав исходный кортеж и новое значение, создав новый кортеж со старыми значениями плюс новое и вернув новый кортеж.Это гарантирует, что любой код, который создает кортеж, может гарантировать, что он не будет изменен при передаче другой функции.Если бы вы создали изменчивый класс, я бы не назвал его Tuple, поскольку это противоречит обычным ожиданиям.

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

...