使用IEnumerable <ienumerable <string>&gt; </ienumerable <string>的更好方法

时间:2011-03-29 20:08:33

标签: c# ienumerable coding-style

我正在调用具有多个结果集(总是2)的存储过程,并将结果写入单独的文件(以管道分隔格式)。我无法将结果集拆分为单独的存储过程。我正在使用IDataReader和IEnumerable在这个过程中保持尽可能少的内存。

是否有更简洁的方式来使用我的IEnumerable<IEnumerable<string>>而不是使用GetEnumerator / MoveNext / Current来到内部IEnumerable<string>传递给File.AppendAllLines?

    public void Execute()
    {
        var reader = GetLines();

        using (var enumerator = reader.GetEnumerator())
        {
            enumerator.MoveNext();

            File.AppendAllLines("file1.dat", enumerator.Current);
            enumerator.MoveNext();

            File.AppendAllLines("file2.dat", enumerator.Current);
        }
    }

    public IEnumerable<IEnumerable<string>> GetLines()
    {
        Database db = DatabaseFactory.CreateDatabase("connectionStringKey");
        using (var command = db.GetStoredProcCommand("getdata_sp"))
        {
            var reader = db.ExecuteReader(command);
            yield return GetInnerEnumerable(reader);
            reader.NextResult();
            yield return GetInnerEnumerable(reader);
        }
    }

    private IEnumerable<string> GetInnerEnumerable(IDataReader reader)
    {
        while (reader.Read())
        {
            object[] rowValues = new object[reader.FieldCount];
            reader.GetValues(rowValues);
            yield return String.Join("|", rowValues);
        }
    }

7 个答案:

答案 0 :(得分:5)

为什么不是foreach循环?这是最基本的。

答案 1 :(得分:4)

就个人而言,我只会使用带有单独变量的foreach循环来跟踪要写入的文件,例如:

public void Execute()
{
    var reader = GetLines();

    int i = 0;
    foreach (var inner in reader)
    {
        if (i % 2 == 0)
            File.AppendAllLines("file1.dat", inner);
        else
            File.AppendAllLines("file2.dat", inner);
        ++i;
    }
}

答案 2 :(得分:4)

您可以使用SelectMany()来展平枚举,因为您只对值本身感兴趣。

修改:

根据评论SelectMany()根据用例不适合,所以最好使用foreach循环:

var reader = GetLines();
int index = 0;
foreach(var lines in reader)
    File.AppendAllLines(string.Format("file{0}.dat", index++%2 + 1), lines);

答案 3 :(得分:2)

也许将GetLines()的结果转换为数组并按索引访问它(因为你说总会有2个结果集)?

public void Execute()
{
    IEnumerable<string>[] rows = GetLines().ToArray();

    File.AppendAllLines("file1.dat", rows[0]);
    File.AppendAllLines("file2.dat", rows[1]);
}

答案 4 :(得分:2)

我只想将GetLines方法更改为以下

public IEnumerable<string> GetLines()
{
    Database db = DatabaseFactory.CreateDatabase("connectionStringKey");
    using (var command = db.GetStoredProcCommand("getdata_sp"))
    {
        var reader = db.ExecuteReader(command);
        for (var i = 0; i < 2; i++) 
        {  
          foreach(var cur in GetInnerEnumerable(reader))
          {
            yield return cur;
          }
          reader.NextResult();
        }
    }
}

让它返回IEnumerable<IEnumerable<string>>会给API的消费者带来不必要的负担。我的猜测是,他们都希望将此视为IEnumerable<string>

答案 5 :(得分:1)

IEnumerable隐式支持

foreach。所以:

public void Execute()
{
    var reader = GetLines();

    using (var enumerator = reader.GetEnumerator())
    {
        enumerator.MoveNext();

        File.AppendAllLines("file1.dat", enumerator.Current);
        enumerator.MoveNext();

        File.AppendAllLines("file2.dat", enumerator.Current);
    }
}

变为:

public void Execute()
{
    var reader = GetLines();

    int index = 0;

    foreach (string line in reader)
    {
        if ((index % 2) == 0)
            File.AppendAllLines("file1.dat", line);

        else
            File.AppendAllLines("file2.dat", line);

        index++;
    }
}

或者:

public void Execute()
{
    var reader = GetLines();

    var evenLines = reader.Where((str, i) => i % 2 == 0);
    var oddLines = reader.Where((str, i) => i % 2 != 0);

    foreach (string line in evenLines)
        File.AppendAllLines("file1.dat", line);

    foreach (string line in oddLines)
        File.AppendAllLines("file2.dat", line);
}

答案 6 :(得分:0)

您可以将结果压缩为Tuple<>,文件名如下:

using System.Linq;
using FileZip = System.Tuple<
    System.String,
    System.Collections.Generic.IEnumerable<
        System.String>>;

public void Execute()
{
     var files = new string[] { "file1.dat", "file2.dat" };
     var results = GetLines();

     foreach (var file in files.Zip(results, (f, r) => new FileZip(f, r)))
     {
         File.AppendAllLines(file.Item1, file.Item2);
     }
}

当然,我很确定只要你返回不同数量的行就会抛出它,但它会做你正在寻找的东西。