C#'dynamic'不能访问另一个程序集中声明的匿名types的属性

下面的代码运行良好,只要我在Class ClassSameAssembly的同一个程序ClassSameAssembly有类ClassSameAssembly 。 但是,当我把类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); } } } 

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException“对象”不包含“名称”的定义

 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 

我相信问题是匿名types是作为internal生成的,所以绑定器并不真正“知道”它。

尝试使用ExpandoObject来代替:

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

我知道这有点难看,但这是目前我能想到的最好的…我不认为你甚至可以使用对象初始值设定项,因为虽然强types为ExpandoObject但编译器不知道该怎么做用“姓名”和“年龄”来做。 你可以这样做:

  dynamic expando = new ExpandoObject() { { "Name", "Michael" }, { "Age", 20 } }; return expando; 

但是这不是更好…

您可以编写一个扩展方法,通过reflection将匿名types转换为具有相同内容的扩展。 然后你可以写:

 return new { Name = "Michael", Age = 20 }.ToExpando(); 

这是非常可怕的,虽然:(

您可以使用[assembly: InternalsVisibleTo("YourAssemblyName")]使组件的内部可见。

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

无论如何 – 这些函数使用RouteValueDictionary类的构造函数。 我自己试了一下,果然有效 – 虽然只有第一级(我使用了多级结构)。 SO – 在代码中这将是:

 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"]); 

所以…这里到底发生了什么? 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); } 

SO – 使用TypeDescriptor.GetProperties(o)我们将能够获得属性和值,尽pipe匿名types被构造为一个单独的程序集内部! 当然,这将是很容易扩展,使其recursion。 如果你想要做一个扩展方法。

希望这可以帮助!

/胜利者

这是ToExpandoObject扩展方法的一个基本版本,我相信这个方法有很大的空间。

  public static ExpandoObject ToExpandoObject(this object value) { // Throw is a helper in my project, replace with your own check(s) Throw<ArgumentNullException>.If(value, Predicates.IsNull, "value"); var obj = new ExpandoObject() as IDictionary<string, object>; foreach (var property in value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { obj.Add(property.Name, property.GetValue(value, null)); } return obj as ExpandoObject; } [TestCase(1, "str", 10.75, 9.000989, true)] public void ToExpandoObjectTests(int int1, string str1, decimal dec1, double dbl1, bool bl1) { DateTime now = DateTime.Now; dynamic value = new {Int = int1, String = str1, Decimal = dec1, Double = dbl1, Bool = bl1, Now = now}.ToExpandoObject(); Assert.AreEqual(int1, value.Int); Assert.AreEqual(str1, value.String); Assert.AreEqual(dec1, value.Decimal); Assert.AreEqual(dbl1, value.Double); Assert.AreEqual(bl1, value.Bool); Assert.AreEqual(now, value.Now); } 

更清洁的解决scheme是:

 var d = ClassSameAssembly.GetValues().ToDynamic(); 

现在是一个ExpandoObject。

记得引用:

 Microsoft.CSharp.dll 

ToExpando扩展方法(在Jon的答案中提到)为勇敢的

 public static class ExtensionMethods { public static ExpandoObject ToExpando(this object obj) { IDictionary<string, object> expando = new ExpandoObject(); foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(obj)) { var value = propertyDescriptor.GetValue(obj); expando.Add(propertyDescriptor.Name, value == null || new[] { typeof (Enum), typeof (String), typeof (Char), typeof (Guid), typeof (Boolean), typeof (Byte), typeof (Int16), typeof (Int32), typeof (Int64), typeof (Single), typeof (Double), typeof (Decimal), typeof (SByte), typeof (UInt16), typeof (UInt32), typeof (UInt64), typeof (DateTime), typeof (DateTimeOffset), typeof (TimeSpan), }.Any(oo => oo.IsInstanceOfType(value)) ? value : value.ToExpando()); } return (ExpandoObject)expando; } } 

下面的解决scheme在我的控制台应用程序项目中工作

将这个[assembly:InternalsVisibleTo(“YourAssemblyName”)]放在单独项目的\ Properties \ AssemblyInfo.cs中,并返回dynamic对象的函数。

“YourAssemblyName”是调用项目的程序集名称。 您可以通过在调用项目中执行它来通过Assembly.GetExecutingAssembly()。FullName获取它。

如果你已经在你的项目中使用了Newtonsoft.Json(或者你愿意为此添加它),那么你可以实现Jon Skeet在他的回答中提到的可怕扩展方法:

 public static class ObjectExtensions { public static ExpandoObject ToExpando(this object obj) => JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(obj)); }