2011-05-10 8 views
21

私は.NET 4.0の大ファンです。Tuple classesです。C#でMutableTuple <T1,T2,TEtc>クラスを作成しないでください。

タプル内のすべての項目は不変です。これが有益である明確なケースがあります(宣言がない特別なValueTypeを表すためにTuplesが使用されている場合が最も明白です)。

しかし、私はタプルのアイテムにセッターを持つ利点を見ることができます(Tuple<T1, T2, T3, T4, T5, T6, T7, TRest>TRestタイプパラメータを除く)。私がソースとMatt Ellis's article on "Building Tuple"にアクセスできることを考えると、そのようなMutableTuple<T1,T2,TEtc>を実装するのはかなりシンプルなようです。

マイクロソフトでは、Tupleを不変にするという決定は明らかでした。 変更できない非タプル項目の代替実装を作成してはいけないということを見落としている理由はありますか?

+8

小さなクラスを作成して、必要な情報を格納し、プロパティとすべてを使用して完成させるのはなぜですか? – Chad

+0

@Chad、必要以上に多くの作業があるので、なぜ[TupleのアイテムがReadOnlyなのか]の – smartcaveman

+0

の複製がありますか?(http://stackoverflow.com/questions/3131400/why-tuples-items-are-readonly) – nawfal

答えて

24

私の意見では、Tupleクラスは通常、短い実装シナリオでのみ内部実装の詳細についてデータを表現するために使用します。たとえば、リファクタリング中にプライベートメソッドから複数の値を返すときに便利です。

値がパブリックAPIの一部になるか、API内で長く存続するようになると、私は個人的には、メンテナンス性の観点から、カスタムクラスまたは構造体を使用する方がはるかに良いと感じています適切な名前で必要とする。

「変更可能なタプル」は、定義上、ライフサイクルが長くなることを意図して作成されたものです。作成する場合は後で変更し、効果的ですオブジェクトは目的のために存在すると言っています。それは含めて、保守性の面で多くの利点を提供しますように私は、その時点で、このデータを保持するために、カスタムクラスをお勧めします:変数の

  • 適切な命名
  • データを含むクラス内の検証を追加する機能

第2の点は、特に時間が経過するにつれて非常に重要になります。データを変更する場合は、ある時点で値を検証して値が適切であることを確認する必要があります。タプルを使用する場合、この検証はデータを含むクラス外で行わなければならず、コードの保守性を大幅に低下させる可能性があります。

+0

実際には、私ユースケースは「より長いライフサイクル」を伴わない。私のユースケースのライフサイクルは、拡張メソッドの範囲内です。 (1) 'Func 、TResult> Tupled (このFunc func)'、および(2) 'TResult InvokeWithContract (1)こののFunc FUNC、のFunc validatePreCondition、アクション resolvePreConditionFail、TのFunc validatePostCondition、アクション resolvePostConditionFail、Tの引数):IStructuralEquatable、IStructuralComparable' – smartcaveman

+0

私は、これは 'のFunc で達成することができることを認識しないが'代理人ではなく、一般的な' in'パラメータを使うことができないことを意味し、私のコードは簡潔ではないでしょう。この特定の状況でMutableTupleが悪い決断をしているとはまだ考えていますか? – smartcaveman

+0

@smartcaveman:私は個人的にはまだそれを不変のまま扱いますが、変更が必要な場合は、メンバーを新しいタプルにコピーします。つまり、このようなAPIの場合は、カスタムクラスを使用する可能性があります。特に、これが公開APIで使用される場合は... –

1

Tupleクラスが不変であるということは、関数型プログラミングの概念に戻る。これらの概念の中では、すべての変数が不変であると予想します。この領域の値を変更する正しい方法は、変更された値でコピーを作成することです。オリジナルのタプルと新しい値を渡し、古い値と新しいタプルを加えた新しいタプルを作成し、新しいタプルを返すことで簡単にこれを達成できます。これにより、タプルを作成するコードは、別の関数に渡されたときに変更されないことが保証されます。変更可能なクラスを作成した場合、それは従来の期待に反してタプルと呼ばれることはありません。

あなたの実装には、実際にf#のようなものを使用する方が良いかもしれません。高次関数、型推論、および計算式を備えていますが、コードの複雑さを必ずしも軽減するわけではありませんが、飛躍的に読みやすさが向上します。

3

Afterthoughtの読み取り/書き込みバージョンを作成しました。しかし、コミュニティからのAPIフィードバックに基づいて、APIを不要にするように変更しました。しかし、私の最初の必要性は、メソッドに強く型付けされたパラメータ値を持ち、読み書き可能なTupleのような振る舞いや.NET 3.5との互換性を求めているあなたのものと似ていました。ここに私の実装、マイナストレストのサポートです:

/// <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; } 
} 

私はそれが利用がよりわかりやすいよう、それはアイテム1以上のParam1を持つよりよいであることに同意します。私は幸いにも、私が読み書き行動が必要なシナリオのために、参照ベースのデリゲートに移動することができました。私の場合は、クラスをソリューションに導入することを避けなければなりませんでした。お役に立てれば!

関連する問題