C#'dynamic'无法访问另一个程序集中声明的匿名类型的属性

meh*_*nik 85 dynamic anonymous-types c#-4.0

只要我在班级和班级ClassSameAssembly相同的班级中,下面的代码就能正常运行Program.但是当我将类移动ClassSameAssembly到一个单独的程序集时,RuntimeBinderException会抛出一个(见下文).有可能解决它吗?

using System;

namespace ConsoleApplication2
{
    public static class ClassSameAssembly
    {
        public static dynamic GetValues()
        {
            return new
            {
                Name = "Michael", Age = 20
            };
        }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            var d = ClassSameAssembly.GetValues();
            Console.WriteLine("{0} is {1} years old", d.Name, d.Age);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:'object'不包含'Name'的定义

at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at ConsoleApplication2.Program.Main(String[] args) in C:\temp\Projects\ConsoleApplication2\ConsoleApplication2\Program.cs:line 23
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 113

我认为问题是匿名类型是生成的internal,因此绑定器并不真正"知道"它本身.

请尝试使用ExpandoObject:

public static dynamic GetValues()
{
    dynamic expando = new ExpandoObject();
    expando.Name = "Michael";
    expando.Age = 20;
    return expando;
}
Run Code Online (Sandbox Code Playgroud)

我知道这有点难看,但这是我现在能想到的最好的...我不认为你甚至可以使用它的对象初始化器,因为它是强类型的,因为ExpandoObject编译器不知道该怎么做用"名字"和"年龄".你可以这样做:

 dynamic expando = new ExpandoObject()
 {
     { "Name", "Michael" },
     { "Age", 20 }
 };
 return expando;
Run Code Online (Sandbox Code Playgroud)

但那不是更好......

你可以潜在地写一个扩展方法匿名类型转换为在Expando通过反射相同的内容.然后你可以写:

return new { Name = "Michael", Age = 20 }.ToExpando();
Run Code Online (Sandbox Code Playgroud)

虽然这很可怕:(

  • 我会喜欢最后可怕的例子,但不是那么可怕.使用:dynamic props = new {Metadata = DetailModelMetadata.Create,PageTitle ="New Content",PageHeading ="Content Management"}; 并添加命名道具作为动态成员将是伟大的! (2认同)

ema*_*ema 61

您可以使用它[assembly: InternalsVisibleTo("YourAssemblyName")]来使装配体内部可见.

  • Jon的答案更完整,但这实际上为我提供了一个相当简单的解决方法.谢谢 :) (2认同)
  • 是的,我认为这个答案很重要,因为我不想更改工作代码,我只需要从另一个程序集中对其进行测试 (2认同)

Vic*_*tor 10

我遇到了类似问题,并希望在Jon Skeets的答案中添加另一种选择.我发现的原因是我意识到Asp MVC3中的许多扩展方法使用匿名类作为输入来提供html属性(new {alt ="Image alt",style ="padding-top:5px"} =>

无论如何 - 这些函数使用RouteValueDictionary类的构造函数.我自己试过,确定它有效 - 虽然只有第一级(我使用了多级结构).所以 - 在代码中这将是:

object o = new {
    name = "theName",
    props = new {
        p1 = "prop1",
        p2 = "prop2"
    }
}
SeparateAssembly.TextFunc(o)

//In SeparateAssembly:
public void TextFunc(Object o) {
  var rvd = new RouteValueDictionary(o);

//Does not work:
Console.WriteLine(o.name);
Console.WriteLine(o.props.p1);

//DOES work!
Console.WriteLine(rvd["name"]);

//Does not work
Console.WriteLine(rvd["props"].p1);
Console.WriteLine(rvd["props"]["p1"]);
Run Code Online (Sandbox Code Playgroud)

所以...这里到底发生了什么?在RouteValueDictionary中查看此代码(上面的值〜= o):

foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
    object obj2 = descriptor.GetValue(values);
    //"this.Add" would of course need to be adapted
    this.Add(descriptor.Name, obj2);
}
Run Code Online (Sandbox Code Playgroud)

所以 - 使用TypeDescriptor.GetProperties(o)我们将能够获得属性和值,尽管匿名类型在单独的程序集中被构造为内部!当然,这很容易扩展以使其递归.并根据需要制作扩展方法.

希望这可以帮助!

/胜利者