繁体   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