优化For循环结果

本文关键字:结果 循环 For 优化 | 更新日期: 2023-09-27 18:18:46

我目前使用这段代码来检查DataReader是否有列存在:

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

我经常重用这段代码,因此它在For循环中连续运行。

我能优化这个吗?例如,如果一列不存在,那么这意味着所有结果都是相同的,我不必再次遍历所有内容。同样,如果它是存在的,我知道它是存在的,不需要再次进行循环。

谢谢!

优化For循环结果

该函数的代码很好。例如,你应该检查你的调用代码,不要在每次迭代你的DataReader或DataTable时都调用这个。

你应该调用这个方法一次之前迭代所有的记录。表或游标的每一行不具有相同列的可能性很小。

主要问题在于调用代码…但是你可以通过改变你的方法来创建并返回一个HashSet<string>:

来避免总共调用该方法不止一次
public static class DataRecordExtensions
{
    public static ISet<string> GetFieldNames(this IDataRecord dr, StringComparer comparer)
    {
        var sequence = Enumerable.Range(0, dr.FieldCount)
                                 .Select(i => dr.GetName(i));
        return new HashSet<string>(sequence, comparer);
    }
}

你可以用你的第一个记录调用这个方法,传递StringComparer.InvariantCultureIgnoreCase作为比较器,你会得到一个ISet<string>,你可以便宜地测试它是否包含一个特定的字段名。

尴尬的部分是可能只调用一次。您可能需要这样的内容:

ISet<string> fields = null;
while (reader.Read())
{
    var record = ...;
    if (fields == null)
    {
        fields = record.GetFieldNames(StringComparer.InvariantCultureIgnoreCase);
    }
    // Use fields and record here
}

您可能还想考虑返回Dictionary<string, int>而不是ISet<string>,如果您还需要字段数字

一种选择是将列名存储在字典中,然后可以使用它非常快速地查找列名。

如果您正在使用扩展方法,则需要使用ConditionalWeakTable来存储和检索给定数据阅读器的字典。首先检查它是否存在,如果不存在,使用此方法缓存并存储它。

请记住,第一次调用这个方法时,它可能会比现在慢,因为你不能短路这个方法(因为你需要缓存所有的列名)。如果这是一个问题,你可以使用混合方法缓存到你找到你之后的列,然后退出,下次如果你没有在你的缓存中找到列,继续搜索和添加到缓存中的任何新列,再次退出一旦你找到你之后的列(或者你得到列的末尾)。

我应该说,这三种方法中哪一种是最好的(你目前的方法加上这两种)在很大程度上取决于:

  1. 为给定的数据读取器调用此方法的次数
  2. 数据阅读器中有多少列,和
  3. 按什么顺序搜索哪些列

例如,如果您的阅读器中有100列,并且搜索位置为97、98和99的列,那么我上面的第一种方法可能是最好的(第一次使用时缓存所有列)。但是,如果在位置1和100之间搜索列,那么混合方法可能更好。只要在任何地方搜索一次数据阅读器,您就可以更好地使用当前的方法!