我最近被问到我们是否可以在方法中只有输入参数? 我对这个问题的直接回答是“不”,但正如我再次考虑的那样,我不确定我是否分享了正确的信息。 另外,我没有理性解释为什么.NET不允许我们只有输入参数,就像我们在实现Contravariance时使用的那样。
答案 0 :(得分:3)
您可以在此处阅读Passing Parameters。
我认为当你说“仅输入”时,你正在引用按值传递的参数。
在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; }
}
答案 1 :(得分:0)
我在这里采取了一个疯狂的飞跃,但听起来你问是否有可能阻止被调用的函数改变参数的内容(如调用者所见)。
例如:确保Bob不喜欢编程
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;
}
你的问题的答案是。 。 。这取决于参数类型!
的 和 强>
您始终可以通过模式创建不可变类型!
传递价值
在进入传递对象的引用之前,这是一个小小的澄清。
所有参数均为“按值”。制作副本并传递给被调用的函数。这有趣的地方是参数不是值,而是 points (尽管引用会更准确)到该值。
这带给我们的是。 。
<强>物件强>
我们在那里做的是Passing a reference to an Object
(“通过价值传递参考”正如@Servy指出的那样。)
如果我们传递一个原语(它不是一个引用类型),Helper中的更改将保留在帮助器中。 。
<强>基元强>
要使基元表现得像引用类型(这与您要求的完全相反),您需要使用ref keyword。
“对象”例外
有些语言允许您明确定义 immutable对象。也就是说,有直接的编译器支持来实现不变性。有关示例,请参阅链接的文章。
换句话说,如果我创建一个Person并将实例传递给一个新方法,那么该方法会受到影响。收到自己的人物副本!
为了保持有趣,.Net有一个 native 不可变类型:String。 String继承自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会很有帮助。你问为什么iCloneable? Deep Copy Vs Shallow
假设您正在编写一个外部库并希望您的库的消费者将您的对象视为不可变的...(即:我写了它,所以我可以做任何我想做的事。你在另一个手不能!)只是做#1,但使用访问修饰符比私有更宽松。例如:Internal
希望有所帮助!
更新: @Dzyann首先提供了一个类似的答案(因为这么详细,我是正确的)。如果你喜欢我的答案,一定要给他打勾。
答案 2 :(得分:-1)
我认为,你应该停止在c#中调用他们不在的东西。
您可以在MSDN上阅读有关ref
和out
个关键字的信息。
我只是让你知道原始类型
void MyVoid(int p1)
通过值传递 - 意味着它们被复制。 在这里我必须进行更正:其他类型也通过了传递给它但是作为对现有对象的引用。
如果通过引用传递所有参数,您的代码将更快但更危险,因为这样可以通过不复制来节省时间。但同样,在这种情况下,您需要具备高编码规则。
“我没有理性解释为什么.NET不允许我们只有输入参数” - 这正是.Net对传递byval
的原始类型有效的原因。
想象一下,如果它必须复制作为参数传递的所有对象结构,效率是多么低效.net我想,这就是为什么MS决定将对象类型作为参考传递