C# 中的“仅输入”参数
本文关键字:仅输入 参数 输入 中的 | 更新日期: 2023-09-27 18:35:46
我最近被问到我们是否可以在方法中使用仅输入参数?我对这个问题的直接回答是"否",但当我再次思考时,我不确定我是否分享了正确的信息。此外,我没有一个合理的解释为什么 .NET 不允许我们有一个仅输入参数,就像我们在实现逆变时使用的那样。
您可以在此处阅读有关传递参数的信息。
我认为,当您说"仅输入"时,您指的是按值传递的参数。
在 c# 中,通常按值传递参数。当您传递对象时,您正在按值传递"指针"。因此,如果您更改指针指向的内容,则外部变量将不会更改。但是,如果您更改对象的内容,因为两个"指针"都指向同一对象,因此都可以看到更改。
因此,您可以通过"value"传递所有内容,但是如果您要传递对象,则必须创建它的副本以避免它被您调用的方法修改。
你可以在这里看到一个小的测试示例来理解我的意思。
[TestMethod]
public void TestMethod()
{
var john = new Person() { Name = "John" };
var tom = new Person() { Name = "Tom" };
var person1 = john;
var person2 = tom;
SwapPersonsMethod1(person1, person2);
//Person1 is still John
Assert.AreEqual(person1, john);
//Person2 is still Tom
Assert.AreEqual(person2, tom);
SwapPersonsMethod2(ref person1, ref person2);
//Person1 is still Tom
Assert.AreEqual(person1, tom);
//Person2 is still John
Assert.AreEqual(person2, john);
UpdateName(person1, "Tomas");
//Person1 is pointing to var tom, and its name now is Tomas.
Assert.AreEqual(person1.Name, "Tomas");
Assert.AreEqual(tom.Name, "Tomas");
SwapPersonsMethod3(person1, person2, "Jonathan");
//Person1 is still Tom
Assert.AreEqual(person1, tom);
//Person2 is still John
Assert.AreEqual(person2, john);
//John name has changed to Jonathan
Assert.AreEqual(person2.Name, "Jonathan");
Assert.AreEqual(john.Name, "Jonathan");
}
private void UpdateName(Person person, string name)
{
person.Name = name;
}
private void SwapPersonsMethod1(Person person1, Person person2)
{
var aux = person1;
person1 = person2;
person2 = aux;
}
private void SwapPersonsMethod2(ref Person person1, ref Person person2)
{
var aux = person1;
person1 = person2;
person2 = aux;
}
private void SwapPersonsMethod3(Person person1, Person person2, string name)
{
var aux = person1;
person1 = person2;
person2 = aux;
UpdateName(person1, name);
}
public class Person
{
public string Name { get; set; }
}
我在这里飞跃,但听起来你在问是否可以阻止被调用的函数更改参数的内容(如调用者所见)。
例如:确保鲍勃不喜欢编程
public void Run()
{
Person Bob = New Person();
Bob.LikesToProgram = false;
Helper(Bob);
Console.WriteLine("Bob likes to program = " + Bob.LikesToProgram);
//Output: Turns out Bob likes to program!!!
}
public void Helper(Person input)
{
input.LikesToProgram = true;
}
您的问题的答案是。 。 。这取决于参数类型!
和
您始终可以通过模式创建不可变类型!
按值
传递在进入传递对象的引用之前,这是一个一些澄清。所有参数都是"按值"的。 创建副本并将其传递给被调用的函数。 有趣的是,如果参数不是值,而是指向(尽管引用会更准确)值。
这就把我们带到了.. .
对象
我们在那里做的是传递对对象的引用。
(正如@Servy所指出的,"按值传递引用"。
如果我们传递了一个基元(不是引用类型),帮助程序中的更改将保留在帮助程序中。
原
要使基元的行为类似于引用类型(这与您所询问的完全相反),您需要使用 ref 关键字。
"对象"例外
某些语言允许您显式定义不可变对象。 也就是说,有直接的编译器支持来实现不可变性。 有关示例,请参阅链接的文章。
换句话说,如果我创建一个 Person 并将实例传递给新方法,该方法将受到影响。 接收自己的 Person 副本!
为了保持有趣,.Net 有一个本机不可变类型:字符串。 字符串继承自 Object,但是当您更改字符串时,它会导致创建字符串的新实例。 您的参数现在指向对象的另一个实例。
需要明确的是,通过模式还有其他不可变的对象,但直接支持意味着你永远不需要编写
public void Run()
{
String foo = "foo";
Helper(foo);
}
public void Helper(String input)
{
input = new String(Foo + "Bar");
//Or CopyTo as seen in Arrays, etc.
}
但这能做到吗? - 是的!!
总有办法的!
将所有属性设为只读,并且只允许在实例化时设置值(通过构造函数)。想要更改值?您需要创建一个新实例!
如果使用此策略,则对象实现 iCloneable 会很有帮助。 你问为什么是可克隆的?深拷贝与浅拷贝假设您正在编写一个外部库,并希望您的库的使用者将您的对象视为不可变的......(即:我写了它,所以我可以做任何我想做的事。 另一方面,你不能! 只需执行与 #1 相同的操作,但使用访问修饰符比私有修饰符更宽松。 例如:内部
希望对您有所帮助!
更新:@Dzyann首先提供了类似的答案(我太啰嗦了)。 如果你喜欢我的回答,一定要给他打勾。
,你应该停止调用 c# 中没有的东西。您可以在 MSDN 上阅读有关ref
和out
关键字的信息。我只是让你知道基元类型
void MyVoid(int p1)
按值传递 - 表示它们被复制。在这里我必须进行更正:其他类型也通过 Val 传递,但作为对现有对象的引用。
如果通过引用传递所有参数,您的代码会更快但更危险,因为这样您就可以通过不复制它来节省时间。但同样,在这种情况下,您将需要有很高的编码纪律。
"对于为什么 .NET 不允许我们仅输入参数,我没有合理的解释" - 这正是 .Net 对于传递byval
的基元类型有效的功能。
想象一下,如果 .net 必须复制作为参数传递的所有对象结构,它会有多低效?我想,这就是为什么MS决定将对象类型作为引用传递的原因