2011-10-04 65 views
2

我做了一個轉換方法來處理procs返回的數據庫值。它看起來像這樣:直接拆箱盒int到

public static T GetVerifiedValue<T>(this IDataRecord record, int index) 
{ 
    object value = record[index]; 

    if (value is string) 
     value = string.IsNullOrEmpty(value as string) ? null : value; 

    //Trata valores de enuns nuláveis. Um cast direto de um tipo numérico (Int32, por exemplo) 
    //para um valor de enum lança uma exception InvalidCastException. 
    Type nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T)); 

    if (nullableUnderlyingType != null) 
    { 
     if (nullableUnderlyingType.IsEnum) 
      return value == null || value.Equals(DBNull.Value) ? default(T) : (T)Enum.ToObject(nullableUnderlyingType, value); 
    } 

    /* 
    //This is my try on solving the problem, but won't compile 
    //becouse T has no struct constraint 
    if (value is ValueType) 
    { 
     ValueType structValue = (ValueType)value; 
     return value.Equals(DBNull.Value) ? default(T) : (T)structValue; 
    } 
    */ 

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

的問題是,當數據庫返回的整數,將value變量將包含一個int,當Tshort,我得到一個InvalidCastException

我該如何改進這種方法來處理這種情況?我希望方法的用戶不要擔心這種問題,不必重複投射。這可能嗎?

+2

不要使用'unboxing'期限,而不是'casting','unboxing'平均引用類型 - >值類型 – sll

+1

這是發生了什麼:'value'變量,這是一個'object',包含一個'int'。然後我把它投向一個「短」。是不是'拆開'變量的'int'值? – Raphael

+0

@Raphael:好的,但是在這種情況下,你可以擴展標題,因爲它真的讓''unboxing int'變成short',就像'object(int) - > short'一樣。 – sll

回答

2

我找到了一種方法(見解釋代碼註釋):

public static T GetVerifiedValue<T>(this IDataRecord record, int index) 
{ 
    object value = record[index]; 

    if (value == null || value.Equals(DBNull.Value)) 
     return default(T); 

    //This handles nullable values, because sometimes there is a need for 
    //a casting on the underlying value before the final cast 
    Type nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T)); 

    if (nullableUnderlyingType != null) 
    { 
     if (nullableUnderlyingType.IsEnum) 
      return (T)Enum.ToObject(nullableUnderlyingType, value); 
     else 
      return (T)Convert.ChangeType(value, nullableUnderlyingType); 
    } 

    //Enums must be handled before the ValueTypes, becouse 
    //enums are also ValueTypes and using Convert.ChangeType with it will fail 
    if (typeof(T).IsEnum) 
     return (T)Enum.ToObject(typeof(T), value); 


    //###################################################################### 
    //Here is the trick: as Convert.ChangeType returns an object, 
    //it is compatible with the unconstrained T. Worked nicely. 
    if (value is ValueType) 
    { 
     ValueType structValue = (ValueType)value; 
     return (T)Convert.ChangeType(structValue, typeof(T)); 
    } 
    //###################################################################### 


    if (value is string) 
     value = string.IsNullOrEmpty(value as string) ? null : value; 

    return (T)value; 
} 
4

只能將盒裝值類型轉換爲正確的值類型,然後再轉換爲不同的值類型(如果支持此類轉換)。

但是,有Convert類。如果您在value中有盒裝int,則可以將其傳遞到Convert.ToInt16(value)並獲得short返回。由於您可能使用Convert類(而不是鑄造)的類型數量很少,因此如果使用Convert,則使用switch的靜態通用幫助程序方法將運行良好。

static bool MaybeConvert<TOutput>(object value, out TOutput result) { 
    output = default(TOutput); 
    switch(typeof(TOutput)) { 
     case typeof(short): 
      result = Convert.ToInt16(value); 
      return true; 

     ... 

     default: 
      return false; 
    } 
} 

我用Convert很多與數據庫的結果,因爲有時候即使你有32位整型領域的工作,如果一些數學或聚集做,他們正在返回爲64位整數。使用Convert.ToInt32比檢查類型和自己做特定的演員要容易得多。

4

使用結果作爲dynamic,然後鑄件應該在存在拳擊的情況下工作。

public static T GetValue<T>(this IDataRecord record, int index) 
{ 
    dynamic result = record[index]; 
    return (result == null || result == DBNull.Value) ? default(T) : (T)result; 
} 

順便說一句,有一個Eric Lippert blog post,簡單地處理這一個。

+0

這是一個非常好的方法來做到這一點,但是,不幸的是,我們在這裏使用3.5框架版本,所以沒有'動態'... :( – Raphael

+0

我測試過這個解決方案,它沒有工作。 'RuntimeBinderException'指出'Operator'=='不能用於'int'和'System.DBNull'類型的操作數。 – Raphael

+0

啊,我沒有嘗試可以爲null的東西,爲什麼不把'=='檢查修改爲使用'object.Equals'來代替?那很難做到。 – mquander