例如,是否有一种内存有效的方法可以在递归函数中使用 'using'将行写入文件?

Is there a memory efficient way to use 'using' within a recursive function when e.g. writing lines to a file?

是否有一种内存有效的方法可以在递归函数中使用 'using' 例如将行写入文件?

我读了 ,它提到您不想将 using 语句放在 for 循环中,除非您必须这样做。 (有道理,如果不需要 'using' 的多个实例,则不想要它们)。所以在 for 循环的情况下,如果你可以把它放在外面,你就可以。

但是在这里,我有一个递归函数。所以 'using' 语句会多次 运行 即使我把它放在 for 之外。

那么放置 'using' 语句的好方法或正确方法是什么?

我不知道是否应该避免 'using',并在方法调用之前声明 StreamWriter 对象 StreamWriter writetext 并在 writetext.Dispose() 之后处理它。或者 'using' 可能有更传统的方式。也许用 'try' 包装 'main' 调用 DirSearch_basic_writetofile("c:\aaa"); 并将 Dispose 行放在最后。然后避免 'using' 。这只是一个想法。

// requires directory c:\texts
File.Delete(@"c:\texts\filelist.txt");
// list files and subdirectories of c:\aaa and write them to file "c:\texts\filelist.txt"
DirSearch_basic_writetofile("c:\aaa");

// recursive function that lists files  and directories and subdirectories,in given directory  

static void DirSearch_basic_writetofile(string sDir)
{
    Console.WriteLine("DirSearch(" + sDir + ")");
    Console.WriteLine(sDir+@"\");
    try
    {
        using (StreamWriter writetext = new StreamWriter("c:\texts\filelist.txt",true))
        {
            writetext.WriteLine("DirSearch(" + sDir + ")");
            writetext.WriteLine(sDir);

            foreach (string f in Directory.GetFiles(sDir))
            {
                Console.WriteLine(f);
                writetext.WriteLine(f);
            }
        }

        foreach (string d in Directory.GetDirectories(sDir))
        {
            DirSearch_basic_writetofile(d);
        }

    }
    catch (System.Exception excpt)
    {
        Console.WriteLine(excpt.Message);
    }

}

链接的东西是您对循环的所有迭代使用相同资源的情况。在这种情况下,每次迭代都打开和关闭它没有任何意义。只要在所有循环结束时关闭,就够省了。

相反的情况是,如果您每次迭代都使用 不同的 资源。比如说,当遍历文件名或完整路径列表时,依次打开每个文件名或完整路径。在那种情况下,你没有选择,但每次迭代都有一个新的文件相关实例。

递归与循环并没有什么不同。您总是可以用递归替换循环,但情况并非总是如此。适用相同的规则:

  • 如果是同一个资源,只需要将资源的创建移到递归函数之外即可。与其采用路径(或使用硬编码路径),不如采用 Stream。这使函数很好地通用
  • 如果您有不同的资源,您别无选择,只能使用每次递归创建一个新实例。但是我想不出任何 "recursive using" 案例。

如果您必须遍历目录中的所有文件,包括所有子目录,您将使用递归函数 recurse 遍历目录(不需要非托管资源)。然后在递归函数中循环迭代当前目录中的文件(需要非托管资源)。

编辑:

static void DirSearch_basic_writetofile(string currentDirectory, StreamWriter Output){
    //do your thing, using output as the stream you write to

    //Do recusirve calls as normal
    DirSearch_basic_writetofile(subDir, Output);
}

调用它:

using (StreamWriter OutputWriter = new StreamWriter("c:\texts\filelist.txt",true){
    DirSearch_basic_writetofile(startDirectory, OutputWriter);
}

如果我们想使用 yield return

来解决

您可能想要重组代码,以便分离出递归部分;例如产量 return.

像下面这样的东西(抱歉,手头没有 IDE,让我们看看这是否有效)是一种简单的方法。

如果您每次切换目录时都需要写出新的 header (DirSearch(" + sDir + ") ),可以通过不 returning String only from producer object 包含字符串目录名、列表文件名和 return 每个目录仅一次。

static void DirSearch_basic_writetofile(string sDir)
{
    Console.WriteLine("DirSearch(" + sDir + ")");
    Console.WriteLine(sDir+@"\");
    IEnumerable<String> producer = DirSearch_Producer(string sDir);
    try
    {
        using (StreamWriter writetext = new StreamWriter("c:\texts\filelist.txt",true))
        {
            writetext.WriteLine("DirSearch(" + sDir + ")");
            writetext.WriteLine(sDir);

            foreach (string f in DirSearch_Producer(sDir))
            {
                Console.WriteLine(f);
            }
        }
    }
    catch (System.Exception excpt)
    {
        Console.WriteLine(excpt.Message);
    }
}

public static IEnumerable<String> DirSearch_Producer(string sDir){
  foreach (string f in Directory.GetFiles(sDir))
  {
    yield return f;
  }
  foreach (string d in Directory.GetDirectories(sDir))
  {
    foreach (String f in DirSearch_Producer(d)){
        yield return f;
    }
  }
}

替代方案,不使用 yield return 我们也可以使用带有 EnumerationOptions 的 Directory.GetFiles 来遍历子目录。它使事情变得简单得多。参见:RecurseSubdirectories

我将接受克里斯托弗的回答..

我会在这里注明我使用的解决方案..

using (writetext = new StreamWriter("c:\texts\filelist.txt", true))
   DirSearch_basic_writetofile_quicker("c:\aaa");

我还使用了 StringBuilder 并在

之后写入控制台和文件
        // 
        System.IO.File.WriteAllText(@"c:\texts\filelist.txt", sb.ToString());

        Console.Write(sb.ToString());

mjwillis 曾向我建议,一次向控制台写入一行是一个瓶颈,而 christopher 最后提到了写入文件的问题。所以我只是在通话结束/之后给两者写信。

我为 StreamWriter 使用了一个静态变量,为 StringBuilder 使用了一个静态变量,这样 main() 和递归函数就可以看到它。我不想为递归函数创建一个新参数,因为我想让递归调用看起来 'simple'(一个参数)。