web-dev-qa-db-ja.com

使用する場合:Tuple vs Class C#7.0

Tuplesの前は、classとその変数を作成してから、このクラスからオブジェクトを作成し、そのオブジェクトを一部の関数の戻り値型にしました。

これで、タプルを使用して同じことができ、C#7.0では、タプルプロパティにわかりやすい名前を割り当てることができます(これまではitem1item2など)。

だから今、私はタプルをいつ使うべきなのか、そしてC#7.0でいつクラスを作成すべきなのか疑問に思っていますか?

52
Madonna Remon

この答えはここの人々の間で混乱を引き起こしているので、質問によると、ここでの「タプル」へのすべての言及は、C#7のValueTupleタイプと新しいTuple構文シュガー機能を指します。古いSystem.Tuple参照型を参照する方法。

だから今、私はタプルを使用する必要がありますか、C#7.0でクラスを作成する必要があるのでしょうか?

本当にあなたのコードに依存するので、あなただけが本当にその質問に答えることができます。

ただし、選択する際の指針となるガイドラインとルールがあります。

タプルは値であるため、参照ではなく値によってコピーされます。

ほとんどの場合、これは問題になりません。ただし、大きな構造体のタプルを渡す場合、これはパフォーマンスに影響を与える可能性があります。ただし、Ref Locals/Returnを使用して、これらのパフォーマンスの問題を回避できます。

さらに、これらは値であるため、コピーをリモートで変更しても元のコピーは変更されません。これは良いことですが、一部の人々を捕まえることができます。

タプル要素名は保持されません

要素に与えられた名前はコンパイラーによって使用され、(ほとんどの場合)実行時に利用できません。つまり、リフレクションを使用して名前を発見することはできません。動的にアクセスできず、カミソリビューで使用できません。

また、これはAPIの重要な考慮事項です。メソッドから返されるタプルは、コンパイル後の名前の検出に関するルールの例外です。コンパイラは、タプル名に関する情報を保持する属性をメソッドに追加します。つまり、あるアセンブリのパブリックメソッドから安全にタプルを返し、別のアセンブリの名前にアクセスできます。

タプルは軽量です

タプルは、冗長性が低く、宣言を「インライン化」できる(つまり使用時に宣言できる)ため、型よりも記述がはるかに簡単です。これは、たとえば、複数の値を返すメソッドを宣言するときにうまく機能します。

ただし、それらは使用時に宣言されるため、MethodAを呼び出すMethodBMethodCを呼び出し、それぞれがタプルを返す場合、タプルを毎回再定義する必要があります。ステージ。タプルのエイリアスを作成し、それを複数のメソッドで再利用する方法はありません( yet )。

常識を使う

Tupleの使用を検討する可能性のある状況については、単に「Tupleはコードをここで単純化します」という質問を自問してください。答えが「はい」の場合、それを使用します。そして、最終的には、タプルを使用するかカスタムクラスを使用するかについての主要な考慮事項です。

38
David Arno

一般的に、名前付きクラスは、システムの設計において重要な意味を持ちます。また、書くのがより冗長です。たとえば、MediaFileOpenerというクラスがあるとします。このクラスが何をするかを知っていることは、設計にとって重要です-メディアファイルで作業しています!

設計上の重要性がなく、必要なのは情報を移動するための軽量のデータ転送オブジェクト(DTO)だけである場合は、匿名型とタプルが使用されます。

ルールとして、クラスの目的を説明するドキュメントが必要な場合、またはクラスが提供する動作がある場合は、完全なクラスを使用します。必要なのが一時ストレージまたは何らかのグループ化だけである場合は、タプルを使用します。非同期メソッドから複数の値を返したい状況を考えてください。 Tupleはその問題を解決するように設計されています。

19
Gusdor

クラスを使用

オブジェクトがアプリケーション全体で広く使用されているエンティティであり、リレーショナルデータベース(SQL Server、MySQL、SQLite)、NoSQLデータベースまたはキャッシュ(Re​​dis、Azure DocumentDB)、または単純なテキストファイルまたはCSV。

そのため、永続的なものには独自のクラスが必要です。

タプルを使用

オブジェクトが短命で、アプリケーションにとって特別な意味がない場合。たとえば、座標のペアをすばやく返す必要がある場合は、次のようなものを用意することをお勧めします。

(double Latitude, double Longitude) getCoordinates()
{
    return (144.93525, -98.356346);
}

別のクラスを定義するより

class Coordinates
{
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}

タプルを使用すると、このような単純な操作のためにnewを使用してヒープにメモリを割り当てる必要がなくなります。

タプルが便利だと思う別の機会は、いくつかのオペランドで複数の数学演算を実行するときです

(double Result1, double Result2, double Result3) performCalculations(int operand1, int operand 2)

この場合、クラスを定義しても意味がありません。計算が何であっても、結果はクラスに属しません。したがって、代わりにout変数を使用することもできますが、タプルの方が表現力が高く、読みやすさが向上すると考えています。

8
dimlucas

まず、C#で 匿名型 が既にサポートされていることに言及したいと思います。参照タイプはどれですか。したがって、名前付きクラスを作成するための適切な代替手段がすでにありました。

名前付きクラスの利点の1つは、再利用(複数の場所で同じ型が必要な場合など)およびドキュメント化が簡単になることです。匿名型は匿名であるため、varを使用できる場合にのみ型変数を取得できます。これにより、匿名型が有用なコンテキストが制限されます(たとえば、フィールド型、戻り値型、またはパラメーター型として使用できません) )。

もちろん、 System.Tuple を使用して、匿名型の制限のいくつかを乗り越えることができます。これも参照型であり、明示的に使用できます。欠点は、メンバーのカスタム名が不足していることです。


C#7タプル( ValueTuple )は、匿名型と同様と見なすことができます。最初の違いは、値タイプであるということです。これは、これらのタプルがローカルスコープ内にあるか、スタックを移動している限り(パフォーマンスの制限により匿名型の一般的な使用方法である限り)、パフォーマンス上の利点があることを意味します。

2番目の違いは、新しい構文により、タプルを匿名型よりも多くの場所に表示できることです。ご存知のように、戻り値の型をValueTupleに定義する構文シュガーがあります(匿名型を使用する場合はobjectを返す必要があります)。

3番目の違いは、ValueTupleがすぐに分解できることです。引用するには C#7.0の新機能

タプルを消費する別の方法は、タプルを分解することです。分解宣言は、タプル(または他の値)をその部分に分割し、それらの部分を個別に新しい変数に割り当てるための構文です。

(string first, string middle, string last) = LookupName(id1); // deconstructing declaration
WriteLine($"found {first} {last}.");

Deconstructメソッド を追加することにより、カスタムタイプでも実行できます。


要約の場合:

  • ValueTupleにはC#7.0の構文糖衣があります。読みやすくするために考慮する必要があります。
  • ValueTupleは値型です。 classの使用とstructの使用の間のすべての長所と短所が適用されます。
  • ValueTupleを明示的に(構文糖の有無にかかわらず)使用して、名前付きメンバーを保持しながらSystem.Tupleの汎用性を持たせることができます。
  • ValueTupleは分解をサポートします。

それが構文糖であるということを考えると、ValueTupleを選択するためのより強力な引数は、structを選択するための同じ引数であると言えます。ほとんどがスタック上にある小さな不変型に理想的です(したがって、多くのボクシングとアンボクシングはありません)。

ValueTupleを完全な構造体と比較し、構文糖を考慮して、 explicit layout が必要な場合、またはメソッドを追加する必要がない限り、デフォルトでValueTupleを使用することをお勧めします。

また、構文糖は必ずしも読みやすさを改善するわけではないことも言いたいと思います。主な理由は、タイプに名前を付けておらず、タイプの名前がコードに意味を与えていることです。それに加えて、理解を容易にするドキュメントをstructまたはclass宣言に追加できます。

全体として、ValueTupleが本当に輝く状況は、メソッドから複数の値を返すことです。この場合、新しいoutパラメーターを作成する必要がなくなります。また、使用されるValueTupleのドキュメントは、メソッドのドキュメントに含まれます。 ValueTupleを使用して何か他のことを行う必要がある場合(たとえば、拡張メソッドを定義する場合)、代わりに名前付き型の作成を検討することをお勧めします。

3
Theraot

メソッドが複数の値を返す場合など、タプルは複数の値を表すことを目的としています。 C#7のTupleサポートは、System.ValueTuple<...>インスタンスを使用してその値のセットを表します。これらの値の名前は、それらが使用されているコンテキストでのみ有効であり、強制されません。

クラスは、複数の属性を持つ単一の値を表すことを目的としています。

2
Paulo Morgado

一般に、オブジェクトを別の場所で使用する場合、またはドメイン内の実際のオブジェクトまたは概念を表す場合は、クラスが必要です。おそらく、タプルではなく、車または車の店を表すクラスを作成します。

一方、場合によっては、メソッドからいくつかのオブジェクトを返したいだけです。それらは特別なものを表していないかもしれませんが、その特定のメソッドで一緒に返す必要があるだけです。時々、それらがあなたのドメインからの概念を表現していても(たとえば、Saleオブジェクトとして表現できる(Car, Store)を返しているとしても)、実際にはどこでもそれらを使用しないでしょう-データを移動しているだけです。これらの場合、タプルを使用しても問題ありません。

さて、具体的にC#について言えば、もう1つ知っておくべきことがあります。 C#7のTuple型は実際にはValueTupleであり、これは構造体です。参照型であるクラスとは異なり、構造体は値型です。詳細については msdn をご覧ください。最も重要なことは、それらが多くのコピーを伴う可能性があることを知っているので、注意してください。

2
andre_ss6

タプルは、カスタムクラスを作成せずに複数の値(異なる型でも可)を1つのオブジェクトに結合する場合に最適なオプションです。この場合、Tupleは高速で完璧なオプションです。

1
Qasim Bataineh

これはよく聞かれる質問になると思います。現在、クラスに対して新しい値のタプルを使用する場合の「ベストプラクティス」はありません。

しかし、読む価値はあります 以前のバージョンのタプルとクラスについての以前の会話で出てきたもの

私の意見では、Tupleの値は最小限で使用し、最大3つの値を使用する必要があります。これは、「クラスを必要とせずにいくつかの値を返す」と「恐ろしい価値の混乱」のバランスが取れていると思います。返す値が3つ以上ある場合は、クラスを作成します。

また、消費者が使用しなければならない公開APIから戻るためにTupleを使用することもありません。繰り返しますが、クラスを使用します。

私が使用したいくつかの実際のコードは次のとおりです。

public async Task<(double temperature, double humidity, string description)> DownloadTodaysForecast()

より複雑なデータを返したいとすぐに、クラスを作成します。

0
user9993

パブリックメソッドの戻り値型としてタプルを使用することは避けます。そのような場合、クラスまたは構造体を定義したいと思います。

0