C#で匿名型のプロパティにアクセスする方法


125

私はこれを持っています:

List<object> nodes = new List<object>(); 

nodes.Add(
new {
    Checked     = false,
    depth       = 1,
    id          = "div_" + d.Id
});

...そして、匿名オブジェクトの "Checked"プロパティを取得できるかどうか疑問に思っています。これが可能かどうかはわかりません。これをやってみました:

if (nodes.Any(n => n["Checked"] == false)) ...しかし、機能しません。

ありがとう

回答:


63

強く型付けされた匿名型のリストが必要な場合は、リストも匿名型にする必要があります。これを行う最も簡単な方法は、配列などのシーケンスをリストに投影することです。たとえば、

var nodes = (new[] { new { Checked = false, /* etc */ } }).ToList();

その後、次のようにアクセスできます。

nodes.Any(n => n.Checked);

コンパイラーの動作方法により、リストを作成した後は匿名型も同じ構造を持ち、同じ型であるため、以下も機能するはずです。これを確認するために渡すコンパイラはありません。

nodes.Add(new { Checked = false, /* etc */ });

263

オブジェクトを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);

を使用?.すると、結果vnull3つの異なる状況になることに注意してください!

  1. oですnullので、何のオブジェクトがすべてではありません
  2. oはありnullませんが、プロパティがありませんFoo
  3. oプロパティFooがありますが、実際の値はnullです。

したがって、これは前の例と同等ではありませんが、3つすべてのケースを同じように処理したい場合に意味があります。


4
.NET 4.0のために前に今までのダイナミックな、素敵なアップデートを使用したことがない
アラン・

C#4ソリューションでは、プロパティが存在しない場合はランタイム例外が発生し(object v = d.Foo)、GetValue(o, null)存在しない場合はnullになります。
YaakovHatam

1
いいえ、GetProperty戻りますnull、とGetValueすることを渡された場合にスローされますnullので、全体的な効果は例外で、。C#4.0バージョンには、より説明的な例外があります。
Daniel Earwicker

4
ソースとは異なるアセンブリで動的を使用している場合は、[InternalsVisibleTo]を使用する必要があります
Sarath

2
@DanielEarwicker完了ありがとうございます。また、匿名型にも適用されます。匿名型に対して生成されたすべてのプロパティは内部的なものなので。
サラス2017年

13

リフレクションを使用して、匿名型のプロパティを反復処理できます。"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!");
    }
}

6
プロパティが1つだけ必要で、その名前が既にわかっている場合は、すべてのプロパティを調べても意味がありません。GetPropertyとGetValueを使用するだけです。また、System.out.printlnはJavaであり、C#ではありません...
Chris Charabaruk '10年

おっと、そうです、クリス!少し恥ずかしい...今修正されました。
glennkentwell、2011年

6

受け入れられた回答は、リストの宣言方法を正しく説明しており、ほとんどのシナリオで強く推奨されています。

しかし、私は別のシナリオに遭遇しました。これには、尋ねられた質問も含まれています。MVCのようViewData["htmlAttributes"]に既存のオブジェクトリストを使用する必要がある場合はどうなりますか?プロパティにアクセスするにはどうすればよいですか(通常、new { @style="width: 100px", ... }ます)?

このわずかに異なるシナリオについて、私が見つけたものを皆さんと共有したいと思います。以下のソリューションでは、次の宣言を想定していますnodes

List<object> nodes = new List<object>();

nodes.Add(
new
{
    Checked = false,
    depth = 1,
    id = "div_1" 
});

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'"

2.反射のあるソリューション

リフレクションを使用したソリューションは、古いバージョンと新しいバージョンのC#コンパイラーの両方で機能します。古いC#バージョンの場合は、この回答の最後にあるヒントを参考にしてください。

バックグラウンド

出発点として、私はここで良い答えを見つけました。アイデアは、リフレクションを使用して匿名データ型を辞書に変換することです。辞書を使用すると、プロパティの名前がキーとして保存されるため、プロパティに簡単にアクセスできます(これらのプロパティにはmyDict["myProperty"])。

上記のリンクのコードに触発され、私が提供する拡張クラスを作成しGetPropUnanonymizePropertiesかつ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の文字列を介して参照されるため、パラメーター化できることです。


1

最近、私は.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;

        ...
    }
    ...
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.