Ist es möglich, Privateigentum durch Reflexion zu setzen?

125

Kann ich durch Reflexion ein Privateigentum festlegen?

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

Ich habe folgendes versucht und es funktioniert nicht, wobei teine Art von darstellt Entity:

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

Ich denke, ich kann das tun, aber ich kann es nicht herausfinden.

AwkwardCoder
quelle
2
Ich weiß, dass dies spät ist, aber ich fand einen Bedarf für diesen Gedanken, ich würde mein „Warum“ teilen. Ich musste die Unannehmlichkeiten einer Software von Drittanbietern überwinden. Insbesondere habe ich die Crystal Reports ExportToStream-Methode verwendet. So wie diese Methode geschrieben wurde, war der Zugriff auf den internen Puffer des Streams nicht zulässig. Um den Bericht an den Browser zu senden, musste ich den Stream in einen neuen Puffer (100 KB +) kopieren und dann senden. Indem ich das private Feld '_exposable' im Stream-Objekt auf 'true' setzte, konnte ich den internen Puffer direkt senden und bei jeder Anforderung eine Zuweisung von über 100 KB speichern.
Ray
20
Warum? Angenommen, Sie haben private Setter für Ihre ID-Eigenschaften für alle Ihre Domänenobjekte und möchten Repository-Tests implementieren. Dann möchten Sie nur in Ihrem Repository-Testprojekt die ID-Eigenschaft festlegen können.
Bounav
2
Ein weiteres Anwendungsszenario: Festlegen automatisch generierter Felder wie "Erstellungsdatum" beim Importieren von Daten.
ANeves
Ein weiterer Grund ist, dass ich nur neugierig bin, ob es möglich ist. So habe ich diese Frage gesehen.
Caleb Mauer

Antworten:

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

BEARBEITEN: Da die Eigenschaft selbst öffentlich ist, müssen Sie sie anscheinend nicht verwenden BindingFlags.NonPublic, um sie zu finden. Das Anrufen, SetValueobwohl der Setter weniger zugänglich ist, macht immer noch das, was Sie erwarten.

Tinister
quelle
5
Um fair zu sein, hängt es von der Vertrauensstufe ab, aber die Antwort scheint gültig zu sein.
Marc Gravell
4
Eigenschaftssatzmethode nicht gefunden bei System.Reflection.RuntimePropertyInfo.SetValue (Objekt obj, Objektwert, BindingFlags invokeAttr, Binder-Binder, Object [] -Index, CultureInfo-Kultur)
CZahrobsky
1
Dies funktioniert gut für mich, wenn ich keine virtuelle Eigenschaft verwende. Wenn ich einen Wert mit einer virtuellen Eigenschaft setze, scheint dies nicht zu funktionieren.
Jonathan
105

Ja, so ist es:

/// <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);
}
Arthur
quelle
7
Nur um andere Haare zu schützen (die mir gerade auf den Kopf gezogen wurden): Dies funktioniert in Silverlight-Laufzeiten nicht: msdn.microsoft.com/de-de/library/xb5dd1f1%28v=vs.95%29.aspx
Marc Wittke
SetValue wäre besser als InvokeMember, da der erstere das Übergeben des Index unterstützt
Chris Xue
8

Sie können vom abgeleiteten Typ über Code auf den privaten Setter zugreifen

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);
}
Siarhei Kuchuk
quelle
+1, aber nur eine Anmerkung hier. BaseType sollte alle erwarteten Eigenschaften haben. Wenn Sie eine Immobilie verstecken (ohne sich daran zu erinnern, dass Sie dies getan haben), kann dies dazu führen, dass einige Haare herausgezogen werden.
Ouflak
3

Keines davon hat für mich funktioniert, und mein Eigenschaftsname war eindeutig, also habe ich nur Folgendes verwendet:

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;
        }
    }
}
CZahrobsky
quelle
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
HdO
quelle