在 C# 中使用“out 参数”递归
本文关键字:out 参数 递归 | 更新日期: 2023-09-27 18:32:06
我可以将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);
}
我决定重写我的答案,以更直接地说明为什么这里不需要使用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++;
}
由于我们仍在按值传递引用类型,因此方法Foo
中的 type
值仍将为 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++;
}
现在,Foo
方法中type
的值将为 1。这是因为我们重用了引用指向的相同存储位置,而不是在内存中创建新位置。
那么这一切如何适用于out
呢?请记住,out
与具有不同用法的对称ref
相同。不同之处在于,使用 ref
时,您可以保留该方法而不初始化引用,而使用 out
时,必须在方法返回之前显式初始化它。
因此,在您在此处使用 out
的情况下,完全没有必要,因为您已经有现有的引用语义为您工作。我希望这能澄清一点。
您在代码示例中滥用了关键字 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
会返回假。
另一方面,如果Bar
是这样的:
public void Bar(List<int> list)
{
list = new List<int>();
list.Add(2);
}
然后Foo
会返回 true。有关更多详细信息,请参阅out
。
您的代码将在不使用 out
的情况下按原样工作。而且,事实上,不会编译 - 并且使其编译会增加您尝试执行的操作的不必要的复杂性。不要在您的情况下使用out
。