0

最近有人问我是否可以在方法中使用仅输入参数?我对这个问题的直接回答是“不”,但当我再次思考时,我不确定我是否分享了正确的信息。另外,我没有合理的解释为什么 .NET 不允许我们使用仅输入参数,就像我们在实现逆变时使用的那样。

4

3 回答 3

3

您可以在此处阅读有关传递参数的信息。

我假设当您说“仅输入”时,您指的是按值传递的参数。

在 c# 中,您通常按值传递参数。当您传递一个对象时,您正在按值传递一个“指针”。因此,如果您更改指针指向的内容,则外部变量不会更改。但是如果你改变对象的东西,因为两个“指针”都指向同一个对象,所以都会看到变化。

因此,您可以通过“值”传递所有内容,但如果您传递对象,则必须创建它的副本以避免它被您调用的方法修改。

您可以在此处查看一个小型测试示例以了解我的意思。

[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; }
    }
于 2013-10-17T19:13:12.787 回答
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;
}  

你的问题的答案是。. . 这取决于参数类型!
并且
您始终可以通过模式创建不可变类型!


按值传递
这是在开始传递对象引用之前的一点说明。所有参数都是“按值”。制作一个副本并将其传递给被调用的函数。有趣的是,如果参数不是一个值,而是指向该值(尽管引用会更准确)。
这将我们带到 . . .

Objects
我们正在做的是传递对Object的引用。
(正如@Servy 指出的那样,“按值传递引用”
。) 如果我们传递了一个原语(它不是引用类型),Helper 中的更改将保留在 helper 中。. .

基元
要使基元表现得像引用类型(这与您所要求的完全相反),您需要使用ref 关键字

“对象”异常
某些语言允许您显式定义 不可变对象。也就是说,有直接的编译器支持来实现不变性。有关示例,请参阅链接文章。

换句话说,如果我创建一个 Person 并将实例传递给一个新方法,该方法就会生效。收到自己的副本的人!

为了保持有趣,.Net 有一个原生的不可变类型: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.
}

但是能做到吗?- 是的!!!


总会有办法的!

  1. 将所有属性设为只读,并且只允许在实例化时设置值(通过构造函数)。想要更改值?您需要创建一个新实例!

    如果您使用此策略,则对象实现iCloneable会很有帮助。为什么要问 iCloneable?深拷贝与浅拷贝

  2. 假设您正在编写一个外部库并希望您是库的使用者将您的对象视为不可变的......(即:我写了它,所以我可以做任何我想做的事情。另一方面,你不能!)只需做与 #1 相同的操作,但使用比私有更宽松的访问修饰符。例如:内部

希望有帮助!

更新: @Dzyann 首先提供了一个类似的答案(因为我太冗长了)。如果您喜欢我的回答,请务必也给他打勾。

于 2013-10-17T19:45:29.007 回答
-1

我认为,你应该停止调用 C# 中没有的东西。您可以在 MSDN 上阅读有关refout关键字的信息。我只是让你知道原始类型

void MyVoid(int p1)

按值传递 - 意味着它们被复制。在这里我必须进行更正:其他类型也传递了 byVal 但作为对现有对象的引用。

如果您通过引用传递所有参数,您的代码会更快但更危险,因为这样您就可以通过不复制它来节省时间。但同样,在这种情况下,您将需要具有高度的编码纪律。

“我没有合理的解释为什么 .NET 不允许我们使用仅输入参数”——这正是 .Net 对传递的原始类型有效的原因byval

想象一下,如果必须复制所有作为参数传递的对象结构,.net 会有多低效?我想,这就是为什么 MS 决定将对象类型作为参考传递

于 2013-10-17T19:17:13.507 回答