我可以在递归方法中使用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);
}
答案 0 :(得分:5)
我决定重写我的答案,以更直接地解释为什么在这里不需要使用out
。我写这篇文章是相当冗长的,因为我认为你的问题的根源更多的是不理解传递值,传递引用之间的差异,以及引用类型之间的常见混淆通过引用传递。另请注意,这不仅仅是递归。
在C#中,引用类型默认按值传递。很多人可以将参考类型与通过引用传递混淆,但是有一个重要的区别。为了使自己更清楚,我将按原样引用引用类型,并使用ref
或out
关键字通过引用传递。
按值传递引用类型,因为它是对内存中某些存储位置的引用,允许您创建和保留更改。举个例子。
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。这是引用类型的本质。如果type
是struct
,则它在Foo方法中的值仍然为0,因为创建了一个副本而不是保持对该对象的引用。
现在我希望你理解按值传递引用类型的语法,让我们实际谈论通过引用传递引用类型 。这是使用out
和ref
关键字完成的。请立即注意,在CLR中,out
在技术上不存在,只有ref
。 out
实际上在元数据中表示为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++;
}
因为我们仍然按值传递我们的引用类型,方法type
中Foo
的值仍然是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
。