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

Почему я должен избегать создания класса С# MutableTuple <T1, T2, TEtc> в С#?

Я большой поклонник .NET 4.0 Классы Tuple.

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

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

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

4b9b3361

Ответ 1

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

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

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

  • Собственное присвоение имен
  • Возможность добавления проверки в класс, содержащий данные

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

Ответ 2

Я создал версию для чтения/записи для Afterthought. Однако, основываясь на отзывах API от сообщества, я решил изменить API, чтобы сделать его ненужным. Однако моя первоначальная потребность была похожа на вашу, с сильно типизированными значениями параметров для методов и желанием поведения, подобного типу, которое было прочитано/записано, а также совместимо с .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, поскольку это делает использование более понятным. Я, к счастью, смог перейти к делегатам, основанным на ссылках, для одного сценария, где мне нужно поведение чтения/записи. В моем случае мне пришлось избегать введения каких-либо классов в решение. Надеюсь, это поможет!

Ответ 3

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

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