IT이야기

C#에서 익명 개체의 속성 반복

cyworld 2021. 10. 9. 14:39
반응형

C#에서 익명 개체의 속성을 어떻게 반복합니까?


익명 개체를 메서드에 대한 인수로 사용하고 해당 속성을 반복하여 각 속성/값을 aa dynamic 에 추가하고 싶습니다 ExpandoObject.

그래서 내가 필요한 것은

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

각 속성의 이름과 값을 알고 이를 에 추가할 수 있습니다 ExpandoObject.

이 작업을 수행하려면 어떻게 해야 합니까?

참고 사항: 이것은 많은 단위 테스트에서 수행되므로(설정에서 많은 정크를 리팩터링하는 데 사용하고 있습니다) 성능은 어느 정도 관련이 있습니다. 나는 리플렉션에 대해 확실히 말할 만큼 충분히 알지 못하지만 내가 이해한 바에 따르면 성능이 상당히 무거우므로 가능하면 피하는 것이 좋습니다 ...

후속 질문: 내가 말했듯이 이 익명 개체를 메서드에 대한 인수로 사용합니다. 메서드의 서명에 어떤 데이터 유형을 사용해야 합니까? 를 사용하면 모든 속성을 사용할 수 object있습니까?


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

익명 개체를 반영하여 속성 이름과 값을 가져온 다음 ExpandoObject를 실제로 사전으로 사용하여 채우십시오. 다음은 단위 테스트로 표현된 예입니다.

    [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");
    }

다른 접근 방식은 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 라는 유사한 재정의 사용 )하여 캐시 사전에서 값을 동적으로 설정할 수 있습니다.

물론 리플렉션 오버헤드는 걱정할 가치가 없지만 큰 개체의 경우 반사의 영향을 제한할 수 있습니다. 더 흥미로운 점은 이것이 제공하는 추가적인 유연성입니다.


이것은 오래된 질문이지만 이제 다음 코드로 이 작업을 수행할 수 있습니다.

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());
}

출력은 다음과 같습니다.

이름 = James Kirk : 유형: System.String

번호 = 34 : 형식: System.Int32


리플렉션을 사용해야 합니다....( 이 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);
}

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);
}

ReferenceURL : https://stackoverflow.com/questions/2594527/how-do-i-iterate-over-the-properties-of-an-anonymous-object-in-c

반응형