复制类的引用与复制值之间的区别

时间:2018-09-11 13:25:12

标签: c# class copy value-type reference-type

为进行实验,我尝试了此操作:

(1)创建100000个类,每个类都包装一个double变量

---这是实验部分---

(2)通过运行100000次来测量两种方法的性能:

  • 创建一个double []并分配包装变量的值。

  • 创建一个类[]并分配包装类的引用。

以上内容可能会使您感到困惑,所以我附上了代码:

static void Main(string[] args)
{
    int length = 100000;
    Test test = new Test(length);

    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();

    for (int i = 0; i < length; i++)
        test.CopyValue();
        //test.CopyReference(); //test.CopyValue(); or test.CopyReference();

    stopwatch.Stop();
    Console.WriteLine("RunTime : {0} ", stopwatch.ElapsedMilliseconds);
}

class DoubleWrapper
{
    public double value = 0.0;
}

class Test
{
    DoubleWrapper[] wrapper;

    public void CopyValue()
    {
        double[] x = new double[wrapper.Length];
        for (int i = 0; i < wrapper.Length; i++)
            x[i] = wrapper[i].value;
    }

    public void CopyReference()
    {
        DoubleWrapper[] x = new DoubleWrapper[wrapper.Length];
        for (int i = 0; i < wrapper.Length; i++)
            x[i] = wrapper[i];
    }

    public Test(int length)
    {
        wrapper = new DoubleWrapper[length];
        for (int i = 0; i < length; i++)
            wrapper[i] = new DoubleWrapper();
    }
}

结果如下:

test.CopyValue():56890(millisec)

test.CopyReference():66688(millisec)

(使用发行版配置构建并运行exe)

我尝试了几次,但结果并没有太大变化。 因此,我得出结论,CopyReference()需要更长的时间。


但是我几乎不明白为什么。这是问题:

我认为,不管CopyValue()或CopyReference(),我的机器执行的操作都是“在内存中复制数字”,尽管一个是双精度值,而另一个是对类的引用。因此,性能上不应有有意义的差异,但事实并非如此。

那么复制值和复制引用之间有什么区别? 复制引用比复制值做更多的事情吗?

(当传递不带ref关键字的引用时,是不是真的像复制了引用一样复制了引用呢?

ClassA x = new ClassA();
ClassA y = x;

意味着复制“ x的引用”,然后分配给变量y,因此y = null根本不会影响x。这是真的吗?)

如果我使用错误的假设,请让我知道我的错。

感谢您的帮助和建议。

-

我猜测GC可能会产生一些影响,但是通过TryStartNoGCRegion(Int64)关闭GC不会改变结论。 (两者都变快,但CopyReference()仍然变慢。)

2 个答案:

答案 0 :(得分:0)

  

表示复制“ x的引用”,然后赋给变量   y,因此y = null根本不影响x。这是真的吗?

是的-您制作了副本参考。

现在,为什么您执行CopyReference的速度变慢呢?

您必须进行性能分析才能获得有关它的真正见解,但最重要的是:

您正在该方法内创建DoubleWrapper 引用类型的新实例。 请记住,C#不是像C/C++/Rust那样的“零成本抽象”语言。 创建一个甚至简单的引用类型的实例,它只不过是对原始类型的简单包装而已,因此可能会花费更多。因为该实例中包含的内容比简单的double值要多-DoubleWrapper对象的大小将不等于8个字节。

答案 1 :(得分:-1)

阅读有关堆栈和堆的信息可能有助于您理解。如果复制引用,则实际上是将显示的指针复制到堆中的实际对象,并且如果该实际对象发生更改,则这些更改将显示在引用该对象的所有事物上。

如果执行“深层复制”或克隆(如实现IClonable时的克隆),则将在堆栈中复制该数据并创建一个指向它的指针,因此不再依赖原始对象。

我希望这个解释对您有所帮助?参见https://www.c-sharpcorner.com/article/C-Sharp-heaping-vs-stacking-in-net-part-i/,了解有关堆栈和堆的信息。