Подтвердить что ты не робот

Как выполнить итерацию свойств анонимного объекта в С#?

Я хочу взять анонимный объект в качестве аргумента для метода, а затем перебрать его свойства, чтобы добавить каждое свойство/значение в динамический ExpandoObject.

Итак, мне нужно перейти от

new { Prop1 = "first value", Prop2 = SomeObjectInstance, Prop3 = 1234 }

чтобы знать имена и значения каждого свойства и быть в состоянии добавить их в ExpandoObject.

Как это сделать?

Боковое примечание. Это будет сделано во многих моих модульных тестах (я использую его для реорганизации большого количества нежелательной почты в настройке), поэтому производительность в некоторой степени актуальна. Я не знаю достаточно о том, чтобы отразить, чтобы сказать наверняка, но из того, что я понял, это довольно высокая производительность, поэтому, если это возможно, я бы предпочел избежать этого...

Последующий вопрос: Как я уже сказал, я рассматриваю этот анонимный объект как аргумент метода. Какой тип данных следует использовать в сигнатуре метода? Будут доступны все свойства, если я использую object?

4b9b3361

Ответ 1

foreach(var prop in myVar.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
   Console.WriteLine("Name: {0}, Value: {1}",prop.Name, prop.GetValue(myVar,null));
}

Ответ 2

Отразите анонимный объект, чтобы получить его имена и значения свойств, затем используйте ExpandoObject, фактически являющийся словарем для его заполнения. Вот пример, выраженный как unit test:

    [TestMethod]
    public void ShouldBeAbleToConvertAnAnonymousObjectToAnExpandoObject()
    {
        var additionalViewData = new {id = "myControlId", css = "hide well"};
        dynamic result = new ExpandoObject();
        var dict = (IDictionary<string, object>)result;
        foreach (PropertyInfo propertyInfo in additionalViewData.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            dict[propertyInfo.Name] = propertyInfo.GetValue(additionalViewData, null);
        }
        Assert.AreEqual(result.id, "myControlId");
        Assert.AreEqual(result.css, "hide well");
    }

Ответ 3

Альтернативный подход заключается в использовании DynamicObject вместо ExpandoObject, и таким образом у вас есть только накладные расходы на выполнение отражения, если вы действительно пытаетесь получить доступ к свойству из другого объекта.

public class DynamicForwarder : DynamicObject 
{
    private object _target;

    public DynamicForwarder(object target)
    {
        _target = target;
    }

    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        var prop = _target.GetType().GetProperty(binder.Name);
        if (prop == null)
        {
            result = null;
            return false;
        }

        result = prop.GetValue(_target, null);
        return true;
    }
}

Теперь это только отражение, когда вы на самом деле пытаетесь получить доступ к свойству через динамический get. С другой стороны, если вы повторно обращаетесь к одному и тому же свойству, он должен делать отражение каждый раз. Таким образом, вы можете кэшировать результат:

public class DynamicForwarder : DynamicObject 
{
    private object _target;
    private Dictionary<string, object> _cache = new Dictionary<string, object>();

    public DynamicForwarder(object target)
    {
        _target = target;
    }

    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        // check the cache first
        if (_cache.TryGetValue(binder.Name, out result))
            return true;

        var prop = _target.GetType().GetProperty(binder.Name);
        if (prop == null)
        {
            result = null;
            return false;
        }

        result = prop.GetValue(_target, null);
        _cache.Add(binder.Name, result); // <-------- insert into cache
        return true;
    }
}

Вы можете поддерживать сохранение списка целевых объектов для объединения их свойств и поддержки свойств настройки (с аналогичным переопределением, называемым TrySetMember), чтобы вы могли динамически устанавливать значения в словаре кеша.

Конечно, накладные расходы на размышления, вероятно, не стоит беспокоиться, но для больших объектов это может ограничить его влияние. Что может быть более интересным, так это дополнительная гибкость, которую это дает вам.

Ответ 4

Используйте Reflection.Emit, чтобы создать общий метод для заполнения ExpandoObject.

ИЛИ использовать выражения (возможно, это возможно только в .NET 4).

Ни один из этих подходов не использует отражение при вызове только при настройке делегата (который, очевидно, необходимо кэшировать).

Вот какой код Reflection.Emit для заполнения словаря (я думаю, что ExpandoObject не за горами);

static T CreateDelegate<T>(this DynamicMethod dm) where T : class
{
  return dm.CreateDelegate(typeof(T)) as T;
}

static Dictionary<Type, Func<object, Dictionary<string, object>>> cache = 
   new Dictionary<Type, Func<object, Dictionary<string, object>>>();

static Dictionary<string, object> GetProperties(object o)
{
  var t = o.GetType();

  Func<object, Dictionary<string, object>> getter;

  if (!cache.TryGetValue(t, out getter))
  {
    var rettype = typeof(Dictionary<string, object>);

    var dm = new DynamicMethod(t.Name + ":GetProperties", rettype, 
       new Type[] { typeof(object) }, t);

    var ilgen = dm.GetILGenerator();

    var instance = ilgen.DeclareLocal(t);
    var dict = ilgen.DeclareLocal(rettype);

    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Castclass, t);
    ilgen.Emit(OpCodes.Stloc, instance);

    ilgen.Emit(OpCodes.Newobj, rettype.GetConstructor(Type.EmptyTypes));
    ilgen.Emit(OpCodes.Stloc, dict);

    var add = rettype.GetMethod("Add");

    foreach (var prop in t.GetProperties(
      BindingFlags.Instance |
      BindingFlags.Public))
    {
      ilgen.Emit(OpCodes.Ldloc, dict);

      ilgen.Emit(OpCodes.Ldstr, prop.Name);

      ilgen.Emit(OpCodes.Ldloc, instance);
      ilgen.Emit(OpCodes.Ldfld, prop);
      ilgen.Emit(OpCodes.Castclass, typeof(object));

      ilgen.Emit(OpCodes.Callvirt, add);
    }

    ilgen.Emit(OpCodes.Ldloc, dict);
    ilgen.Emit(OpCodes.Ret);

    cache[t] = getter = 
      dm.CreateDelegate<Func<object, Dictionary<string, object>>>();
  }

  return getter(o);
}

Ответ 5

Это старый вопрос, но теперь вы должны сделать это со следующим кодом:

dynamic expObj = new ExpandoObject();
    expObj.Name = "James Kirk";
    expObj.Number = 34;

// print the dynamically added properties
// enumerating over it exposes the Properties and Values as a KeyValuePair
foreach (KeyValuePair<string, object> kvp in expObj){ 
    Console.WriteLine("{0} = {1} : Type: {2}", kvp.Key, kvp.Value, kvp.Value.GetType());
}

Результат будет выглядеть следующим образом:

Name = James Kirk: Тип: System.String

Число = 34: Тип: System.Int32

Ответ 6

вам нужно использовать отражение.... (код "заимствован" из этого URL-адреса)

using System.Reflection;  // reflection namespace

// get all public static properties of MyClass type
PropertyInfo[] propertyInfos;
propertyInfos = typeof(MyClass).GetProperties(BindingFlags.Public |
                                              BindingFlags.Static);
// sort properties by name
Array.Sort(propertyInfos,
        delegate(PropertyInfo propertyInfo1, PropertyInfo propertyInfo2)
        { return propertyInfo1.Name.CompareTo(propertyInfo2.Name); });

// write property names
foreach (PropertyInfo propertyInfo in propertyInfos)
{
  Console.WriteLine(propertyInfo.Name);
}