it-swarm.com.de

.NET - Ruft den Standardwert für eine reflektierte PropertyInfo ab

Das stört mich heute wirklich. Ich bin sicher, dass es nicht so schwer ist, aber ich habe ein System.Reflection.PropertyInfo-Objekt. Ich möchte den Wert auf der Grundlage des Ergebnisses einer Datenbanksuche festlegen (etwa ORM, eine Spalte wieder einer Eigenschaft zuordnen).

Mein Problem ist, wenn der von DB zurückgegebene Wert DBNull ist. Ich möchte nur den Eigenschaftswert auf seinen Standard setzen, genau wie beim Aufruf:

value = default(T);  // where T is the type of the property.

Die default () -Methode kann jedoch nicht kompiliert werden, wenn Sie ihr einen Typ geben.

object myObj = ???; // doesn't really matter. some arbitrary class.
PropertyInfo myPropInf = ???; // the reflection data for a property on the myObj object.
myPropInf.SetValue(myObj, default(myPropInf.PropertyType), null);

Das oben genannte kompiliert nicht. Standard (Typ) ist ungültig. Ich dachte auch darüber nach:

object myObj = ???;
PropertyInfo myPropInf = ???;
myPropInf.SetValue(myObj, Activator.CreateInstance(myPropInf.PropertyType), null);

Wenn der Typ jedoch string ist, würde dies den Wert "new String ()" zuweisen, aber ich möchte wirklich "null", was "default (string)" zurückgeben würde.

Also, was fehlt mir hier? Eine wirklich harte Methode wäre, eine neue Instanz von myObj's Type zu erstellen und die Eigenschaft zu kopieren, aber das erscheint einfach dumm.

object myObj = ???;
PropertyInfo  myPropInf = ???;
var blank = Activator.CreateInstance(myObj.GetType());
object defaultValue = myPropInf.GetValue(blank, null);
myPropInf.SetValue(myObj, defaultValue, null);

Ich möchte lieber nicht den Speicher verschwenden, um eine ganz neue Instanz zu erstellen, nur um den Standard für die Eigenschaft zu erhalten. Scheint sehr verschwenderisch.

Irgendwelche Ideen?

55
CodingWithSpike

Ich glaube, wenn du es einfach tust 

prop.SetValue(obj,null,null);

Wenn es sich um einen Werttyp handelt, wird es auf den Standardwert gesetzt. Wenn es sich um einen Referenztyp handelt, wird es auf Null gesetzt.

49
BFree
object defaultValue = type.IsValueType ? Activator.CreateInstance(type) : null;
47
Darin Dimitrov

Der "null" -Trick setzt den Wert zero für den Typ, der nicht unbedingt mit dem Standard für die Eigenschaft identisch ist. Erstens, wenn es sich um ein neues Objekt handelt, warum lassen Sie es nicht einfach in Ruhe? Alternativ können Sie TypeDescriptor verwenden:

PropertyDescriptor prop = TypeDescriptor.GetProperties(foo)["Bar"];
if (prop.CanResetValue(foo)) prop.ResetValue(foo);

Dies gilt sowohl für [DefaultValue] als auch für die Reset{name}()-Muster (wie sie von Bindung und Serialisierung verwendet werden), wodurch es sehr vielseitig und wiederverwendbar ist.

Wenn Sie viel davon tun, können Sie auch eine Leistungssteigerung mit TypeDescriptor anstelle von Reflektion erzielen, indem Sie PropertyDescriptorCollection erneut verwenden und HyperDescriptor verwenden (gleicher Code, aber viel schneller als entweder refletion oder raw TypeDescriptor).

28
Marc Gravell

Versuchen Sie die folgenden Methoden, die ich geschrieben und mit Tausenden von Typen getestet habe:

    /// <summary>
    /// [ <c>public static T GetDefault&lt; T &gt;()</c> ]
    /// <para></para>
    /// Retrieves the default value for a given Type
    /// </summary>
    /// <typeparam name="T">The Type for which to get the default value</typeparam>
    /// <returns>The default value for Type T</returns>
    /// <remarks>
    /// If a reference Type or a System.Void Type is supplied, this method always returns null.  If a value type 
    /// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an 
    /// exception.
    /// </remarks>
    /// <seealso cref="GetDefault(Type)"/>
    public static T GetDefault<T>()
    {
        return (T) GetDefault(typeof(T));
    }

    /// <summary>
    /// [ <c>public static object GetDefault(Type type)</c> ]
    /// <para></para>
    /// Retrieves the default value for a given Type
    /// </summary>
    /// <param name="type">The Type for which to get the default value</param>
    /// <returns>The default value for <paramref name="type"/></returns>
    /// <remarks>
    /// If a null Type, a reference Type, or a System.Void Type is supplied, this method always returns null.  If a value type 
    /// is supplied which is not publicly visible or which contains generic parameters, this method will fail with an 
    /// exception.
    /// </remarks>
    /// <seealso cref="GetDefault&lt;T&gt;"/>
    public static object GetDefault(Type type)
    {
        // If no Type was supplied, if the Type was a reference type, or if the Type was a System.Void, return null
        if (type == null || !type.IsValueType || type == typeof(void))
            return null;

        // If the supplied Type has generic parameters, its default value cannot be determined
        if (type.ContainsGenericParameters)
            throw new ArgumentException(
                "{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type +
                "> contains generic parameters, so the default value cannot be retrieved");

        // If the Type is a primitive type, or if it is another publicly-visible value type (i.e. struct), return a 
        //  default instance of the value type
        if (type.IsPrimitive || !type.IsNotPublic)
        {
            try
            {
                return Activator.CreateInstance(type);
            }
            catch (Exception e)
            {
                throw new ArgumentException(
                    "{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe Activator.CreateInstance method could not " +
                    "create a default instance of the supplied value type <" + type +
                    "> (Inner Exception message: \"" + e.Message + "\")", e);
            }
        }

        // Fail with exception
        throw new ArgumentException("{" + MethodInfo.GetCurrentMethod() + "} Error:\n\nThe supplied value type <" + type + 
            "> is not a publicly-visible type, so the default value cannot be retrieved");
    }

Die erste (generische) Version von GetDefault ist für C # natürlich überflüssig, da Sie nur das Standardschlüsselwort (T) verwenden können.

Genießen!

13
Mark Jones

Ich weiß, dass dies ein alter Beitrag ist, aber ich mag diese Wendung von Darin Dimitrovs Antwort. Es prüft zuerst auf DefualtValue-Attribute und verwendet dann Darin Dimitrovs Antwort :

public static object GetDefaultValueForProperty(this PropertyInfo property)
    {
        var defaultAttr = property.GetCustomAttribute(typeof(DefaultValueAttribute));
        if (defaultAttr != null)
            return (defaultAttr as DefaultValueAttribute).Value;

        var propertyType = property.PropertyType;
        return propertyType.IsValueType ? Activator.CreateInstance(propertyType) : null;
    }
0
rahicks

Dies ist eine ausgefeiltere Version, die die Funktionalität von .NET Runtime beibehält, ohne unnötigen benutzerdefinierten Code hinzuzufügen. 

HINWEIS: Dieser Code wurde für .NET 3.5 SP1 geschrieben 

namespace System {

public static class TypedDefaultExtensions {

    public static object ToDefault(this Type targetType) {

        if (targetType == null)
            throw new NullReferenceException();

        var mi = typeof(TypedDefaultExtensions)
            .GetMethod("_ToDefaultHelper", Reflection.BindingFlags.Static | Reflection.BindingFlags.NonPublic);

        var generic = mi.MakeGenericMethod(targetType);

        var returnValue = generic.Invoke(null, new object[0]);
        return returnValue;
    }

    static T _ToDefaultHelper<T>() {
        return default(T);
    }
}

}

VERWENDUNGSZWECK:

PropertyInfo pi; // set to some property info
object defaultValue = pi.PropertyType.ToDefault();

public struct MyTypeValue { public int SomeIntProperty { get; set; }
var reflectedType = typeof(MyTypeValue);
object defaultValue2 = reflectedType.ToDefault();

Rashad Rivera (OmegusPrime.com)

0
RashadRivera