sourcecode

반사를 사용하여 개체 속성 설정

copyscript 2023. 5. 9. 22:57
반응형

반사를 사용하여 개체 속성 설정

C#에서 반사를 사용하여 객체 속성을 설정할 수 있는 방법이 있습니까?

예:

MyObject obj = new MyObject();
obj.Name = "Value";

설정합니다.obj.Name곰곰이 생각하여다음과 같은 것:

Reflection.SetProperty(obj, "Name") = "Value";

이것을 할 수 있는 방법이 있습니까?

예, 사용할 수 있습니다.Type.InvokeMember():

using System.Reflection;
MyObject obj = new MyObject();
obj.GetType().InvokeMember("Name",
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
    Type.DefaultBinder, obj, "Value");

이 경우 다음과 같은 경우 예외가 될 것입니다.obj라는 속성이 없습니다.Name설정할 수 없습니다.

또 다른 방법은 속성에 대한 메타데이터를 가져온 다음 설정하는 것입니다.이렇게 하면 속성이 있는지 확인하고 속성을 설정할 수 있는지 확인할 수 있습니다.

using System.Reflection;
MyObject obj = new MyObject();
PropertyInfo prop = obj.GetType().GetProperty("Name", BindingFlags.Public | BindingFlags.Instance);
if(null != prop && prop.CanWrite)
{
    prop.SetValue(obj, "Value", null);
}

다음 작업도 수행할 수 있습니다.

Type type = target.GetType();

PropertyInfo prop = type.GetProperty("propertyName");

prop.SetValue (target, propertyValue, null);

여기서 target은 속성을 설정할 개체입니다.

반사, 즉, 기본적으로.

myObject.GetType().GetProperty(property).SetValue(myObject, "Bob", null);

또는 편의성과 성능 면에서 모두 도움이 되는 라이브러리가 있습니다. 예를 들어 FastMember:

var wrapped = ObjectAccessor.Create(obj); 
wrapped[property] = "Bob";

(필드인지 속성인지 사전에 알 필요가 없다는 장점도 있음)

아니면 마크의 라이너 하나를 자신의 확장 클래스 안에 감쌀 수도 있습니다.

public static class PropertyExtension{       

   public static void SetPropertyValue(this object obj, string propName, object value)
    {
        obj.GetType().GetProperty(propName).SetValue(obj, value, null);
    }
}

그리고 이렇게 부릅니다.

myObject.SetPropertyValue("myProperty", "myValue");

적절한 측정을 위해 속성 값을 가져오는 방법을 추가합니다.

public static object GetPropertyValue(this object obj, string propName)
{
        return obj.GetType().GetProperty(propName).GetValue (obj, null);
}

다음과 같은 것을 사용합니다.

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    property.SetValue(p_object, Convert.ChangeType(value, property.PropertyType), null);
   }
}

또는

public static class PropertyExtension{       

   public static void SetPropertyValue(this object p_object, string p_propertyName, object value)
   {
    PropertyInfo property = p_object.GetType().GetProperty(p_propertyName);
    Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
    object safeValue = (value == null) ? null : Convert.ChangeType(value, t);

    property.SetValue(p_object, safeValue, null);
   }
}

예, 사용System.Reflection:

using System.Reflection;

...

    string prop = "name";
    PropertyInfo pi = myObject.GetType().GetProperty(prop);
    pi.SetValue(myObject, "Bob", null);

다음과 같은 방법으로 필드에 액세스할 수도 있습니다.

var obj=new MyObject();
FieldInfo fi = obj.GetType().
  GetField("Name", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(obj,value)

성찰을 통해 모든 것이 열린 책이 될 수 있습니다:) 이 예에서는 개인 인스턴스 수준 필드에 바인딩되어 있습니다.

속성 이름을 사용하여 다른 개체에서 개체의 속성을 대량 할당하려는 경우 이를 시도할 수 있습니다.

public static void Assign(this object destination, object source)
    {
        if (destination is IEnumerable && source is IEnumerable)
        {
            var dest_enumerator = (destination as IEnumerable).GetEnumerator();
            var src_enumerator = (source as IEnumerable).GetEnumerator();
            while (dest_enumerator.MoveNext() && src_enumerator.MoveNext())
                dest_enumerator.Current.Assign(src_enumerator.Current);
        }
        else
        {
            var destProperties = destination.GetType().GetProperties();
            foreach (var sourceProperty in source.GetType().GetProperties())
            {
                foreach (var destProperty in destProperties)
                {
                    if (destProperty.Name == sourceProperty.Name && destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
                    {
                        destProperty.SetValue(destination,     sourceProperty.GetValue(source, new object[] { }), new object[] { });
                        break;
            }
        }
    }
}

저는 방금 첫 번째 수준의 속성뿐만 아니라 지정된 개체에 중첩된 속성도 설정할 수 있는 Nuget 패키지를 게시했습니다.

여기 소포가 있습니다.

루트의 경로를 기준으로 개체의 속성 값을 설정합니다.

개체는 복잡한 개체이고 속성은 다중 수준의 중첩된 속성이거나 루트 바로 아래에 있는 속성일 수 있습니다. ObjectWriter속성 경로 매개 변수를 사용하여 속성을 찾고 해당 값을 업데이트합니다.속성 경로는 루트에서 설정하려는 끝 노드 속성으로 방문한 속성의 이름을 구분 기호 문자열 매개 변수로 구분합니다.

용도:

개체 루트 바로 아래에 속성을 설정하는 경우:

예.LineItem클래스에 다음과 같은 int 속성이 있습니다.ItemId

LineItem lineItem = new LineItem();

ObjectWriter.Set(lineItem, "ItemId", 13, delimiter: null);

개체 루트 아래에 중첩된 속성을 여러 수준으로 설정하는 경우:

예.Invite에는 클스다속있습다니성이음라는 .State라는 속성을 가지고 있습니다.Invite( type은 (Invite 유의형) 다음속가집라는 있습니다Recipient라는 속성을 가지고 있습니다.Id.

더 , 상을훨더씬만위해들기게잡하복황,위해,State유형이 입니다. 속성은struct.

다음은 객체 트리의 맨 아래에 있는 ID 속성("outlook"의 문자열 값으로)을 한 줄로 설정하는 방법입니다.

Invite invite = new Invite();

ObjectWriter.Set(invite, "State_Invite_Recipient_Id", "outlook", delimiter: "_");

Marc Gravel의 제안을 바탕으로 다음과 같은 정적 방법을 구성했습니다.메소드는 일반적으로 FastMember를 사용하여 소스 개체에서 대상으로 일치하는 모든 속성을 할당합니다.

 public static void DynamicPropertySet(object source, object target)
    {
        //SOURCE
        var src_accessor = TypeAccessor.Create(source.GetType());
        if (src_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var src_members = src_accessor.GetMembers();
        if (src_members == null)
        {
            throw new ApplicationException("Could not fetch members!");
        }
        var src_class_members = src_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var src_class_propNames = src_class_members.Select(x => x.Name);
        var src_propNames = src_members.Except(src_class_members).Select(x => x.Name);

        //TARGET
        var trg_accessor = TypeAccessor.Create(target.GetType());
        if (trg_accessor == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_members = trg_accessor.GetMembers();
        if (trg_members == null)
        {
            throw new ApplicationException("Could not create accessor!");
        }
        var trg_class_members = trg_members.Where(x => x.Type.IsClass && !x.Type.IsPrimitive);
        var trg_class_propNames = trg_class_members.Select(x => x.Name);
        var trg_propNames = trg_members.Except(trg_class_members).Select(x => x.Name);



        var class_propNames = trg_class_propNames.Intersect(src_class_propNames);
        var propNames = trg_propNames.Intersect(src_propNames);

        foreach (var propName in propNames)
        {
            trg_accessor[target, propName] = src_accessor[source, propName];
        }
        foreach (var member in class_propNames)
        {
            var src = src_accessor[source, member];
            var trg = trg_accessor[target, member];
            if (src != null && trg != null)
            {
                DynamicPropertySet(src, trg);
            }
        }
    }

언급URL : https://stackoverflow.com/questions/619767/set-object-property-using-reflection

반응형