C#中带“out参数”的递归

时间:2012-04-25 03:06:20

标签: c# recursion

我可以在递归方法中使用out参数吗?如果可能,我该如何使用以下代码进行操作?

private void PrepareDir(out List<_File> listFiles,string root,string dirPath) {
    DirectoryInfo dirRoot = new DirectoryInfo(dirPath);
    FileInfo [] Files = dirRoot.GetFiles();
    dirPath = dirPath.Substring(root.Length);

    foreach (FileInfo file in Files) {
        _File _file = new _File();
        _file.Name = dirPath + "\\" + file.Name;
        _file.Path = file.FullName;
        _file.Size = file.Length;
        listFiles.Add(_file);
    }

    foreach (DirectoryInfo dir in dirRoot.GetDirectories()) {
        PrepareDir(out listFiles, root, dir.FullName);
    }
}

private void btnButton1_Click(object sender, EventArgs e) {
    List<_File> Files = new List<_File>();
    PrepareDir(out Files,currAddress, currAddress);
}

2 个答案:

答案 0 :(得分:5)

我决定重写我的答案,以更直接地解释为什么在这里不需要使用out。我写这篇文章是相当冗长的,因为我认为你的问题的根源更多的是不理解传递值,传递引用之间的差异,以及引用类型之间的常见混淆通过引用传递。另请注意,这不仅仅是递归。

在C#中,引用类型默认按值传递。很多人可以将参考类型与通过引用传递混淆,但是有一个重要的区别。为了使自己更清楚,我将按原样引用引用类型,并使用refout关键字通过引用传递。

按值传递引用类型,因为它是对内存中某些存储位置的引用,允许您创建和保留更改。举个例子。

public class MyRefType
{
    public int Value { get; set; }
}

public void Foo() {
    MyRefType type = new MyRefType();
    AddOne(type);
}

public void AddOne(MyRefType type) {
    type.Value++;
}

这里会发生的是,班级type现在的值为1。这是引用类型的本质。如果typestruct,则它在Foo方法中的值仍然为0,因为创建了一个副本而不是保持对该对象的引用。

现在我希望你理解按值传递引用类型的语法,让我们实际谈论通过引用传递引用类型 。这是使用outref关键字完成的。请立即注意,在CLR中,out在技术上不存在,只有refout实际上在元数据中表示为ref,并且应用了特殊属性,它们应该被视为相同。

现在假设我们想在添加之前在AddOne方法中重新分配我们的引用类型。

public class MyRefType
{
    public int Value { get; set; }
}

public void Foo() {
    MyRefType type = new MyRefType();
    AddOne(type);
}

public void AddOne(MyRefType type) {
    type = new MyReferenceType();
    type.Value++;
}

因为我们仍然按值传递我们的引用类型,方法typeFoo的值仍然是0.我们所做的只是在内存中初始化另一个存储位置,而不是重新分配原始存储位置)。但我们希望实际通过引用传递我们的引用类型,所以我们应该这样做:

public class MyRefType
{
    public int Value { get; set; }
}

public void Foo() {
    MyRefType type = new MyRefType();
    AddOne(ref type);
}

public void AddOne(ref MyRefType type) {
    type = new MyReferenceType();
    type.Value++;
}

现在type方法中Foo的值将为1.这是因为我们重用了引用所指向的相同存储位置,而不是在内存中创建新位置。

那么这一切如何适用于out?请记住,out与具有不同使用语义的ref相同。不同之处在于,使用ref,您可以在不初始化引用的情况下保留方法,而使用out时,必须在方法返回之前显式初始化它。

因此,在此处使用out的情况下,完全没有必要,因为您已经有适用于您的现有引用语义。我希望这有点清楚。

答案 1 :(得分:2)

您在代码示例中滥用了关键字out。您的List<_File>作为引用类型传递,对该列表所做的任何更改都将影响调用者的列表。您只需要在重新分配呼叫者的标识符时传递out参数。

考虑:

public bool Foo()
{
  List<int> list = new List<int>();
  list.Add(1);
  Bar(out list);
  return list.Contains(1);
}

public void Bar(out List<int> list)
{
  list = new List<int>();
  list.Add(2);
}

Foo会返回false。

另一方面,如果Bar是这样的话:

public void Bar(List<int> list)
{
  list = new List<int>();
  list.Add(2);
}

然后Foo将返回true。有关详细信息,请参阅out

您的代码将在不使用out的情况下正常运行。而且,事实上,不会编译 - 并且使其编译会增加你想要做的事情的不必要的复杂性。不要在你的情况下使用out