2

编辑:我错过了一个关键点:.NET 2.0

考虑一下我有一个未排序项目列表的情况,为了简单起见,如下所示:

class TestClass
{
    DateTime SomeTime;
    decimal SomePrice;

    // constructor
}

我需要创建一个类似报告的输出,其中累积了每天的总价格。每个项目应该有一行,后面是适当的摘要行。

拿这个测试数据:

List<TestClass> testList = new List<TestClass> { 
new TestClass(new DateTime(2008,01,01), 12),
new TestClass(new DateTime(2007,01,01), 20),
new TestClass(new DateTime(2008,01,01), 18)
};

所需的输出将是这样的:

2007-01-01: 
20
Total: 20

2008-01-01: 
12
18
Total: 30

处理这种情况的最佳方法是什么?对于这样的列表,我将为 TestClass 实现 IComparable 接口,以便可以对列表进行排序。

要创建报告本身,可以使用类似的东西(假设我们有方法来完成诸如累积价格、跟踪当前日期等任务):

for (int i=0;i<testList.Count;i++)
{
    if (IsNewDate(testList[i]))
    {
        CreateSummaryLine();
        ResetValuesForNewDate();
    }

    AddValues(testList[i]);
}

// a final summary line is needed to include the data for the last couple of items.
CreateSummaryLine();

这行得通,但就第二个“CreateSummaryLines”而言,我有一种奇怪的感觉。

您以什么方式处理这种情况(特别是考虑到这样一个事实,我们需要使用 List<> 项目而不是预先分类的 Dictionary 或类似的东西)?

4

6 回答 6

3

好的,所以如果你不能使用 LINQ:

(我使用 var 来节省空间,但如果需要,它很容易转换为 C# 2.0...)

var grouped = new SortedDictionary<DateTime, List<TestClass>>();
foreach (TestClass entry in testList) {
  DateTime date = entry.SomeTime.Date;
  if (!grouped.ContainsKey(date)) {
    grouped[date] = new List<TestClass>();
  }
  grouped[date].Add(entry);
}

foreach (KeyValuePair<DateTime, List<TestClass>> pair in testList) {
  Console.WriteLine("{0}: ", pair.Key);
  Console.WriteLine(BuildSummaryLine(pair.Value));
}
于 2008-10-17T12:55:41.317 回答
2

关键问题:您是否使用 .NET 3.5,从而允许使用 LINQ?如果是这样,您可以按 SomeTime.Date 分组,然后分别处理每个日期。

如果您有大量数据,您可能会发现我的Push LINQ项目很有用 - 但否则直接 LINQ to Objects 是您最好的选择。

于 2008-10-17T12:46:44.890 回答
2

[编辑] 由于您将 .NET 2.0 与 C# 3.0 一起使用,因此您可以使用LINQBridge来启用它。

LINQ;就像是:

        var groups = from row in testList
                  group row by row.SomeTime;
        foreach (var group in groups.OrderBy(group => group.Key))
        {
            Console.WriteLine(group.Key);
            foreach(var item in group.OrderBy(item => item.SomePrice))
            {
                Console.WriteLine(item.SomePrice);
            }
            Console.WriteLine("Total" + group.Sum(x=>x.SomePrice));
        }
于 2008-10-17T12:46:54.813 回答
1

您使用的是哪个版本的 C#/.NET?如果您是最新的,请查看 LINQ。这允许对您的数据进行分组。在您的情况下,您可以按日期分组:

var days = from test in testList group test by test.SomeTime;
foreach (var day in days) {
    var sum = day.Sum(x => x.SomePrice);
    Report(day, sum);
}

这样,您可以报告每个 的值列表day,以及当天的金额。

于 2008-10-17T12:46:55.967 回答
0

您想要的输出感觉就像您想要创建一个代表您的摘要数据的数据结构。您想将日期与 TestClass 对象列表配对,然后收集这些对象。您可以使用 HashSet 或 Dictionary 作为集合。

总结一下,pair数据结构可以为你实现ToString。

于 2008-10-17T12:47:29.337 回答
0

无论您如何生成报告输出,如果您要在应用程序中定期执行此操作,请考虑组合一个报告接口并为此接口实现类,以便将用于在一个位置创建报告的逻辑本地化,而不是将代码分散到在整个应用程序中创建报告。以我的经验,应用程序通常需要(或不断增长)对多个报告以及最终的多种格式的需求。

如果您只有一份报告,请在编写第二份报告之前不要担心,然后开始研究一种更易于扩展和维护的报告架构。

例如(并使用@Marc Gravell 的代码):

public interface IReport
{
   string GetTextReportDocument();
   byte[] GetPDFReportDocument();  // needing this triggers the development of interface
}

public class SalesReport : IReport
{
   public void AddSale( Sale newSale )
   {
       this.Sales.Add(newSale);  // or however you implement it
   }

   public string GetTextReportDocument()
   {
       StringBuilder reportBuilder = new StringBuilder();
       var groups = from row in this.Sales
                    group row by row.SomeTime.Date;
       foreach (var group in groups.OrderBy(group => group.Key))
       {            
          reportBuilder.AppendLine(group.Key);
          foreach(var item in group.OrderBy(item => item.SomePrice))            
          {
             reportBuilder.AppendLine(item.SomePrice);
          }
          reportBuilder.AppendLine("Total" + group.Sum(x=>x.SomePrice));
      }

      return reportBuilder.ToString();
   }

   public byte[] GetPDFReportDocument()
   {
        return PDFReporter.GenerateDocumentFromXML( this.ConvertSalesToXML() );
   }

...其余的留给读者练习...

于 2008-10-17T13:52:50.733 回答