听起来您误解了ref/out - 当您使用传递给方法的引用类型(类)时,他们为您做的唯一一件事就是允许该方法分配一个新的将对象实例传递给参数变量,调用代码会看到更改。它们绝对不需要更改传递变量的某些数据内容。
我个人认为围绕这个问题的很多困惑来自于“通过引用传递”这个短语,其中包含一个以ref 开头的单词,巧合的是ref 中的三个字母
当你创建一个引用类型实例时:
Person john = new Person(){ Name = "John", Age = 27 };
你可以把它传递给一个方法:
void ChangeName(Person p){
p.Name = "Fred";
}
您无需指定 ref 即可更改此名称并将其保留
ChangeName(john);
Console.Print(john.Name); //Fred
在 C# 内部,假设您创建 John 并且 C# 分配一些内存,将名称放入其中并为您提供变量引用:
john --> { Name: "John", Age: 27 }
您调用该方法,而 C# 暂时创建另一个名为 p 的引用(因为该方法需要一个人 p)
john --> { Name: "John", Age: 27 } <-- p
新的引用,相同的对象。您通过 p.Name 更改名称,原始对象受到影响
john --> { Name: "Fred", Age: 27 } <-- p
如果我们使用ref C# 没有创建新的引用,想象它只是暂时重命名我们现有的引用,然后在方法完成时将其重新命名
void ChangeName(ref Person p){
p.Name = "Fred";
}
john --> { Name: "John", Age: 27 } //start with
p --> { Name: "John", Age: 27 } //when start calling the method
p --> { Name: "Fred", Age: 27 } //method changes the person name
john --> { Name: "Fred", Age: 27 } //method exits, name is changed
我们在这里从来不需要参考
不同的场景。人是不可变的。更改名称的唯一方法是将其换成一个全新的对象
void ChangeName(Person p){
p = new Person { Name = "Fred", Age: 0 };
}
现在,this 需要参考。看看当我们没有 ref 时会发生什么:
john --> { Name: "John", Age: 27 }
john --> { Name: "John", Age: 27 } <-- p
john --> { Name: "John", Age: 27 } p --> { Name: "Fred", Age: 0 }
john --> { Name: "John", Age: 27 }
调用该方法会为旧对象创建一个新引用,然后说 p = new... 会将该新引用重新附加到某个新对象,该新对象会在方法结束时立即销毁,没有任何变化
带参考:
john --> { Name: "John", Age: 27 }
p --> { Name: "John", Age: 27 }
p --> { Name: "Fred", Age 0 }
john --> { Name: "Fred", Age 0 }
临时重命名该引用意味着当我们创建一个新对象并分配它时,原始调用代码也会遇到new
仅当您将传入的事物设置为 new 并希望调用者看到新事物时,您才需要 ref。大多数时候我们不需要它
脚注:out 与ref 类似,只是编译器会检查该方法是否确实分配了一个值。将变量传递给方法时,您需要使用“out”或“ref”,因为它会引起您的注意“此方法可能会覆盖您传入的任何内容”(对于 ref)或“此方法肯定会覆盖您的任何内容传入”(不传)
现代 .net 还具有很少使用的 in 修饰符,它使编译器检查您没有将任何 new 分配给传入的变量(存在非引用场景的风险,其中Fred在方法结束时丢失)