簡體   English   中英

Convert.ChangeType() 在可空類型上失敗

[英]Convert.ChangeType() fails on Nullable Types

我想將字符串轉換為對象屬性值,我將其名稱作為字符串。 我正在嘗試這樣做:

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

問題是當屬性類型是可空類型時,這會失敗並拋出無效的強制轉換異常。 這不是無法轉換值的情況 - 如果我手動執行此操作,它們將起作用(例如DateTime? d = Convert.ToDateTime(value); )我已經看到了一些類似的問題,但仍然無法得到它上班。

未經測試,但也許這樣的事情會奏效:

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

你必須獲得底層類型才能做到這一點......

試試這個,我已經成功地將它用於泛型:

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

我在代碼中的很多地方都使用了它,一個例子是我用來以類型安全的方式轉換數據庫值的輔助方法:

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

調用使用:

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

我寫了一系列博客文章,包括在http://www.endswithsaurus.com/2010_07_01_archive.html (向下滾動到附錄, @JohnMacintyre實際上在我的原始代碼中發現了錯誤,這導致我走上了與您相同的道路現在)。 自從那篇文章還包括枚舉類型的轉換后,我有一些小的修改,所以如果您的屬性是枚舉,您仍然可以使用相同的方法調用。 只需添加一行以檢查枚舉類型,您就可以使用以下內容參加比賽:

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

通常你會進行一些錯誤檢查或使用 TryParse 而不是 Parse,但你會得到圖片。

這對於一個例子來說有點長,但這是一種相對健壯的方法,並將從未知值轉換為未知類型的任務分開

我有一個 TryCast 方法可以做類似的事情,並將可空類型考慮在內。

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

當然 TryCast 是一個帶有類型參數的方法,因此要動態調用它,您必須自己構造 MethodInfo:

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

然后設置實際的屬性值:

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

以及處理 property.CanAssignValue 的擴展方法...

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

我有類似的需求,LukeH 的回答為我指明了方向。 我想出了這個通用函數來簡化它。

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

用法是這樣的:

        NotNullableDateProperty = CopyValue(NullableDateProperty, NotNullableDateProperty);

請注意,第二個參數僅用作原型來顯示函數如何轉換返回值,因此它實際上不必是目標屬性。 這意味着你也可以做這樣的事情:

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

我是這樣做的,而不是使用 out ,因為您不能使用 out 屬性。 照原樣,它可以處理屬性和變量。 如果需要,您還可以創建一個重載來傳遞類型。

這甚至適用於 Nullable 類型:

TypeConverter conv = TypeDescriptor.GetConverter(type);
return conv.ConvertFrom(value);

為了類型安全,您還應該在調用ConvertFrom()之前調用conv.CanConvertFrom(type)方法。 如果它返回 false,您可以回ChangeType或其他東西。

我是這樣做的

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

謝謝@LukeH
我改變了一點:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM