【问题标题】:Default parameter passing in C#C#中的默认参数传递
【发布时间】:2011-12-24 08:07:48
【问题描述】:

也许是一个老问题,但在互联网上找不到任何全面的东西。

如果C#中默认的参数传递方式是By value,那么它对初始Reference Type变量有什么影响呢?

即在下面的示例中,如果它是按值传递的参数,为什么它会打印“Hello World”而不是“Hello”?

void Foo (StringBuilder x)
{
    x.Append (" World");
}   

StringBuilder y = new StringBuilder();
y.Append ("Hello");
Foo (y);
Console.WriteLine (y);

【问题讨论】:

  • 对字符串生成器对象的引用是按值传递的。

标签: c# parameters parameter-passing pass-by-value reference-type


【解决方案1】:

首先,了解 C# 中有两种基本类型:值类型引用类型

此外,这些类型中的每一种都可以传递给 By ValueBy Reference 方法。 (因此,这意味着实际上有四种方式将参数传递给方法。)

无论您如何传递引用类型(按值或按引用),您都可以更改该引用指向的值!

现在,关于您的特定示例,您的问题参数是 StringBuilder 类型,它是一个引用类型。 (StringBuilder 是一个 Class 并且 Classes 是引用类型。)同样,因为您将引用类型传递给您的方法,您可以在该方法内部更改与该引用关联的值.

最后,请注意您是按值传递引用类型参数。相反,如果您传递您的引用类型By Reference,然后将其设置为null,您实际上会破坏与引用关联的值。 (这与将参数变量设置为方法的null outside 相同。)

您可以在此处找到更全面、更易读的解释:C# Concepts: Value vs Reference Types

【讨论】:

    【解决方案2】:

    该参数仍然是按值传递,但参数变量x 具有StringBuilder 对象的引用。

    引用变量y有StringBuilder对象的引用

    StringBuilder y = new StringBuilder();
    

    StringBuilder 对象的引用被复制到 Foo 的参数 x 中。

    Foo (y);
    

    【讨论】:

      【解决方案3】:

      因为 StringBuilder 是一个mutable 类并且将通过引用传递。你使用字符串而不是字符串生成器,它将是Hello,因为字符串是immutable。对于 intenum 等值类型,也没有任何变化。

      为简单起见,值类型是结构、枚举、原始类型,... 和引用类型都是类,但正如我提到的,有一些类如字符串是不可变的,实际上它们将按值传递。

      【讨论】:

      • 太棒了!感谢大家快速而详细的回答。面试时被问到这个问题!我只是在验证我的答案;)所以总结一下:C# 中的默认参数传递方法是什么... 1. 值类型按值传递,引用类型按引用传递(现在我知道它不是 :) ,但我已将其作为答案) 2. 通过值传递不可变类型,通过引用传递可变类型 3. 通过值传递原始类型,通过引用传递非原始类型 4. 还是其他什么?
      • @Vishruth 其实你的回答已经足够好了,而且我认为这不是一个很好的面试问题,如果你能理解值类型和引用类型之间的区别,以及不可变和可变对象之间的区别,那就很好了够了,只是他们面试太多,把它当成纸笔考试。
      【解决方案4】:

      除了原始类型(例如intbyte 等)之外的任何内容都默认通过引用传递。您正在将相同的 StringBuilder 实例传递给该方法。

      【讨论】:

      • 它们将通过引用传递,但如果它们是不可变的,则它们没有变化。例如string 不是原始类型,但它的值没有变化。
      • -1:引用是按值传递的,此外还有DateTime等非原始值类型。
      • 根据 MSDN 自己的文章,字符串是原始的 msdn.microsoft.com/en-us/library/aa711900%28VS.71%29.aspx (当我在 .net 中向 google 键入原始类型时的第一个结果)。我知道字符串实例在内存中是不可变的,但是“对于用户”,它充当原始类型。 (从同样的角度来看,日期时间也是原始的)
      • @Vishruth 原始类型是数字类型;整数和浮点数。并且像字符串和日期时间这样的类型在.net中也被认为是原始的,从某种意义上说,如果你将它们传递给一个方法,它们的值将被传递,并且你对传递的值所做的任何更改都不会反映到另一个(第一)另一种方法的价值。另一方面,所有其他类型(据我所知)都是引用,当您将它们传递给方法时,它们的“指针”被复制,仍然“指向内存中的同一个对象”
      • @canpoyrazoğlu 是的,这是我的错误,但我的观点或 Henrik 的观点是,除了你所说的之外,还有其他类型,它们是值类型,实际上我们没有原始类型/非原始类型为了分离它们的调用方式,这些是一般规则的子类,值类型和引用类型,所有结构都是值类型但不是原始的......
      【解决方案5】:

      StringBuilder是一个类,所以会通过引用传递。

      阅读更多:Value vs reference types

      【讨论】:

        【解决方案6】:

        比较以下。首先使用 StringBuilder(引用类型):

            public struct Tmd
            {
                public StringBuilder sb;
            }
        
            public void DoIt(Tmd a)
            {
                a.sb.Append(" World!");
            }
        
            public void Main()
            {
                Tmd a = new Tmd();
                a.sb = new StringBuilder();
                a.sb.Append("Hello");
                DoIt(a);
                Console.WriteLine(a.sb); // Hello World
            }
        

        这里结构被复制,对 StringBuilder 的引用也被复制,但 StringBuilder 本身没有被复制。

        现在有了一个可变结构(值类型):

            public struct EvilMutable
            {
                public int i;
            }
        
            public struct Tmd
            {
                public EvilMutable em;
            }
        
            public void DoIt(Tmd a)
            {
                a.em.i += 1;
            }
        
            public void DoIt(EvilMutable em)
            {
                em.i += 1;
            }
        
            public void Main()
            {
                Tmd a = new Tmd();
                a.em.i += 5;
                Console.WriteLine(a.em.i); // 5
                DoIt(a);
                Console.WriteLine(a.em.i); // 5 (unchanged)
                DoIt(a.em);
                Console.WriteLine(a.em.i); // 5 (unchanged)
            }
        

        在这种情况下,一切都被复制了。但是,如果我们将其更改为引用类型:

            public class Tmd
            {
                public EvilMutable em;
            }
        

        然后,我们会得到这个:

                Tmd a = new Tmd();
                a.em.i += 5;
                Console.WriteLine(a.em.i); // 5
                DoIt(a);
                Console.WriteLine(a.em.i); // 6
                DoIt(a.em);
                Console.WriteLine(a.em.i); // 6 (unchanged)
        

        【讨论】:

          猜你喜欢
          • 2016-01-02
          • 2012-08-06
          • 2016-05-12
          • 1970-01-01
          • 1970-01-01
          • 2014-04-09
          • 1970-01-01
          • 2015-12-07
          • 2020-07-29
          相关资源
          最近更新 更多