.NETは「名前付き匿名」タイプの恩恵を受けるでしょうか?
-
19-08-2019 - |
質問
このことを考慮:
var me = new { FirstName = "John", LastName = "Smith" };
これを行うことができるので、これは問題ありません:
Console.WriteLine("{0} {1}", me.FirstName, me.LastName);
しかし、これはできません:
public T GetMe()
{
return new { FirstName = "John", LastName = "Smith" };
}
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クエリの結果を返すことができます。
この比較的複雑なLINQクエリを考慮してください:
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)
};
次のようなクラスを定義する代わりに:
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>();
return from number in list
select new public class MyNumbers
{
Number = number,
Square = number*number,
Absolute = Math.Abs(number),
Range = Enumerable.Range(0, number)
};
解決
実際、メソッドから匿名タイプを取り戻すためにできる「ハック」があります。このことを考慮:
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は、匿名タイプと同じタイプのオブジェクトになります。
他のヒント
必要な言語機能は次のとおりです。
public var GetMe()
{
return new { FirstName = "John", LastName = "Smith" };
}
あれは、 var
メソッドリターンタイプとして有効であり、コンパイラは返されるものから実際のタイプを推測します。その後、コールサイトでこれを行う必要があります。
var me = GetMe();
同じタイプのメンバーを持つ2つの匿名タイプは同じタイプであるため、同じパターンを返す他の関数を書いた場合、それらは同じタイプになります。 bがAのメンバーのサブセットを持っている任意のタイプAおよびBの場合、Aはbとadassmentに互換性があります(bはaの基本クラスのようなものです)。あなたが書いた場合:
public var GetMeFrom(var names)
{
return new { FirstName = names["First"], LastName = names["Last"] };
}
コンパイラは、これを2つのタイプパラメーターを持つ一般的な方法として効果的に定義します。 T1
名前のタイプであること T2
インデクサーによって返されるタイプであること T1
文字列を受け入れます。 T1
文字列を受け入れるインデクサーが必要になるように制約されます。そして、コールサイトでは、文字列を受け入れて返されたインデクサーがいるものをすべて渡すだけです あなたが好きな任意のタイプ, 、そしてそれはのタイプを決定します FirstName
と LastName
によって返されたタイプ GetMeFrom
.
したがって、タイプの推論はあなたのためにこれをすべて把握し、コードから発見できるタイプの制約を自動的にキャプチャします。
根本的な問題は匿名のタイプとは何の関係もありませんが、クラスを宣言することはあまりにも冗長です。
オプション1:
このようなクラスを宣言できる場合:
public class MyClass
{ properties={ int Number, int Square, int Absolute, IEnumerable<int> Range } }
または、他の同様の迅速な方法(タプルの例のように)では、コードを保存するためだけに匿名のタイプでハッキーなことをする必要性を感じないでしょう。
「コンパイラAs A Service」がC#5に到着すると、彼らがそれを統合するのに良い仕事をすることを願っています。メタプログラムを使用して、これらの種類の問題をきれいに解決できるようになります。 1958年のようなパーティー!
オプション2:
または、C#4では、匿名のタイプをASとして渡すだけです dynamic
そして、すべてのキャスティングを避けてください。もちろん、これにより、変数などの名前を変更すると、ランタイムエラーが発生します。
オプション3:
C#がC ++と同じ方法でジェネリックを実装する場合、匿名タイプをメソッドに渡すことができ、適切なメンバーがある限り、コンパイルするだけです。静的なタイプの安全性のすべての利点を得ることができます。入力するたびに where T : ISomething
C#では、彼らがこれをしなかったことに悩まされます!
あなたが説明しているのは、(匿名タイプと名付けられた)基本的に「タプルタイプ」です。
彼らは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で動作させるのは少し難しいです。なぜなら、公開署名で匿名のタイプに名前を付けることができたら、個別にコンパイルされたアセンブリでそれらを統一できる必要があるからです。タプルタイプを使用するアセンブリに「モジュールコンストラクター」を埋め込むことで実現できます。見る この郵便受け 例として。
そのアプローチの唯一の欠点は、タイプ生成のCLRの「怠zy」モデルを尊重しないことです。つまり、多くの異なるタプルタイプを使用するアセンブリは、荷重タイプがわずかに遅くなる可能性があります。より良いアプローチは、TupleタイプのサポートをCLRに直接追加することです。
しかし、CLRの変更とは別に、モジュールコンストラクターアプローチはこのようなことをする最良の方法だと思います。
私はこの機能が大好きです、私がこれを望んでいたことが何度もありました。
良い例は、XMLの処理です。それらを解析して、オブジェクトを取り戻しますが、その後、オブジェクトの具体的なバージョンを作成して発信者に送り返す必要があります。多くの場合、かなり変化するXMLを取得し、それを処理するために多くのクラスを作る必要があります。 linqtoxmlをvarとして使用してオブジェクトを構築するだけで、それを返すことができれば、それは素晴らしいことではないでしょうか?
これはタプルにとって素晴らしいコンパイラマジックだと思います:
タプルを作成する:
(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];
Properties FirstNameとLastNameを使用してインターフェイスを作成して使用できますか?