簡體   English   中英

通用參數的自定義默認值

[英]Custom default value for generic parameter

我正在嘗試編寫一種通用方法來從SQLDataReader讀取數據。 除非我想為某些數據類型獲取自定義默認值,否則它的效果非常好。 例如,對於string我想要得到string.Empty而不是null

public static T SafeGetValue<T>(SqlDataReader dr, string columnName)
{
    T returnValue = default(T);
    var value = dr[columnName];

    if (value != null && value != DBNull.Value)
    {
        returnValue = (T)value;
    }
    else
    {
        returnValue.Null();
    }

    return returnValue;
}

public static object Null(this object o)
{
    return null;
}

public static string Null(this string stringValue)
{
    return string.Empty;
}

Tstring ,我試圖將其轉到string Null重載,但它仍然轉到object重載。 有什么辦法嗎?

Null方法靜態綁定到對象版本。 我認為您最簡單的選擇是使用開關或字典來處理特殊情況。 像這樣:

private static readonly Dictionary<Type, Object> _NullValues = new Dictionary<Type, Object>()
{
    { typeof(String), String.Empty }
};

public static object Null<T>(this T o)
{
    object ret;
    return _NullValues.TryGetValue(typeof(T), out ret)
        ? ret : default(T);
}

為了擴展我在上面給出的注釋,我建議傳遞一個默認值,但是您也可以傳遞一個生成器,您只需要在一個地方進行更改即可。 例如:

public class DefaultValueGenerator<T>
{
    public virtual T Default()
    {
        return default(T);
    }
}

public class StringValueGenerator : DefaultValueGenerator<string>
{
    public override string Default()
    {
        return "";
    }
}

public static T SafeGetValue<T>(SqlDataReader dr, string columnName, 
    DefaultValueGenerator<T> defaultGenerator)
{
    //snip
    returnValue = defaultGenerator.Default();
}

並像這樣使用它:

var stringDefaultGenerator = new StringValueGenerator();
var x = SafeGetValue<string>(dr, "column", sg);

對於這種情況,我在默認值的方法中使用了可選參數。 因此,僅在需要的地方覆蓋默認行為:

public static T GetValueOrDefault<T>(SqlDataReader dr, string columnName, T defaultValue = default(T))
{
     T returnValue = default(T);
     var value = dr[columnName];

     return value == null ? (T) defaultValue : value;
}

如果您想更深入地處理不存在column或處理Nullable類型的情況-這是我在項目中使用的部分代碼:

public static T GetValueOrDefault<T>(IDataRecord row, string fieldName, T defaultValue = default(T))
{
    if (HasColumn(row, fieldName))
    {
        int ordinal = row.GetOrdinal(fieldName);
        object value = row.GetValue(ordinal);

        return row.IsDBNull(ordinal)
            ? defaultValue
            : ConvertValue<T>(value);
    }
    else
    {
        return defaultValue;
    }
}

public static bool HasColumn(IDataRecord row, string columnName)
{
    for (var i = 0; i < row.FieldCount; i++)
    {
        if (row.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
        {
            return true;
        }
    }
    return false;
}

public static T ConvertValue<T>(object value)
{
    var type = typeof(T);
    type = Nullable.GetUnderlyingType(type) ?? type;

    var result = Convert.ChangeType(value, type);
    return (T)result;
}

這聽起來像是您在嘗試使數據訪問層掩蓋上層的數據不存在錯誤處理。

雖然這似乎是避免全局錯誤的好主意,但從長遠來看,這可能會傷害您,因為您可能無法區分“記錄不存在”和“記錄具有我選擇掩蓋空值的值”,未來。 如果您想在不同的使用情況下為同一類型提供不同的默認值,它也無濟於事。

我只是想提出這一點,以便使您清楚地知道走這條路也是有代價的。

就個人而言,我將使用T defaultValue參數,將一些Option<T>作為返回類型,或者將out T參數與bool返回類型一起使用。 這既不會放棄編譯時的安全性,也不會放棄每次使用丟失記錄的處理。

暫無
暫無

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

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