【问题标题】:C# implicitly updates parameter passed to method with no return type and no out or ref usageC# 隐式更新传递给方法的参数,没有返回类型,也没有 out 或 ref 使用
【发布时间】:2014-01-23 00:35:29
【问题描述】:

这对我来说太时髦了,显然开发人员应该使用关键字 out 但我不敢相信这甚至可以工作:

public BusinessResponse<MyResultType> MyMethod(){

    BusinessResponse<MyResultType> res = new BusinessResponse<MyResultType>();
    ProcessResult(res);

    return res; //this has the values set by ProcessResult!!! How?
}

private void ProcessResult(BusinessResponse<MyResultType> result)
{
    result.State = BusinessResponseState.Success;
    //set some other stuff in the result argument
}

我的同事说这叫做“深度引用”,我不敢相信这在 C# 中有效。我会重构为使用 out aka private void ProcessResult(out BusinessResponse&lt;MyResultType&gt; result) 但我仍然想知道为什么这段代码有效。

更新

所以看起来我不正确,这段代码很好。似乎情绪是,作为 C# 开发人员,我应该立即知道,因为传递给 processResult 的参数是堆上的引用类型,通过值传递,引用本身的副本被制作,并且复制的引用仍然指向同一个对象。

【问题讨论】:

  • 对象通过引用传递。如果这是一个值类型,它将按照您的预期工作。
  • 啊,当然,谢谢 TheEvilPenguin ;)
  • 对不起,我只是不小心回滚了某人的编辑,不是故意的
  • 回复:您的更新。是的,您只需要(非常快速地)熟悉您在 C# 中对对象所做的大多数事情都是通过引用完成的这一事实。

标签: c#


【解决方案1】:

当然,在您的情况下BusinessResponse&lt;T&gt;class,也称为"reference type"。引用类型(对象)总是在 C# 中“通过引用”传递,(类似于良好的 C 语言中的指针)1

这意味着无论你如何传递一个引用,它仍然引用内存中的一个实例。这意味着无论您如何引用它,您总是在修改同一个副本。

1 - 这并不完全正确。实际发生的是 reference(变量)是按值传递的。当函数接收到这个引用时(再一次,想想指向托管堆上某个对象的指针),它引用的是同一个对象。但是,引用本身在传入时被复制了。

我们使用的另一种变量是“值类型”。这些是structs,以及所有的原语,如charboolint 等。不出所料,它们总是按值传递,这意味着每个将值类型分配给新变量(包括将其传递给函数)的时间。要通过函数调用修改这些,您必须使用 refout 关键字。

那么您什么时候将ref 与引用类型一起使用?

当你想改变哪个实例一个变量引用:

public class Foo {
    public string Name { get; private set; }
    public Foo(string name) {
        Name = name;
    }
}

public class Program {

    public static void Example(ref Foo f) {
        // This will print "original"
        Console.WriteLine("Example() was given {0}", f.Name);

        // We assign a new instance to the reference which was
        // passed by reference
        f = new Foo("replacement");
    } 

    public static void Main() {
        Foo f;                       // This variable is a "reference" to a Foo

        f = new Foo("original");     // We assign a new instance to that variable

        Example(ref f);

        // This will print "replacement"
        Console.WriteLine("After calling Example(), f is {0}", f.Name);
    }

}

【讨论】:

  • @BrianOgden 绝对不是。这意味着完全不同的事情。
  • 引用类型按引用传递的说法是错误的。除非您声明该参数“ref”或“out”,否则所有方法参数都按值传递。正确的说法是,当您按值传递引用类型时,您传递的值是引用而不是对象。您可以按值或按引用传递值类型,也可以按值或按引用传递引用类型。当您按值传递时,您会创建原始值的副本。对于值类型,值是对象,对于引用类型,值是对对象的引用。
  • @jmcilhinney 你是绝对正确的,我理解其中的区别。但是即使对于理解的人,您的解释也令人困惑。这就是为什么我没有这么说。
  • “为了更好的可读性,最好还是使用关键字 out 或 ref 吧?” 绝对不行!唯一使用“out”的时间是,如果您打算以未初始化的形式传递参数,然后在方法内直接分配给该参数。您以相同的方式使用“ref”,除了参数在传入之前初始化并且在方法中使用初始值。您拥有的代码与应有的完全一样。你不是在传递一个新对象;您只是在修改传入的对象。因为它是引用类型的对象,所以它的工作方式与您(应该)期望的完全一样。
  • @BrianOgden 当一个引用类型(对象)被传递with ref/out时,你传递的是对该引用的引用,(或指向你的局部变量)。因此,当在被调用函数中分配该引用(新的/不同的对象)时,调用函数中的原始引用(局部变量)会受到影响。
【解决方案2】:

这需要一点挖掘;您正在发现值类型和引用类型的差异。当这么多已经在线时,我不会给你我的两分钱。这里有几篇文章
basic difference between value types and reference types
Reference and Value types scenario
What is a Value Class and what is a reference Class in C#?
最后是 MSDN 文章
http://msdn.microsoft.com/en-us/library/4d43ts61%28v=vs.90%29.aspx

【讨论】:

    猜你喜欢
    • 2011-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-20
    • 2011-08-29
    相关资源
    最近更新 更多