回答:
強く型付けされた匿名型のリストが必要な場合は、リストも匿名型にする必要があります。これを行う最も簡単な方法は、配列などのシーケンスをリストに投影することです。たとえば、
var nodes = (new[] { new { Checked = false, /* etc */ } }).ToList();
その後、次のようにアクセスできます。
nodes.Any(n => n.Checked);
コンパイラーの動作方法により、リストを作成した後は匿名型も同じ構造を持ち、同じ型であるため、以下も機能するはずです。これを確認するために渡すコンパイラはありません。
nodes.Add(new { Checked = false, /* etc */ });
オブジェクトをtypeとして保存する場合は、object
リフレクションを使用する必要があります。これは、匿名またはその他のオブジェクトタイプに当てはまります。オブジェクトoでは、そのタイプを取得できます。
Type t = o.GetType();
次に、そこからプロパティを検索します。
PropertyInfo p = t.GetProperty("Foo");
次に、そこから値を取得できます。
object v = p.GetValue(o, null);
この回答は、C#4の更新にはまだ遅れています。
dynamic d = o;
object v = d.Foo;
そして今、C#6のもう1つの代替案:
object v = o?.GetType().GetProperty("Foo")?.GetValue(o, null);
を使用?.
すると、結果v
がnull
3つの異なる状況になることに注意してください!
o
ですnull
ので、何のオブジェクトがすべてではありませんo
はありnull
ませんが、プロパティがありませんFoo
o
プロパティFoo
がありますが、実際の値はnull
です。したがって、これは前の例と同等ではありませんが、3つすべてのケースを同じように処理したい場合に意味があります。
object v = d.Foo
)、GetValue(o, null)
存在しない場合はnullになります。
GetProperty
戻りますnull
、とGetValue
することを渡された場合にスローされますnull
ので、全体的な効果は例外で、。C#4.0バージョンには、より説明的な例外があります。
リフレクションを使用して、匿名型のプロパティを反復処理できます。"Checked"プロパティがあるかどうかを確認し、ある場合はその値を取得します。
このブログ投稿を参照してください: http //blogs.msdn.com/wriju/archive/2007/10/26/c-3-0-anonymous-type-and-net-reflection-hand-in-hand.aspx
したがって、次のようなもの:
foreach(object o in nodes)
{
Type t = o.GetType();
PropertyInfo[] pi = t.GetProperties();
foreach (PropertyInfo p in pi)
{
if (p.Name=="Checked" && !(bool)p.GetValue(o))
Console.WriteLine("awesome!");
}
}
受け入れられた回答は、リストの宣言方法を正しく説明しており、ほとんどのシナリオで強く推奨されています。
しかし、私は別のシナリオに遭遇しました。これには、尋ねられた質問も含まれています。MVCのようViewData["htmlAttributes"]
に既存のオブジェクトリストを使用する必要がある場合はどうなりますか?プロパティにアクセスするにはどうすればよいですか(通常、new { @style="width: 100px", ... }
ます)?
このわずかに異なるシナリオについて、私が見つけたものを皆さんと共有したいと思います。以下のソリューションでは、次の宣言を想定していますnodes
:
List<object> nodes = new List<object>();
nodes.Add(
new
{
Checked = false,
depth = 1,
id = "div_1"
});
ではC#4.0以降のバージョンは、単に動的な書き込みにキャストすることができます:
if (nodes.Any(n => ((dynamic)n).Checked == false))
Console.WriteLine("found not checked element!");
注:これはレイトバインディングを使用しています。つまり、オブジェクトにChecked
プロパティがない場合のみ実行時に認識しRuntimeBinderException
、この場合はをスローします。したがって、存在しないChecked2
プロパティを使用しようとすると、次のメッセージが表示され ます。ランタイム:"'<>f__AnonymousType0<bool,int,string>' does not contain a definition for 'Checked2'"
。
リフレクションを使用したソリューションは、古いバージョンと新しいバージョンのC#コンパイラーの両方で機能します。古いC#バージョンの場合は、この回答の最後にあるヒントを参考にしてください。
バックグラウンド
出発点として、私はここで良い答えを見つけました。アイデアは、リフレクションを使用して匿名データ型を辞書に変換することです。辞書を使用すると、プロパティの名前がキーとして保存されるため、プロパティに簡単にアクセスできます(これらのプロパティにはmyDict["myProperty"]
)。
上記のリンクのコードに触発され、私が提供する拡張クラスを作成しGetProp
、UnanonymizeProperties
かつUnanonymizeListItems
拡張メソッド、匿名のプロパティへの簡素化のアクセスなど。このクラスを使用すると、次のようにクエリを実行できます。
if (nodes.UnanonymizeListItems().Any(n => (bool)n["Checked"] == false))
{
Console.WriteLine("found not checked element!");
}
または、式nodes.UnanonymizeListItems(x => (bool)x["Checked"] == false).Any()
をif
条件として使用することもできます。これは、暗黙的にフィルタリングしてから、返された要素があるかどうかを確認します。
"Checked"プロパティを含む最初のオブジェクトを取得し、そのプロパティ "depth"を返すには、次を使用できます。
var depth = nodes.UnanonymizeListItems()
?.FirstOrDefault(n => n.Contains("Checked")).GetProp("depth");
以下: nodes.UnanonymizeListItems()?.FirstOrDefault(n => n.Contains("Checked"))?["depth"];
注:すべてのプロパティを必ずしも含まないオブジェクトのリストがあり(たとえば、「Checked」プロパティを含まないオブジェクトもある)、「Checked」値に基づいてクエリを構築したい場合は、これを行う:
if (nodes.UnanonymizeListItems(x => { var y = ((bool?)x.GetProp("Checked", true));
return y.HasValue && y.Value == false;}).Any())
{
Console.WriteLine("found not checked element!");
}
これにより、KeyNotFoundException
「Checked」プロパティが存在しない場合にa が発生するのを防ぎます。
以下のクラスには、次の拡張メソッドが含まれています。
UnanonymizeProperties
:オブジェクトに含まれているプロパティの匿名化に使用されます。このメソッドはリフレクションを使用します。オブジェクトをプロパティとその値を含む辞書に変換します。UnanonymizeListItems
:オブジェクトのリストを、プロパティを含む辞書のリストに変換するために使用されます。オプションで、事前にフィルタリングするラムダ式を含めることができます。GetProp
:指定されたプロパティ名に一致する単一の値を返すために使用されます。存在しないプロパティをKeyNotFoundException(false)ではなくnull値(true)として扱うことができます。上記の例で必要なのは、以下の拡張クラスを追加することだけです。
public static class AnonymousTypeExtensions
{
// makes properties of object accessible
public static IDictionary UnanonymizeProperties(this object obj)
{
Type type = obj?.GetType();
var properties = type?.GetProperties()
?.Select(n => n.Name)
?.ToDictionary(k => k, k => type.GetProperty(k).GetValue(obj, null));
return properties;
}
// converts object list into list of properties that meet the filterCriteria
public static List<IDictionary> UnanonymizeListItems(this List<object> objectList,
Func<IDictionary<string, object>, bool> filterCriteria=default)
{
var accessibleList = new List<IDictionary>();
foreach (object obj in objectList)
{
var props = obj.UnanonymizeProperties();
if (filterCriteria == default
|| filterCriteria((IDictionary<string, object>)props) == true)
{ accessibleList.Add(props); }
}
return accessibleList;
}
// returns specific property, i.e. obj.GetProp(propertyName)
// requires prior usage of AccessListItems and selection of one element, because
// object needs to be a IDictionary<string, object>
public static object GetProp(this object obj, string propertyName,
bool treatNotFoundAsNull = false)
{
try
{
return ((System.Collections.Generic.IDictionary<string, object>)obj)
?[propertyName];
}
catch (KeyNotFoundException)
{
if (treatNotFoundAsNull) return default(object); else throw;
}
}
}
ヒント:上記のコードは、使用している、NULLで条件付きの C#バージョン6.0以降で利用可能演算子を、 -もしあなたとしている作業古いC#コンパイラ(例えばC#3.0)を、単純に置き換える?.
ことにより、.
および?[
により[
例えば、どこでも
var depth = nodes.UnanonymizeListItems()
.FirstOrDefault(n => n.Contains("Checked"))["depth"];
古いC#コンパイラを使用する必要がない場合は、そのままにしておきます。null条件を使用すると、nullの処理がはるかに簡単になります。
注:ダイナミックを使用する他のソリューションと同様に、このソリューションも遅延バインディングを使用していますが、この場合、例外は発生しません-存在しないプロパティを参照している場合、要素が見つからないだけです。ヌル条件演算子を保持するため。
一部のアプリケーションに役立つ可能性があるのは、プロパティがソリューション2の文字列を介して参照されるため、パラメーター化できることです。
最近、私は.NET 3.5内で同じ問題を抱えていました(動的は利用できません)。これが私が解決した方法です:
// pass anonymous object as argument
var args = new { Title = "Find", Type = typeof(FindCondition) };
using (frmFind f = new frmFind(args))
{
...
...
}
Stackoverflowのどこかから適応:
// Use a custom cast extension
public static T CastTo<T>(this Object x, T targetType)
{
return (T)x;
}
次に、キャストを介してオブジェクトを取得します。
public partial class frmFind: Form
{
public frmFind(object arguments)
{
InitializeComponent();
var args = arguments.CastTo(new { Title = "", Type = typeof(Nullable) });
this.Text = args.Title;
...
}
...
}