Код ниже работает хорошо, если у меня есть класс 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);
}
}
}
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
dynamic
c#-4.0
anonymous-types
механик
источник
источник
Ответы:
Я считаю, что проблема в том, что анонимный тип создается как
internal
, поэтому связыватель на самом деле не «знает» об этом как таковом.Попробуйте вместо этого использовать ExpandoObject:
public static dynamic GetValues() { dynamic expando = new ExpandoObject(); expando.Name = "Michael"; expando.Age = 20; return expando; }
Я знаю, что это несколько уродливо, но это лучшее, что я могу придумать на данный момент ... Я не думаю, что вы даже можете использовать с ним инициализатор объекта, потому что, хотя он строго типизирован, поскольку
ExpandoObject
компилятор не знает, что делать с «Именем» и «Возрастом». Вы можете сделать это:dynamic expando = new ExpandoObject() { { "Name", "Michael" }, { "Age", 20 } }; return expando;
но это не намного лучше ...
Вы могли бы потенциально написать метод расширения для преобразования анонимного типа в раскрывающийся с тем же содержимым через отражение. Тогда вы могли написать:
return new { Name = "Michael", Age = 20 }.ToExpando();
Хотя это довольно ужасно :(
источник
return Build<ExpandoObject>.NewObject(Name:"Micheal", Age: 20);
object
для универсального типа, либо для него (вы можете потребовать, чтобы это был класс ...) и проверять тип во время выполнения.Вы можете использовать,
[assembly: InternalsVisibleTo("YourAssemblyName")]
чтобы сделать вашу сборку видимой.источник
Я столкнулся с аналогичной проблемой и хотел бы добавить к ответу Джона Скитса, что есть еще один вариант. Причина, по которой я узнал, заключалась в том, что я понял, что многие методы расширения в 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"]);
ТАК ... Что на самом деле здесь происходит? Заглянув внутрь 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); }
ТАК - с помощью TypeDescriptor.GetProperties (o) мы сможем получить свойства и значения, несмотря на то, что анонимный тип создается как внутренний в отдельной сборке! И, конечно, это было бы довольно легко расширить, чтобы сделать его рекурсивным. И сделать способ расширения, если хотите.
Надеюсь это поможет!
/Виктор
источник
Вот элементарная версия метода расширения для 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); }
источник
Более чистое решение:
var d = ClassSameAssembly.GetValues().ToDynamic();
Теперь это ExpandoObject.
Не забудьте указать:
источник
Ниже решение сработало для меня в моих проектах консольных приложений.
Поместите эту [сборку: InternalsVisibleTo ("YourAssemblyName")] в \ Properties \ AssemblyInfo.cs отдельного проекта с функцией, возвращающей динамический объект.
YourAssemblyName - имя сборки вызывающего проекта. Вы можете получить это через Assembly.GetExecutingAssembly (). FullName, выполнив его в вызывающем проекте.
источник
Метод расширения ToExpando (упомянутый в ответе Джона) для смелых
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; } }
источник
Если вы уже используете Newtonsoft.Json в своем проекте (или хотите добавить его для этой цели), вы можете реализовать этот ужасный метод расширения, на который Джон Скит ссылается в своем ответе следующим образом:
public static class ObjectExtensions { public static ExpandoObject ToExpando(this object obj) => JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(obj)); }
источник