通用参数的自定义默认值



我正在尝试编写一个通用方法来从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;
}

T string时,我试图让它进入Null string过载,但它仍然进入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);

听起来像是你试图让数据访问层掩盖上层中不存在的数据错误处理。

虽然这似乎是避免全局错误的一个好主意,但从长远来看,它可能会伤害您,因为您将来可能无法区分"记录不存在"和"记录具有我选择掩盖 null 的值"。如果您希望在不同的使用方案中为同一类型提供不同的默认值,它也不会帮助您。

我只是想把这个提出来,以便你清楚地知道走这条路也是有代价的。

就个人而言,我会选择一个T defaultValue参数,有一些Option<T>作为返回类型,或者可能是一个out T参数和一个bool返回类型。这既不会放弃编译时的安全性,也不会放弃每次使用丢失的记录处理。

在这种情况下,

我在方法中使用具有默认值的可选参数。因此,您仅在需要的地方覆盖默认行为:

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

如果您想更深入地处理列不存在或处理 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;
}

最新更新