0

我正在将二进制文件读入 (t) 的绑定列表;绑定到datagridview。
文件中的每一行都代表一个事务,但我需要合并和/或过滤满足特定条件的事务。

我从机械的角度知道如何做到这一点(在添加每个项目时循环列表,添加新项目,或将数据与现有项目合并),但我正在寻找一种实践、模式、现有组件或我缺少的其他东西(我正在为要搜索的关键字绘制空白)。

如果我没有,我也不想重新发明轮子。我特别关心在某些情况下要处理的 100k 以上记录的速度和性能问题。

目前正在使用 .NET 2.0,但如果存在特别性感的解决方案,将移至 3.5。


更新 我已将解决方案更改为 3.5,因此这不再是问题。我应该指出这个项目是 VB.NET,但我可能会为这个特定的函数添加一个新的 C# 库以利用 C# 迭代器。

4

2 回答 2

1

是的,您想要 3.5,因为这为您提供了 LINQ——语言集成查询。

一点性能成本,但对于大型记录集,您可以使用 PLINQ(并行处理)来抵消这一点。

LINQ 是一种处理集合的声明式函数式方法。

您需要的概念:
- lambda 表达式() =>
- 扩展方法

考虑从一组 10,000 个字符串中,您需要前 100 个长度超过 24 个字符的字符串:

var result = source.Where(s => s.Length > 24).Take(100);

您要从一组Person对象中返回名称,但它们分为firstNamelastName属性。

var result = source.Select(person => person.firstName + person.LastName);

这返回IEnumerable<string>

从同一组你想要平均年龄:

var result = source.Average(person => person.Age);

年龄最小的10人:

var result = source.OrderBy(person => person.Age).Take(10);

每个人,按姓氏的第一个字母分组:

var result = source.GroupBy(person => person.lastName[0]);

这返回IGrouping<char, Person>

姓氏以 S 开头的最年长 25 人的姓名:

var result = source.Where(person => person.lastName.StartsWith("S"))
   .OrderByDescending(person => person.Age)
   .Take(25)
   .Select(person => person.firstName + person.lastName);

想象一下,您必须在foreach循环中编写多少代码才能完成此任务,以及在该代码中引入缺陷或错过优化的空间有多大。LINQ 语法的声明性使其更易于阅读和维护。

有一种类似于 SQL 的替代语法,但它显示了您如何真正定义针对任意对象集的查询。考虑一下你想要得到名字是“Bob”的人:

var result = 
    from person in source
    where person.firstName == "Bob"
    select person;

它看起来很奇怪,但如果你从 2.0 开始,这是有效的 C# 代码。

我唯一的警告是,一旦您使用 LINQ,您可能会拒绝再次使用 2.0。

有很多很棒的资源可用于学习 LINQ 语法——不会花很长时间。


更新

针对第 1 条评论的其他注意事项:

在 C# 2.0 中,您已经拥有了一个非常强大的工具——迭代器。

考虑:

public class Foo
{
    private IEnumerable<Record> GetRecords()
    {
        Record record = // do I/O stuff, instantiate a record

        yield return record;
    }

    public void DisplayRecords()
    {
        foreach (Record record in GetRecords())
        {
            // do something meaningful

            // display the record
        }
    }
}

那么,这有什么了不起的呢?该GetRecords()方法是一个迭代器块,yield关键字根据请求返回结果(“惰性求值”)。

这意味着当您调用 时DisplayRecords(),它会调用GetRecords(),但是一旦GetRecords()Record,它就会将其返回给DisplayRecords(),然后它可以用它做一些有用的事情。当foreach块再次循环时,执行将返回到GetRecords(),这将返回下一项,依此类推。

这样,您不必等待从磁盘读取 100,000 条记录,就可以开始排序和显示结果。

这提供了一些有趣的可能性;这在您的情况下是否有用取决于您(例如,您不想刷新网格绑定 100,000 次)。

于 2010-09-11T03:10:42.180 回答
0

听起来你想在伪 LINQ 中做这样的事情:data.GroupBy().Select(Count()).Where()- 你按一些标准分组(合并),计算每个组中的数字,然后按结果过滤。

但是,您建议您可能有太多数据无法一次全部加载到内存中,因此您希望在加载数据时进行合并。这可以用你自己的GroupByCount操作符来完成,有点像这个过度简化的版本:

public static IEnumerable<KeyValuePair<T, int>>
    GroupByCount<T>(IEnumerable<T> input)
{
    Dictionary<T, int> counts = new Dictionary<T, int>();
    foreach (T item in input)
        if (counts.ContainsKey(item))
            counts[item]++;
        else
            counts[item] = 1;
    return counts;
}

然后您将拥有data.GroupByCount().Where()并且您的数据将在加载时全部合并,因为它foreach只会在处理前一个项目后加载下一个项目。

于 2010-09-12T05:18:12.877 回答