是否有更通用的方法来执行此类行为?

发布于 2024-12-12 04:59:50 字数 1618 浏览 0 评论 0原文

我们从 DataRow 进行大量打包和解包。是的,我们应该使用 ORM,但在那之前,这就是我们所拥有的。结果,有很多代码看起来像这样:

string username;

var temp = dr["Username"];
if (DbNull.Equals (temp))
{
    username = "Anonymous";
} else {
    username = dr["Username"].ToString();
}

最终,这成为一种模式并被翻译成辅助方法:

string username = StringExtensions.SafeParse (dr["Username"], "Anonymous");

这仍然很麻烦,并且需要各种原语的扩展方法。它还使代码变得混乱。我在 object 上创建了一个通用扩展方法,称为 As。用法如下:

string username = dr["Username"].As<string> ("Anonymous");

这个相对简单的更改得到了其他开发人员的大力支持,并且在很多地方得到了使用。我不满意的部分是潜在的性能影响。 现在,我知道没有过早的优化。我编写的代码绝对没有任何过早的优化,并且它已经足够封装,因此事后对其进行优化应该不是什么大问题。我已经对该方法进行了基准测试,在我相对普通的 2GHz 工作站上每秒进行约二百五十万次转换,我必须承认,与它节省其他开发人员的时间和我们获得的可读性提升相比,这是惊人的性能。然而,考虑到下面的代码示例,我觉得我滥用了语言功能,而且还可以做得更好。该方法在 xmldoc 中包含“HERE BE DRAGONS”,以便大声喊叫!我正在寻找一种更好的方法来避免双重拳击。为简洁起见,我省略了实际版本,它在许多情况下实际上使用了 TryParse

public static TDestination As<TDestination> (this object source, TDestination defaultValue = default(TDestination))
{
    if (source is TDestination)
        return (TDestination) source;

    if (source == null || DbNull.Equals(source))
        return defaultValue;

    if (TDestination is int)
        return (TDestination) (object) Convert.ToInt32 (source.ToString ());

    if (TDestination is long)
        return (TDestination) (object) Convert.ToInt64 (source.ToString ());

    if (TDestination is short)
        return (TDestination) (object) Convert.ToInt16 (source.ToString ());

    // and so on...
}

We do a lot of packing and unpacking from DataRow. Yes, we should be using an ORM, but until then, this is what we've got. As a result of this, there is a lot of code that looks like this:

string username;

var temp = dr["Username"];
if (DbNull.Equals (temp))
{
    username = "Anonymous";
} else {
    username = dr["Username"].ToString();
}

Eventually, this became a pattern and was translated into helper methods:

string username = StringExtensions.SafeParse (dr["Username"], "Anonymous");

This is still cumbersome, and required extension methods for all kinds of primitives. It also clutters up the code. I created a generic extension method, on object, called As<T>. Usage looks like:

string username = dr["Username"].As<string> ("Anonymous");

This relatively simple change has been met with great aplomb with the other developers, and is getting used in a LOT of places. The part I'm unhappy with are potential performance implications. Now, I'm aware of no premature optimization. I definitely wrote the code without any premature optimization, and it's encapsulated enough that optimizing it afterwards shouldn't be a big deal. I've benchmarked the method to do about two and a half million conversions per second on my relatively modest 2GHz workstation, and I must admit this is phenomenal performance, compared to the time it saves other devs and the readability boost we gain. However, given the sample of code below, I feel like I'm misusing language features and it could be done much better. The method is xmldoc'ed with "HERE BE DRAGONS" for crying out loud! I'm looking for a better way to avoid the double-boxing. The actual version, which I've omitted for brevity, actually uses TryParse in many instances.

public static TDestination As<TDestination> (this object source, TDestination defaultValue = default(TDestination))
{
    if (source is TDestination)
        return (TDestination) source;

    if (source == null || DbNull.Equals(source))
        return defaultValue;

    if (TDestination is int)
        return (TDestination) (object) Convert.ToInt32 (source.ToString ());

    if (TDestination is long)
        return (TDestination) (object) Convert.ToInt64 (source.ToString ());

    if (TDestination is short)
        return (TDestination) (object) Convert.ToInt16 (source.ToString ());

    // and so on...
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

却一份温柔 2024-12-19 04:59:50

为什么不检查您的对象是否是 IConvertible,如果是,则使用 ToType:

var convertible = source as IConvertible;
if (convertible != null)
    return (TDestination)convertible.ToType(typeof(TDestination), Thread.CurrentThread.CurrentUICulture);

Why not check if your object is IConvertible, and, if it is, use ToType:

var convertible = source as IConvertible;
if (convertible != null)
    return (TDestination)convertible.ToType(typeof(TDestination), Thread.CurrentThread.CurrentUICulture);
带刺的爱情 2024-12-19 04:59:50

根据问题中给出的示例 As 方法,您可以这样做:

public static TDestination As<TDestination>
    (this object source, TDestination defaultValue = default(TDestination))
{
    if ((source == null) || Convert.IsDBNull(source))
        return defaultValue;

    return (TDestination)source;
}

Based on the example As method given in your question, you could just do this instead:

public static TDestination As<TDestination>
    (this object source, TDestination defaultValue = default(TDestination))
{
    if ((source == null) || Convert.IsDBNull(source))
        return defaultValue;

    return (TDestination)source;
}
佼人 2024-12-19 04:59:50

每当我进行反思或检查泛型类的 T 时,我都会使用字典 Dictionary。作为价值,我总是把每次应该做的事情作为 FuncAction 放入其中。在你的情况下,我可能会这样写:

public static class MyConverter
{
    private static Dictionary<Type, Func<object, object>> _MyConverter;

    static MyConverter()
    {
        _MyConverter = new Dictionary<Type, Func<object, object>>();

        // Use the Add() method to include a lambda with the proper signature.
        _MyConverter.Add(typeof(int), (source) => Convert.ToInt32 (source.ToString()));

        // Use the index operator to include a lambda with the proper signature.
        _MyConverter[typeof(double)] = (source) => Convert.ToDouble(source.ToString());

        // Use the Add() method to include a more complex lambda using curly braces.
        _MyConverter.Add(typeof(decimal), (source) =>
        {
            return Convert.ToDecimal(source.ToString());
        });

        // Use the index operator to include a function with the proper signature.
        _MyConverter[typeof(float)] = MySpecialConverterFunctionForFloat;
    }

    // A function that does some more complex conversion which is simply unreadable as lambda.
    private static object MySpecialConverterFunctionForFloat(object source)
    {
        var something = source as float?;

        if (something != null
            && something.HasValue)
        {
            return something.Value;
        }

        return 0;
    }

    public static TDestination As<TDestination>(this object source, TDestination defaultValue = default(TDestination))
    {
        // Do some parameter checking (if needed).
        if (source == null)
            throw new ArgumentNullException("source");

        // The fast-path exit.
        if (source.GetType().IsAssignableFrom(typeof(TDestination)))
            return (TDestination)source;

        Func<object, object> func;

        // Check if a converter is available.
        if (_MyConverter.TryGetValue(typeof(TDestination), out func))
        {
            // Call it and return the result.
            return (TDestination)func(source);
        }

        // Nothing found, so return the wished default.
        return defaultValue;
    }
}

这种方法的唯一缺点是使用 object 导致(取消)装箱,如果经常重复调用该函数,这可能会导致一些性能问题在很短的时间内。但一如既往在提出要求之前先衡量

另一方面,添加更多转换器非常容易,并且由于字典的使用,您将始终是 O(1) 。

Whenever i goes into reflection or checking the T of my generic class i'm going to use a dictionary Dictionary<Type, ???>. As value i always put something in that should be done for each time as Func or Action. In your case i would write it maybe in that way:

public static class MyConverter
{
    private static Dictionary<Type, Func<object, object>> _MyConverter;

    static MyConverter()
    {
        _MyConverter = new Dictionary<Type, Func<object, object>>();

        // Use the Add() method to include a lambda with the proper signature.
        _MyConverter.Add(typeof(int), (source) => Convert.ToInt32 (source.ToString()));

        // Use the index operator to include a lambda with the proper signature.
        _MyConverter[typeof(double)] = (source) => Convert.ToDouble(source.ToString());

        // Use the Add() method to include a more complex lambda using curly braces.
        _MyConverter.Add(typeof(decimal), (source) =>
        {
            return Convert.ToDecimal(source.ToString());
        });

        // Use the index operator to include a function with the proper signature.
        _MyConverter[typeof(float)] = MySpecialConverterFunctionForFloat;
    }

    // A function that does some more complex conversion which is simply unreadable as lambda.
    private static object MySpecialConverterFunctionForFloat(object source)
    {
        var something = source as float?;

        if (something != null
            && something.HasValue)
        {
            return something.Value;
        }

        return 0;
    }

    public static TDestination As<TDestination>(this object source, TDestination defaultValue = default(TDestination))
    {
        // Do some parameter checking (if needed).
        if (source == null)
            throw new ArgumentNullException("source");

        // The fast-path exit.
        if (source.GetType().IsAssignableFrom(typeof(TDestination)))
            return (TDestination)source;

        Func<object, object> func;

        // Check if a converter is available.
        if (_MyConverter.TryGetValue(typeof(TDestination), out func))
        {
            // Call it and return the result.
            return (TDestination)func(source);
        }

        // Nothing found, so return the wished default.
        return defaultValue;
    }
}

The only drawback of this approach that the usage of object leads to (un)boxing which maybe makes some performance problems if the function is called repeatedly for often in a very short time. But as always measure before claiming.

On the other side it is quite easy to add further converter and you'll be always O(1) due to the usage of the dictionary.

掐死时间 2024-12-19 04:59:50

如何为 datarow Field 属性定义一个扩展方法,您可以在其中提供与 Field 类型相同的自己的 null 值,如下所示:

        public static T Field<T>(this DataRow row, string columnName, T nullValue) { return !row.IsNull(columnName) ? row.Field<T>(columnName) : nullValue; }

How about defining an extension method for the datarow Field property where you can supply your own null value of the same type as the Field, like so:

        public static T Field<T>(this DataRow row, string columnName, T nullValue) { return !row.IsNull(columnName) ? row.Field<T>(columnName) : nullValue; }
海之角 2024-12-19 04:59:50

我同意对 Field 函数进行变体是最好的方法,但如果您担心性能,则不要使用 IsNull() 或实际的 Field 函数,因为它们会执行大量冗余检查。以下方法就是您真正需要的。

public static T Field<T>(this DataRow row, string columnName, T nullValue)
{
  object value = row[columnName];
  return ((DBNull.Value == value) ? nullValue : (T)value);
}

这消除了发生额外装箱的需要,并且如果您小心使用 nullValue 参数,您通常可以不必在调用函数时显式指定 T。双赢。

I agree that making a variation of the Field function is the best way to go, but if you are concerned about performance then don't use IsNull() or the actual Field function as these perform a lot of redundant checking. The following method is all you really need.

public static T Field<T>(this DataRow row, string columnName, T nullValue)
{
  object value = row[columnName];
  return ((DBNull.Value == value) ? nullValue : (T)value);
}

This removes the need for additional boxing to occur and if you're careful with how you use the nullValue parameter you could generally forego having to specify T explicitly when calling the function. win-win.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文