리플렉션을 통해 사유 재산을 설정할 수 있습니까?


125

반사를 통해 사유 재산을 설정할 수 있습니까?

public abstract class Entity
{
    private int _id;
    private DateTime? _createdOn;
    public virtual T Id
    {
        get { return _id; }
        private set { ChangePropertyAndNotify(ref _id, value, x => Id); }
    }
    public virtual DateTime? CreatedOn
    {
        get { return _createdOn; }
        private set { ChangePropertyAndNotify(ref _createdOn, value, x => CreatedOn); }
    }
}

나는 다음을 시도하고 어디는 작동하지 않는 t유형을 나타냅니다 Entity:

var t = typeof(Entity);
var mi = t.GetMethod("set_CreatedOn", BindingFlags.Instance | BindingFlags.NonPublic);

나는 이것을 할 수 있다고 생각하지만 나는 그것을 해결할 수 없습니다.


2
나는 이것이 늦다는 것을 알고 있지만, 나는 나의 '이유'를 공유 할 것이라는 생각이 필요하다는 것을 발견했습니다. 일부 타사 소프트웨어의 불편 함을 극복해야했습니다. 특히 Crystal Reports ExportToStream 메서드를 사용하고있었습니다. 이 메소드가 작성된 방식으로 스트림의 내부 버퍼에 대한 액세스가 허용되지 않았습니다. 보고서를 브라우저로 보내기 위해서는 스트림을 새 버퍼 (100K +)에 복사 한 다음 보내야했습니다. 스트림 개체의 비공개 '_exposable'필드를 'true'로 설정하여 내부 버퍼를 직접 보낼 수있어 각 요청에 대해 100,000 개 이상의 할당을 절약 할 수 있습니다.
Ray

20
왜? 모든 도메인 개체의 Id 속성에 개인 설정자가 있고 저장소 테스트를 구현하려고한다고 가정 해 보겠습니다. 그런 다음 저장소 테스트 프로젝트에서만 Id 속성을 설정할 수 있기를 원할 것입니다.
bounav 2015-06-17

2
또 다른 사용 시나리오 : 데이터를 가져올 때 "생성 일"과 같은 자동 생성 필드 설정.
ANeves 2014

또 다른 이유는 가능한지 궁금합니다. 이것이 제가이 질문을 보는 방법입니다.
Caleb Mauer

답변:


94
t.GetProperty("CreatedOn")
    .SetValue(obj, new DateTime(2009, 10, 14), null);

편집 : 속성 자체가 공개되어 있으므로 BindingFlags.NonPublic찾기 위해 사용할 필요가없는 것 같습니다. SetValue접근성이 낮은 setter에도 불구하고 전화 는 여전히 예상대로 수행됩니다.


5
공정하게 말하면 신뢰 수준에 따라 다르지만 대답은 유효한 것 같습니다.
Marc Gravell

4
System.Reflection.RuntimePropertyInfo.SetValue (OBJ 개체, 개체 값 BindingFlags의 invokeAttr 바인더 바인더 개체 [] 인덱스 CultureInfo를 배양)에서 찾을 수없는 속성 설정 방법
CZahrobsky

1
가상 속성을 사용하지 않으면 잘 작동합니다. 가상 속성으로 값을 설정하면 작동하지 않는 것 같습니다.
JonathanPeel

105

예, 다음과 같습니다.

/// <summary>
/// Returns a _private_ Property Value from a given Object. Uses Reflection.
/// Throws a ArgumentOutOfRangeException if the Property is not found.
/// </summary>
/// <typeparam name="T">Type of the Property</typeparam>
/// <param name="obj">Object from where the Property Value is returned</param>
/// <param name="propName">Propertyname as string.</param>
/// <returns>PropertyValue</returns>
public static T GetPrivatePropertyValue<T>(this object obj, string propName)
{
    if (obj == null) throw new ArgumentNullException("obj");
    PropertyInfo pi = obj.GetType().GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    if (pi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Property {0} was not found in Type {1}", propName, obj.GetType().FullName));
    return (T)pi.GetValue(obj, null);
}

/// <summary>
/// Returns a private Property Value from a given Object. Uses Reflection.
/// Throws a ArgumentOutOfRangeException if the Property is not found.
/// </summary>
/// <typeparam name="T">Type of the Property</typeparam>
/// <param name="obj">Object from where the Property Value is returned</param>
/// <param name="propName">Propertyname as string.</param>
/// <returns>PropertyValue</returns>
public static T GetPrivateFieldValue<T>(this object obj, string propName)
{
    if (obj == null) throw new ArgumentNullException("obj");
    Type t = obj.GetType();
    FieldInfo fi = null;
    while (fi == null && t != null)
    {
        fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        t = t.BaseType;
    }
    if (fi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName));
    return (T)fi.GetValue(obj);
}

/// <summary>
/// Sets a _private_ Property Value from a given Object. Uses Reflection.
/// Throws a ArgumentOutOfRangeException if the Property is not found.
/// </summary>
/// <typeparam name="T">Type of the Property</typeparam>
/// <param name="obj">Object from where the Property Value is set</param>
/// <param name="propName">Propertyname as string.</param>
/// <param name="val">Value to set.</param>
/// <returns>PropertyValue</returns>
public static void SetPrivatePropertyValue<T>(this object obj, string propName, T val)
{
    Type t = obj.GetType();
    if (t.GetProperty(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) == null)
        throw new ArgumentOutOfRangeException("propName", string.Format("Property {0} was not found in Type {1}", propName, obj.GetType().FullName));
    t.InvokeMember(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, null, obj, new object[] { val });
}

/// <summary>
/// Set a private Property Value on a given Object. Uses Reflection.
/// </summary>
/// <typeparam name="T">Type of the Property</typeparam>
/// <param name="obj">Object from where the Property Value is returned</param>
/// <param name="propName">Propertyname as string.</param>
/// <param name="val">the value to set</param>
/// <exception cref="ArgumentOutOfRangeException">if the Property is not found</exception>
public static void SetPrivateFieldValue<T>(this object obj, string propName, T val)
{
    if (obj == null) throw new ArgumentNullException("obj");
    Type t = obj.GetType();
    FieldInfo fi = null;
    while (fi == null && t != null)
    {
        fi = t.GetField(propName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        t = t.BaseType;
    }
    if (fi == null) throw new ArgumentOutOfRangeException("propName", string.Format("Field {0} was not found in Type {1}", propName, obj.GetType().FullName));
    fi.SetValue(obj, val);
}

7
다른 사람의 머리카락을 보호하기 위해 (방금 내 머리에서 뽑은) : Silverlight 런타임에서는 작동하지 않습니다. msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs.95%29.aspx
Marc Wittke 2012

SetValue를 인덱스 통과 이전 지원하기 때문에, InvokeMember에보다 더 나은 것
크리스 슈

8

코드를 통해 파생 된 유형에서 개인 setter에 액세스 할 수 있습니다.

public static void SetProperty(object instance, string propertyName, object newValue)
{
    Type type = instance.GetType();

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

    prop.SetValue(instance, newValue, null);
}

+1,하지만 여기에 메모 만 있습니다. BaseType에는 예상하는 모든 속성이 있어야합니다. 재산을 숨기는 경우 (그렇게했다는 사실을 기억하지 못함) 머리카락이 빠질 수 있습니다.
ouflak

3

이것들 중 어느 것도 나를 위해 일하지 않았고 내 속성 이름이 고유했기 때문에 이것을 사용했습니다.

public static void SetPrivatePropertyValue<T>(T obj, string propertyName, object newValue)
{
    // add a check here that the object obj and propertyName string are not null
    foreach (FieldInfo fi in obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
    {
        if (fi.Name.ToLower().Contains(propertyName.ToLower()))
        {
            fi.SetValue(obj, newValue);
            break;
        }
    }
}

0
    //mock class
    public class Person{
        public string Name{get; internal set;}
    }

    // works for all types, update private field through reflection
    public static T ReviveType<T>(T t, string propertyName, object newValue){
        // add a check here that the object t and propertyName string are not null
        PropertyInfo pi = t.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
         pi.SetValue(t, newValue, null); 
        return t;
    }

    // check the required function
    void Main()
    {
        var p = new Person(){Name="John"};
        Console.WriteLine("Name: {0}",p.Name);

        //box the person to object, just to see that the method never care about what type you pass it
        object o = p;
        var updatedPerson = ReviveType<Object>(o, "Name", "Webber") as Person;

         //check if it updated person instance
        Console.WriteLine("Name: {0}",updatedPerson.Name);
    }



// Console Result: -------------------
Name: John
Name: Webber
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.