web-dev-qa-db-ja.com

匿名型を動的にキャストする

MVCコントローラーでテストする匿名型を返す関数があります。

public JsonResult Foo()
{
    var data = new
                  {
                      details = "something",
                      more = "More"
                  };
    return Json(data);
}

Foo関数から取得したデータを確認したいのですが、今行っているのは、データ型を取得し、リフレクションを使用してそのプロパティ値を取得することです。

[Test]
public void TestOne()
{
    var data = _controller.Foo().Data;
    var details = data.GetType().GetProperty("details").GetValue(data, null);
    var more = data.GetType().GetProperty("more").GetValue(data, null);

    Assert.AreEquals("something", details);
    Assert.AreEquals("More", more);
}

匿名プロパティをチェックするこれに似た簡単な方法はありますか?

[Test]
public void TestTwo()
{
    var data = (dynamic) _controller.Foo().Data;
    var details = data.details; // RunTimeBinderException object does not contain definition for details
    var more = data.more;

    Assert.AreEquals("something", details);
    Assert.AreEquals("More", more);
}

匿名オブジェクトはinternalです。つまり、それらのメンバーは、それらを宣言するアセンブリの外部では非常に制限されています。 dynamicはアクセシビリティを尊重するため、これらのメンバーを見ることができないふりをします。呼び出しサイトが同じアセンブリ内にあった場合、それが機能することを期待しています。

リフレクションコードはmemberアクセシビリティを尊重しますが、型のアクセシビリティをバイパスします-したがって機能します。

つまり、いいえ。

40
Marc Gravell

このブログには有効な回答がありました: http://blog.jorgef.net/2011/06/converting-any-object-to-dynamic.html -ありがとう@ Jorge-Fioranelli。

public static class DynamicExtensions {
    public static dynamic ToDynamic(this object value) {
        IDictionary<string, object> expando = new ExpandoObject();

        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
            expando.Add(property.Name, property.GetValue(value));

        return expando as ExpandoObject;
    }
}
25
Hafthor

匿名型は.NETの通常の静的型であり、名前を付けないだけです(ただし、コンパイラは名前を付けます)。これがdynamicへのキャストが機能しない理由です。ただし、Foo()を制御できる場合は、匿名の代わりにdynamicオブジェクトを作成して返すことができ、コードが機能します。これでうまくいくはずです:

dynamic JsonResult Foo() {
    dynamic data = new ExpandoObject();
    data.details = "something";
    data.mode = "More";
    return Json(data);
}
9
dasblinkenlight

@TrueWillおよび@Marc Gravellが示唆したように、これらも参照 このブログ投稿

これは単体テスト用なので、InternalsVisibleToを使用できます。匿名型は内部である、C#4.0動的注意に注意してください!匿名オブジェクトが内部にあることを指摘してくれた@MarcGravellに感謝します!

結論:あるアセンブリから別のアセンブリに匿名オブジェクトを共有する場合は、[Assembly: InternalsVisibleTo("foo")]マッピングをセットアップします。 OPの場合は、test projectを参照して、MVCコントローラープロジェクトでこれを設定するだけです。私の特定のケースでは、その逆です(私のテストプロジェクトから "量産コード"プロジェクトに匿名オブジェクトを渡しているため)。

この「他のプロジェクト」で使用できる最も簡単な方法は、確実にdynamicにキャストしてから、通常のようにプロパティを使用することです。動作しますが、問題はありません。

つまり、最終的には、Marc Gravellの答えは少し間違っているように感じます。これは明らかにできる
iff問題のプロジェクトはユーザーが変更できるため、InternalsVisibleToマッピングを適宜設定できます。これにより、その他の理由による問題)。

8
Per Lundberg

NewtonSoftまたはAsp.net MVCライブラリを使用できます。

var data = Json.Decode(Json.Encode(_controller.Foo().Data));

var data=JsonConvert.DeserializeObject<Dictionary<string,object>>(JsonConvert.SerializeObject((_controller.Foo().Data))

1
user4614448