2009-03-17 12 views
16

はこの考えてみましょう:.NETは「名前付き匿名」タイプのメリットはありますか?

var me = new { FirstName = "John", LastName = "Smith" }; 

私たちは、この行うことができますので、これは素晴らしいです:私たちは知らないので

public T GetMe() 
{ 
    return new { FirstName = "John", LastName = "Smith" }; 
} 

:我々はこれを行うことはできませんしかし

Console.WriteLine("{0} {1}", me.FirstName, me.LastName); 

をタイプT.

public object GetMe() 
{ 
    return new { FirstName = "John", LastName = "Smith" }; 
} 

が、その後、我々はそれらにアクセスするためにリフレクションを使用して、オブジェクトのプロパティを検査する必要があるだろう:

var p = new Prog(); 
object o = p.GetMe(); 
Type t = o.GetType(); 
foreach (var prop in t.GetProperties()) 
{ 
    Console.WriteLine(prop.Name + ": " + prop.GetValue(o, null)); 
} 

しかし私たちはそれを定義するよう、我々は匿名型に名前を付けることができればどうですか?もちろん、それはもはや匿名ではありませんが、通常のクラス定義よりも簡潔で保守的です。

public Person GetMe() 
{ 
    return new public class Person { FirstName = "John", LastName = "Smith" }; 
} 

その後、明示的にクラスを定義することなく、方法から複雑なLINQクエリの結果を返すことも可能であるという利点:

が、この考えてみましょう。我々だけではなく、これを行うことができますメソッドからクエリ変数を返すために

public class MyNumbers 
{ 
    public int Number { get; set; } 
    public int Square { get; set; } 
    public int Absolute { get; set; } 
    public IEnumerable<int> Range { get; set; } 
} 

:代わりにそのようなクラスを定義するので

List<int> list = new List<int>(); 
var query = from number in list 
      select 
       new 
        { 
         Number = number, 
         Square = number*number, 
         Absolute = Math.Abs(number), 
         Range = Enumerable.Range(0, number) 
        }; 

は、この比較的複雑なLINQクエリを考えてみます。

List<int> list = new List<int>(); 
return from number in list 
      select new public class MyNumbers 
        { 
         Number = number, 
         Square = number*number, 
         Absolute = Math.Abs(number), 
         Range = Enumerable.Range(0, number) 
        }; 
+3

これは、他のコンテキストでMyNumbersを使い始めたときに非常に醜いものになります。メソッドの境界を越えた型を使用している場合は、その型がインラインで宣言されていないと、より保守性と読みやすさが向上します。 –

+0

+1 to spoon16さんのコメント。 – strager

+0

私はこれらの問題を緩和するためにコンパイラにいくつかの賢いものを置くことができると思います。 –

答えて

13

実際には、匿名のタイプを取得するための「ハック」がありますメソッドからのck。このことを考えてみましょう:あなたは今、これを行うことができます

public object MyMethod() 
    { 
     var myNewObject = new 
     { 
      stringProperty = "Hello, World!", 
      intProperty = 1337, 
      boolProperty = false 
     }; 

     return myNewObject; 
    } 

    public T Cast<T>(object obj, T type) 
    { 
     return (T)obj; 
    } 

var obj = MyMethod(); 
var myNewObj = Cast(obj, new { stringProperty = "", intProperty = 0, boolProperty = false }); 

myNewObjは今、匿名型と同じ型のオブジェクトになります。

+1

私は以前これを見ていませんでした。これはかなりクールなトリックです...私はそれが通常これを使用することをお勧め良いとは思わないけれど、 –

+0

素敵なトリックですが、anonタイプはまだ内部ですので、アセンブリ全体で使用することはできません。 –

+2

さて、これはちょうどハックなので、私はこれをプロダクションコードで使用することはできません。あなたはジョナサンに尋ねるべきです、あなたが別のアセンブリからこれを参照しようとしているなら、本当に匿名のタイプがあなたが望むものだと思いますか? – BFree

4

あなたが記述している(匿名型と呼ばれる)のは基本的に "タプル型"です。

私は彼らがC#に素敵な存在になると思います。

私はC#の場合、このような機能を設計していた場合、私はこのような構文を使用して、それを公開します:あなたが行うことができるように

tuple<int x, int y> 

public tuple<int x, int y> GetStuff() 
{ 
} 

私は、その後の定義を変更します次のようになります。

new { x = 2, y = 2} 

tuple<int x, int y>があります。匿名のタイプ。

パブリックシグネチャで匿名型の名前を付けることができれば、別々にコンパイルされたアセンブリ間で匿名型を統一できる必要があるため、現在のCLRとの連携は少し難解です。これは、タプル型を使用するアセンブリ内に「モジュールコンストラクタ」を埋め込むことで実現できます。例については、this postを参照してください。

このアプローチの唯一の欠点は、タイプ生成のCLRの「怠惰な」モデルを尊重しないことです。つまり、多くの異なるタプル型を使用するアセンブリでは、負荷の種類がやや遅くなる可能性があります。より良いアプローチは、タプル型のサポートをCLRに直接追加することです。

しかし、CLRを変更することを除けば、私はモジュールコンストラクタのアプローチがこのようなことを行う最善の方法だと考えています。

+0

.Net 4では、タプルのBCLサポートが追加されています。例:http://weblogs.asp.net/podwysocki/archive/2008/11/16/functional-net-4-0-tuples-and-zip.aspx –

+0

BCLのタプルタイプはフィールド名を扱いません匿名型のような型識別の一部として、それは全く同じことではありません。 –

+0

名前付きフィールドを持つタプルをサポートしていないと思うのは悪いことです。 フィールドで「item1」や「item2」などの名前を使用してプログラムする頻度は?おそらく、私は推測していないでしょう –

-1

プロパティFirstNameとLastNameのインターフェイスを作成して使用できますか?

5

IMHO根の問題は匿名型とは関係ありませんが、クラスの宣言はあまりにも冗長です。

オプション1:

あなたは、このようなクラス宣言する可能性がある場合:

public class MyClass 
{ properties={ int Number, int Square, int Absolute, IEnumerable<int> Range } } 

または(タプルの例のように)いくつかの他の同様の簡単な方法を、あなたがする必要性を感じないだろういくつかのコードを保存するために、匿名型を使ってハッピーなことをしてください。

「サービスとしてのコンパイラ」がC#5に到着したらうまくいけば、それらを統合することができれば、メタプログラミングを使用してこれらの問題をきれいに解決できます。 1958年のような党!

オプション2:

また、C#4で、あなただけのdynamicとして周囲に匿名型を渡すと、すべてのキャストを避けることができます。

C#はC++と同じようにジェネリックを実装した場合、あなたに匿名型を渡すことができます。もちろん、これは

オプション3など、あなたが変数の名前を変更する場合にエラーをランタイムためにあなたを開きます方法、そしてそれが正しいメンバーを持っていれば、それはちょうどコンパイルされます。静的なタイプの安全性の利点をすべて得ることができますが、欠点はありません。 C#でwhere T : ISomethingと入力するたびに、私は彼らがこれをしなかったことに腹を立てます!ある

public var GetMe() 
{ 
    return new { FirstName = "John", LastName = "Smith" }; 
} 

は、varは、メソッドの戻り値の型として有効だろう、とコンパイラが返されているものから実際の型を推論します:

+0

多くの場合、束の変数をダクトテープと結びつけると便利です。私の好みは、その目的のためにパブリックフィールドの値型を使用することです。単純な汎用の 'ExposedFieldHolder 'または 'ImmutableHolder 'は、可変または不変参照セマンティクスが必要な場合に使用します。いくつかの点で、.NETには、オブジェクトのように動作するように設計されたものと、変数をダクトテープと一緒にバインドするように設計されたものの2種類があります。 'StructTuple {public T1 v1;}のようなタイプの場合、 public T2 v2;} 'はボックス化されたときに本当に不変であると宣言することができます... – supercat

+0

...' StructTuple 'は' StructTuple 'のサブタイプと見なすことができます。それは、すべてのboxed構造体が常に変更可能であるという事実は、そうでなければ意味をなさない場合でも、構造体型共分散を不可能にします。 – supercat

9

必要な言語機能があります。その後、コールサイトでこれを実行する必要があります:

var me = GetMe(); 

あなたは同じパターンを返す他の関数を書いた場合、彼らが持っているだろうので、同じタイプのメンバーを持つ2つの匿名型は、同じ型になります同じタイプ。 BがAのメンバーのサブセットを持つ任意のタイプAとBに対して、AはBと割り当て互換性があります(BはAの基本クラスに似ています)。あなたが書いた場合:

public var GetMeFrom(var names) 
{ 
    return new { FirstName = names["First"], LastName = names["Last"] }; 
} 

をコンパイラが効果的T1は、名前のタイプであることとT2は、文字列を受け取りT1にインデクサによって返されたタイプであること、2種類のパラメータを持つ一般的な方法としてこれを定義します。 T1は、文字列を受け入れるインデクサーを持つ必要があるように制約されます。また、呼び出し側では、文字列を受け入れたのようなタイプの文字列を受け取り、GetMeFromによって返されるタイプのFirstNameLastNameが返されます。

したがって、タイプの推論はコードから検出可能なタイプの制約を自動的にキャプチャして、これをすべて把握します。

+0

まさに、私はいつもこれが欲しかった!私はDにこの型の戻り型推論があると思います。 – nawfal

0

私は、これはタプルのための素敵なコンパイラの魔法だと思う:

はタプルを作成:

に相当
(int, string, Person) tuple = (8, "hello", new Person()); 

:機能で

Tuple<int,string,Person> tuple = new Tuple<int,string,Person>(8 ,"hello", new Person()); 

public (int, string, Person) GetTuple(){ 
    return ... 
} 

値の取得:

int number = tuple[1]; 
string text = tuple[2]; 
Person person = tuple[3]; 
+0

興味深い提案。それをパラメータとして渡すことはきれいではないpublic void Foo((int、string、Person)tuple){} –

+0

なぜlhsですか?単に 'var tuple =(8、" hello "、新しいPerson())'?良くなります。 – nawfal

+0

古い投稿ですが、どのようにこれは実際には既存のより良いです var tuple = Tuple.Create(8、 "hello"、新しいPerson()); ? – tolanj

1

私はこの機能が大好きです。私はこれが欲しかったことが何度もありました。

良い例は、XMLの処理です。それらを解析してオブジェクトを取得しますが、呼び出し側に返すオブジェクトの具体的なバージョンを作成する必要があります。たいていの場合、かなり変化するXMLを取得し、それを処理するために多くのクラスを作成する必要があります。 LinqToXmlをvarとして使用してオブジェクトを構築してから、それを返すだけでいいのではないでしょうか?

関連する問題