当在递归函数中使用 "using "时,例如在向文件中写行时,是否有一种内存效率很高的方法?

问题描述 投票:1回答:1

在递归函数中使用 "using "时,例如向文件中写行时,是否有一种内存效率很高的方法?

我读到 在循环中使用C#代码块 并提到除非万不得已,否则不要把using语句放在for循环里面。有道理,如果不需要的话,就不希望有多个'using'的实例)。 所以在for循环的情况下,如果你能把它放在外面,你就把它放在外面。

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

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

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

// 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);
    }

}
c# recursion memory-management file-io idisposable
1个回答
1
投票

链接的东西是一个案例,你是在使用 一样 资源的所有迭代。在这种情况下,每次迭代打开和关闭它都没有任何作用。只要在所有循环结束时关闭它,它就已经足够保存了。

相反的情况是,如果你使用一个 不同 资源的每一次迭代。比如说,当浏览一个文件名或完整路径的列表时,要依次打开每个文件。在这种情况下,你没有任何选择,只能在每次迭代时有一个新的文件相关实例。

递归和循环没有什么不同。你总是可以用递归代替循环,但相反的情况并不总是如此。同样的规则也适用。

  • 如果是相同的资源,你只需要把创建资源的过程移到递归函数之外。让它取一个路径(或者使用一个硬编码的路径),而不是取一个Stream。这样可以使函数保持很好的通用性
  • 如果你得到了一个不同的资源,你别无选择,只能每次递归都创建一个新的Instance使用。但是我想不出任何 "递归使用 "的情况。

如果你要遍历一个目录中的所有文件,包括所有的子目录,你会让递归函数在目录上递归(不需要非管理资源)。然后在递归函数中循环,对当前目录中的文件进行迭代(这需要非管理资源)。

编辑。

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);
}

1
投票

如果我们想用yield return来解决

你可能想重组代码,使你把递归的部分分离出来;例如用一个收益返回。

像下面这样(对不起,手头没有IDE,让我们看看这是否可行)是一个简单的方法。

如果你需要在每次切换目录时写出新的头 ( DirSearch(" + sDir + ")) ,那就可以通过不返回String来实现,只从producer中返回一个包含String directoryName, List fileNames的对象,并且每个目录只返回一次。

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返回我们可以使用带有EnumerationOptions的Directory.GetFiles来查看子目录。这让事情变得更加简单。参见: RecurseSubdirectories


0
投票

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

我只想在这里说明我使用的解决方案。

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

我还使用了一个StringBuilder,并写到控制台和文件后。

        // https://stackoverflow.com/questions/15443739/what-is-the-simplest-way-to-write-the-contents-of-a-stringbuilder-to-a-text-file
        System.IO.File.WriteAllText(@"c:\texts\filelist.txt", sb.ToString());

        Console.Write(sb.ToString());

mjwillis曾向我建议,一次只写一行到控制台是个瓶颈,christopher也提到了在最后写到文件的问题。所以我只是在调用后最后写到这两个地方。

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

© www.soinside.com 2019 - 2024. All rights reserved.