172

.net 4にTupleクラスが追加されたことで、私は自分のデザインでそれらを使用することが悪い選択であるかどうかを判断しようとしてきました。私の見方では、タプルは結果クラスを作成するためのショートカットになります(他の用途もあると確信しています)。

したがって、この:

public class ResultType
{
    public string StringValue { get; set; }
    public int IntValue { get; set; }
}

public ResultType GetAClassedValue()
{
    //..Do Some Stuff
    ResultType result = new ResultType { StringValue = "A String", IntValue = 2 };
    return result;
}

これと同等です:

public Tuple<string, int> GetATupledValue()
{
    //...Do Some stuff
    Tuple<string, int> result = new Tuple<string, int>("A String", 2);
    return result;
}

それで、私がタプルの要点を見逃している可能性を別にして、タプルの例は悪い設計の選択ですか?私には、それはすっきりしているように見えますが、自己文書化してきれいではありません。つまり、タイプを使用するResultTypeと、クラスの各部分が何を意味するかは後で非常に明確になりますが、維持する追加のコードがあります。を使用するTuple<string, int>と、それぞれが何を表しているのかを調べて理解する必要がありますがItem、記述して維持するコードは少なくなります。

この選択で経験したことはありがたいです。

4

13 に答える 13

167

タプルは、作成と使用の両方を制御する場合に最適です。タプルを理解するために不可欠なコンテキストを維持できます。

ただし、パブリックAPIでは、効果が低くなります。消費者(あなたではない)は、特にのようなものについては、ドキュメントを推測または検索する必要がありますTuple<int, int>

私はそれらをプライベート/内部メンバーに使用しますが、パブリック/保護されたメンバーには結果クラスを使用します。

この回答にもいくつかの情報があります。

于 2010-06-10T18:49:17.563 に答える
79

私の見方では、タプルは結果クラスを作成するためのショートカットです(他の用途もあると確信しています)。

実際、他にも価値のある用途がありTuple<>ます。それらのほとんどは、類似した構造を共有する特定のタイプのグループのセマンティクスを抽象化し、それらを単純に順序付けられた値のセットとして扱うことを含みます。すべての場合において、タプルの利点は、プロパティを公開するがメソッドは公開しないデータのみのクラスで名前空間が乱雑になるのを回避できることです。

の合理的な使用例を次に示しますTuple<>

var opponents = new Tuple<Player,Player>( playerBob, playerSam );

上記の例では、対戦相手のペアを表現したいので、タプルは、新しいクラスを作成せずにこれらのインスタンスをペアにする便利な方法です。別の例を次に示します。

var pokerHand = Tuple.Create( card1, card2, card3, card4, card5 );

ポーカーハンドは単なるカードのセットと考えることができ、タプル(おそらく)はその概念を表現するための合理的な方法です。

タプルのポイントを見逃している可能性は別として、タプルを使用した例は悪い設計上の選択ですか?

Tuple<>パブリックタイプのパブリックAPIの一部として強く型付けされたインスタンスを返すことは、めったに良い考えではありません。ご存知のように、タプルでは、​​関係者(ライブラリの作成者、ライブラリのユーザー)が、使用されているタプルの種類の目的と解釈につ​​いて事前に合意する必要があります。直感的で明確なAPIを作成することは十分に困難であり、Tuple<>公に使用するとAPIの意図と動作が曖昧になるだけです。

匿名型も一種のタプルですが、強く型付けされており、型に属するプロパティに明確で有益な名前を指定できます。ただし、匿名型はさまざまな方法で使用するのが困難です。これらは主に、通常は名前を割り当てたくない型をプロジェクションが生成するLINQなどのテクノロジをサポートするために追加されました。(はい、同じ型と名前付きプロパティを持つ匿名型がコンパイラーによって統合されることを私は知っています)。

私の経験則は次のとおり です。パブリックインターフェイスから返す場合は、名前付きタイプにします。

タプルを使用するためのもう1つの経験則は、 メソッド引数とタイプのlocalc変数にTuple<>できるだけ明確に名前を付けることです。名前がタプルの要素間の関係の意味を表すようにします。var opponents = ...私の例を考えてみてください。

これは、自分のアセンブリ内でのみ使用Tuple<>するデータのみの型を宣言することを回避するために使用した実際のケースの例です。この状況では、匿名型を含むジェネリックディクショナリを使用すると、メソッドに名前を付けることができないパラメータが必要になるため、メソッドを使用してディクショナリ内のアイテムを検索することが困難になるという事実が関係しています。TryGetValue()out

public static class DictionaryExt 
{
    // helper method that allows compiler to provide type inference
    // when attempting to locate optionally existent items in a dictionary
    public static Tuple<TValue,bool> Find<TKey,TValue>( 
        this IDictionary<TKey,TValue> dict, TKey keyToFind ) 
    {
        TValue foundValue = default(TValue);
        bool wasFound = dict.TryGetValue( keyToFind, out foundValue );
        return Tuple.Create( foundValue, wasFound );
    }
}

public class Program
{
    public static void Main()
    {
        var people = new[] { new { LastName = "Smith", FirstName = "Joe" },
                             new { LastName = "Sanders", FirstName = "Bob" } };

        var peopleDict = people.ToDictionary( d => d.LastName );

        // ??? foundItem <= what type would you put here?
        // peopleDict.TryGetValue( "Smith", out ??? );

        // so instead, we use our Find() extension:
        var result = peopleDict.Find( "Smith" );
        if( result.First )
        {
            Console.WriteLine( result.Second );
        }
    }
}

PS辞書の匿名型から生じる問題を回避する別の(より簡単な)方法があります。それは、varキーワードを使用して、コンパイラーに型を「推測」させることです。そのバージョンは次のとおりです。

var foundItem = peopleDict.FirstOrDefault().Value;
if( peopleDict.TryGetValue( "Smith", out foundItem ) )
{
   // use foundItem...
}
于 2010-06-10T19:31:21.203 に答える
18

タプルは便利な場合がありますが、後で苦痛になる場合もあります。Tuple<int,string,string,int>後でそれらの値が何であるかをどのように知るかを返すメソッドがある場合。彼らだったID, FirstName, LastName, Ageのか、彼らだったのかUnitNumber, Street, City, ZipCode

于 2010-06-10T18:48:29.450 に答える
9

タプルは、C#プログラマーの観点からは、CLRへのかなり圧倒的な追加です。長さが異なるアイテムのコレクションがある場合は、コンパイル時に一意の静的な名前を付ける必要はありません。

ただし、一定の長さのコレクションがある場合、これは、コレクション内の固定された場所がそれぞれ特定の事前定義された意味を持つことを意味します。また、その場合は、、などの重要性を覚えておくよりも、適切な静的な名前を付ける方が常に適切ですItem1Item2

C#の匿名クラスは、タプルの最も一般的な私的使用に対する優れたソリューションをすでに提供しており、アイテムに意味のある名前を付けるため、その意味で実際に優れています。唯一の問題は、名前付きメソッドからリークできないことです。C#でタプルを特別にサポートするよりも、制限が解除されることを望んでいます(おそらくプライベートメソッドの場合のみ)。

private var GetDesserts()
{
    return _icecreams.Select(
        i => new { icecream = i, topping = new Topping(i) }
    );
}

public void Eat()
{
    foreach (var dessert in GetDesserts())
    {
        dessert.icecream.AddTopping(dessert.topping);
        dessert.Eat();
    }
}
于 2010-06-11T15:08:48.163 に答える
8

キーワードと同様にvar、これは利便性を目的としていますが、悪用されやすいです。

Tuple私の最も謙虚な意見では、リターンクラスとして公開しないでください。サービスまたはコンポーネントのデータ構造で必要な場合は、プライベートに使用しますが、パブリックメソッドから整形式の既知のクラスを返します。

// one possible use of tuple within a private context. would never
// return an opaque non-descript instance as a result, but useful
// when scope is known [ie private] and implementation intimacy is
// expected
public class WorkflowHost
{
    // a map of uri's to a workflow service definition 
    // and workflow service instance. By convention, first
    // element of tuple is definition, second element is
    // instance
    private Dictionary<Uri, Tuple<WorkflowService, WorkflowServiceHost>> _map = 
        new Dictionary<Uri, Tuple<WorkflowService, WorkflowServiceHost>> ();
}
于 2010-06-10T18:49:23.943 に答える
5

のようなクラスを使用するResultType方が明確です。クラス内のフィールドに意味のある名前を付けることができます(タプルを使用すると、それらはandと呼ばれますItem1Item2。2つのフィールドのタイプが同じである場合、これはさらに重要です。名前はそれらを明確に区別します。

于 2010-06-10T18:47:50.640 に答える
5

タプルをdecorate-sort-undecorateパターンで使用するのはどうですか?(Perlの人々のためのシュワルツ変換)。確かに、これは不自然な例ですが、タプルはこの種のことを処理するための良い方法のようです。

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] files = Directory.GetFiles("C:\\Windows")
                    .Select(x => new Tuple<string, string>(x, FirstLine(x)))
                    .OrderBy(x => x.Item2)
                    .Select(x => x.Item1).ToArray();
        }
        static string FirstLine(string path)
        {
            using (TextReader tr = new StreamReader(
                        File.Open(path, FileMode.Open)))
            {
                return tr.ReadLine();
            }
        }
    }
}

これで、2つの要素のObject []を使用することも、この特定の例では2つの要素の文字列[]を使用することもできます。重要なのは、内部で使用され、非常に読みやすいタプルの2番目の要素として何でも使用できたということです。

于 2010-06-11T14:42:15.020 に答える
4

IMOこれらの「タプル」は、基本的に、名前のないメンバーを持つstructすべてのパブリックアクセス匿名タイプです。

タプルを使用する唯一の場所は、非常に限られた範囲で、いくつかのデータをすばやくまとめる必要がある場合です。 データのセマンティクスは明白である必要があるため、コードを読むのは難しくありません。したがって、(row、col)にタプル(int、 )を使用するのは妥当なようです。intしかし、名前の付いたメンバーを使用するよりも利点を見つけるのは難しいですstruct(したがって、間違いがなく、行/列が誤って交換されることはありません)

発信者にデータを送り返す場合、または発信者からのデータを受け入れる場合は、実際にはstruct名前付きメンバーでを使用する必要があります。

簡単な例を見てみましょう。

struct Color{ float r,g,b,a ; }
public void setColor( Color color )
{
}

タプルバージョン

public void setColor( Tuple<float,float,float,float> color )
{
  // why?
}

名前付きメンバーを持つ構造体の代わりにタプルを使用することに利点はありません。名前のないメンバーを使用することは、コードの読みやすさと理解しやすさのために一歩後退します。

タプルはstruct、実際の名前付きメンバーでを作成することを避けるための怠惰な方法として私を襲います。タプルの乱用は、あなた/またはあなたのコードに遭遇した他の誰かが名前付きメンバーを必要とするだろうと本当に感じているところで、私がこれを見たことがあれば、ABadThing™です。

于 2013-05-10T21:37:19.613 に答える
4

私を判断しないでください。私は専門家ではありませんが、C#7.xの新しいタプルを使用すると、次のようなものを返すことができます。

return (string Name1, int Name2)

少なくとも今、あなたはそれに名前を付けることができ、開発者はいくつかの情報を見るかもしれません。

于 2018-03-09T19:00:47.787 に答える
2

もちろん状況によります!あなたが言ったように、タプルはあなたがローカル消費のためにいくつかのアイテムを一緒にグループ化したいときにあなたのコードと時間を節約することができます。また、それらを使用して、具象クラスを渡す場合よりも一般的な処理アルゴリズムを作成することもできます。KeyValuePairまたはDataRowを超えて、あるメソッドから別のメソッドに日付をすばやく渡すことができたらと思った回数を思い出せません。

一方、それをやり過ぎて、タプルに何が含まれているのかを推測することしかできないタプルを回す可能性は十分にあります。クラス間でタプルを使用する場合は、1つの具象クラスを作成する方がよいでしょう。

もちろん、適度に使用すると、タプルはより簡潔で読みやすいコードにつながる可能性があります。タプルが他の言語でどのように使用されるかの例については、C ++、STL、およびBoostを参照できますが、最終的には、.NET環境に最適な方法を見つけるために実験する必要があります。

于 2010-06-10T18:49:21.490 に答える
1

タプルは.NET4の役に立たないフレームワーク機能です。C#4.0では素晴らしい機会を逃したと思います。名前付きメンバーを持つタプルがあればよかったので、Value1Value2などの代わりに名前でタプルのさまざまなフィールドにアクセスできます...

言語(構文)の変更が必要でしたが、非常に便利でした。

于 2010-06-10T20:12:38.737 に答える
1

値が何を表すかが示されていないため、個人的にはタプルをリターンタイプとして使用することはありません。タプルは、オブジェクトとは異なり、値型であり、したがって平等を理解するため、いくつかの貴重な用途があります。このため、マルチパートキーが必要な場合は辞書キーとして、複数の変数でグループ化する必要があり、ネストされたグループ化が必要ない場合はGroupBy句のキーとして使用します(ネストされたグループ化が必要な場合)。極端な冗長性の問題を克服するために、ヘルパーメソッドを使用してそれらを作成できます。(Item1、Item2などを介して)メンバーに頻繁にアクセスする場合は、構造体や匿名クラスなどの別の構造体を使用する必要があることに注意してください。

于 2014-10-09T16:11:47.387 に答える
0

さまざまなシナリオで、Tupleと新しいの両方のタプルを使用し、次の結論に達しました。を使用しないでくださいValueTuple

毎回、次の問題が発生しました。

  • 強力な名前がないため、コードが読み取れなくなりました。
  • 基本クラスDTOや子クラスDTOなどのクラス階層機能を使用できません。
  • それらが複数の場所で使用されている場合、クリーンなクラス名ではなく、これらの醜い定義をコピーして貼り付けることになります。

私の意見では、タプルはC#の機能ではなく、不利益です。

私はやや似ていますが、とに対する批判はそれほど厳しくありませFunc<>Action<>。これらは多くの状況、特に単純なものActionFunc<type>バリアントで役立ちますが、それを超えると、デリゲートタイプを作成する方がエレガントで、読みやすく、保守しやすく、ref/outパラメーターなどの機能が増えることがわかりました。

于 2018-06-01T00:39:15.643 に答える