it-swarm.com.de

Convert.ChangeType () schlägt bei nullfähigen Typen fehl

Ich möchte einen String in einen Objekteigenschaftswert konvertieren, dessen Namen ich als String habe. Ich versuche das so zu machen:

string modelProperty = "Some Property Name";
string value = "SomeValue";
var property = entity.GetType().GetProperty(modelProperty);
if (property != null) {
    property.SetValue(entity, 
        Convert.ChangeType(value, property.PropertyType), null);
}

Das Problem ist, dass dies fehlschlägt und eine ungültige Cast-Ausnahme auslöst, wenn der Eigenschaftstyp ein nullfähiger Typ ist. Dies ist nicht der Fall, wenn die Werte nicht konvertiert werden können - sie funktionieren, wenn ich dies manuell mache (z. B. DateTime? d = Convert.ToDateTime(value);). Ich habe einige ähnliche Fragen gesehen, aber immer noch nicht, dass es funktioniert.

254
iboeno

Ungetestet, aber vielleicht funktioniert so etwas:

string modelProperty = "Some Property Name";
string value = "Some Value";

var property = entity.GetType().GetProperty(modelProperty);
if (property != null)
{
    Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;

    object safeValue = (value == null) ? null : Convert.ChangeType(value, t);

    property.SetValue(entity, safeValue, null);
}
359
LukeH

Sie müssen den zugrundeliegenden Typ dafür bekommen ...

Probieren Sie dies aus, ich habe es erfolgreich mit Generika verwendet:

//Coalesce to get actual property type...
Type t = property.PropertyType();
t = Nullable.GetUnderlyingType(t) ?? t;

//Coalesce to set the safe value using default(t) or the safe type.
safeValue = value == null ? default(t) : Convert.ChangeType(value, t);

Ich verwende es an verschiedenen Stellen in meinem Code. Ein Beispiel ist eine Hilfsmethode, die ich für die typsichere Konvertierung von Datenbankwerten verwende:

public static T GetValue<T>(this IDataReader dr, string fieldName)
{
    object value = dr[fieldName];

    Type t = typeof(T);
    t = Nullable.GetUnderlyingType(t) ?? t;

    return (value == null || DBNull.Value.Equals(value)) ? 
        default(T) : (T)Convert.ChangeType(value, t);
}

Aufruf mit:

string field1 = dr.GetValue<string>("field1");
int? field2 = dr.GetValue<int?>("field2");
DateTime field3 = dr.GetValue<DateTime>("field3");

Ich schrieb eine Reihe von Blogbeiträgen, darunter auch unter http://www.endswithsaurus.com/2010_07_01_archive.html (Scrollen Sie nach unten zum Addendum, @JohnMacintyre ) hat tatsächlich den Fehler in meinem ursprünglichen Code entdeckt Ich bin auf dem gleichen Weg, den Sie jetzt gehen. Ich habe seit diesem Beitrag ein paar kleine Änderungen vorgenommen, die auch die Konvertierung von Enummentypen beinhalten. Wenn Ihre Eigenschaft also ein Enum ist, können Sie immer noch denselben Methodenaufruf verwenden. Fügen Sie einfach eine Zeile hinzu, um nach Aufzählungstypen zu suchen, und Sie gehen zu den Rennen, indem Sie Folgendes verwenden:

if (t.IsEnum)
    return (T)Enum.Parse(t, value);

Normalerweise haben Sie einige Fehlerprüfungen oder verwenden TryParse anstelle von Parse, aber Sie erhalten das Bild.

64
BenAlabaster

Dies ist ein wenig langwierig für ein Beispiel, aber dies ist ein relativ robuster Ansatz und trennt die Aufgabe des Castings von einem unbekannten Wert in einen unbekannten Typ

Ich habe eine TryCast-Methode, die etwas Ähnliches ausführt und nullfähige Typen berücksichtigt.

public static bool TryCast<T>(this object value, out T result)
{
    var type = typeof (T);

    // If the type is nullable and the result should be null, set a null value.
    if (type.IsNullable() && (value == null || value == DBNull.Value))
    {
        result = default(T);
        return true;
    }

    // Convert.ChangeType fails on Nullable<T> types.  We want to try to cast to the underlying type anyway.
    var underlyingType = Nullable.GetUnderlyingType(type) ?? type;

    try
    {
        // Just one Edge case you might want to handle.
        if (underlyingType == typeof(Guid))
        {
            if (value is string)
            {
                value = new Guid(value as string);
            }
            if (value is byte[])
            {
                value = new Guid(value as byte[]);
            }

            result = (T)Convert.ChangeType(value, underlyingType);
            return true;
        }

        result = (T)Convert.ChangeType(value, underlyingType);
        return true;
    }
    catch (Exception ex)
    {
        result = default(T);
        return false;
    }
}

Natürlich ist TryCast eine Methode mit einem Typparameter. Um sie dynamisch aufzurufen, müssen Sie die MethodInfo selbst erstellen:

var constructedMethod = typeof (ObjectExtensions)
    .GetMethod("TryCast")
    .MakeGenericMethod(property.PropertyType);

Dann den tatsächlichen Wert der Eigenschaft einstellen:

public static void SetCastedValue<T>(this PropertyInfo property, T instance, object value)
{
    if (property.DeclaringType != typeof(T))
    {
        throw new ArgumentException("property's declaring type must be equal to typeof(T).");
    }

    var constructedMethod = typeof (ObjectExtensions)
        .GetMethod("TryCast")
        .MakeGenericMethod(property.PropertyType);

    object valueToSet = null;
    var parameters = new[] {value, null};
    var tryCastSucceeded = Convert.ToBoolean(constructedMethod.Invoke(null, parameters));
    if (tryCastSucceeded)
    {
        valueToSet = parameters[1];
    }

    if (!property.CanAssignValue(valueToSet))
    {
        return;
    }
    property.SetValue(instance, valueToSet, null);
}

Und die Erweiterungsmethoden, um mit property.CanAssignValue umzugehen ...

public static bool CanAssignValue(this PropertyInfo p, object value)
{
    return value == null ? p.IsNullable() : p.PropertyType.IsInstanceOfType(value);
}

public static bool IsNullable(this PropertyInfo p)
{
    return p.PropertyType.IsNullable();
}

public static bool IsNullable(this Type t)
{
    return !t.IsValueType || Nullable.GetUnderlyingType(t) != null;
}
8
Eric Burcham

Ich hatte ein ähnliches Bedürfnis, und die Antwort von LukeH zeigte mir die Richtung. Ich habe mir diese generische Funktion ausgedacht, um es einfach zu machen.

    public static Tout CopyValue<Tin, Tout>(Tin from, Tout toPrototype)
    {
        Type underlyingT = Nullable.GetUnderlyingType(typeof(Tout));
        if (underlyingT == null)
        { return (Tout)Convert.ChangeType(from, typeof(Tout)); }
        else
        { return (Tout)Convert.ChangeType(from, underlyingT); }
    }

Die Nutzung ist wie folgt:

        NotNullableDateProperty = CopyValue(NullableDateProperty, NotNullableDateProperty);

Beachten Sie, dass der zweite Parameter nur als Prototyp verwendet wird, um der Funktion zu zeigen, wie der Rückgabewert umgewandelt werden soll. Es muss also nicht wirklich die Zieleigenschaft sein. Das heißt, du kannst auch so etwas tun:

        DateTime? source = new DateTime(2015, 1, 1);
        var dest = CopyValue(source, (string)null);

Ich habe es auf diese Weise gemacht, anstatt ein Out zu verwenden, weil Sie Eigenschaften nicht verwenden können. Wie es ist, kann es mit Eigenschaften und Variablen arbeiten. Sie können auch eine Überladung erstellen, um den Typ zu übergeben, wenn Sie möchten.

6
Steve In CO

Ich habe es auf diese Weise gemacht 

public static List<T> Convert<T>(this ExcelWorksheet worksheet) where T : new()
    {
        var result = new List<T>();
        int colCount = worksheet.Dimension.End.Column;  //get Column Count
        int rowCount = worksheet.Dimension.End.Row;

        for (int row = 2; row <= rowCount; row++)
        {
            var obj = new T();
            for (int col = 1; col <= colCount; col++)
            {

                var value = worksheet.Cells[row, col].Value?.ToString();
                PropertyInfo propertyInfo = obj.GetType().GetProperty(worksheet.Cells[1, col].Text);
                propertyInfo.SetValue(obj, Convert.ChangeType(value, Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType), null);

            }
            result.Add(obj);
        }

        return result;
    }
0
user2134745

Danke @LukeH
Ich habe mich etwas verändert: 

public static object convertToPropType(PropertyInfo property, object value)
{
    object cstVal = null;
    if (property != null)
    {
        Type propType = Nullable.GetUnderlyingType(property.PropertyType);
        bool isNullable = (propType != null);
        if (!isNullable) { propType = property.PropertyType; }
        bool canAttrib = (value != null || isNullable);
        if (!canAttrib) { throw new Exception("Cant attrib null on non nullable. "); }
        cstVal = (value == null || Convert.IsDBNull(value)) ? null : Convert.ChangeType(value, propType);
    }
    return cstVal;
}
0
hs586sd46s