前一个“新”运算符会发生什么变化?

时间:2018-04-03 11:45:28

标签: c#

DispatcherTimer dt = new DispatcherTimer();
dt.Interval = new TimeSpan(0, 0, 0, 0, 100);
dt.Tick += new EventHandler(dt_dt);

我对new关键字有疑问。我有一个DispatcherTimer,我设置为一个间隔。假设用户想要更改间隔。

dt.Interval = new TimeSpan(0, 0, 0, 0, 50);

那么,第一个new TimeSpan会发生什么?它还在吗?或者新的覆盖了之前的?我不这么认为。

如果我想更改间隔,new关键字是声明新TimeSpan的唯一方法吗?我问这个问题,因为我不确定每次值变化时是否确实宣布新的TimeSpan是正确的,甚至是正确的。

2 个答案:

答案 0 :(得分:25)

您的类(DispatcherTimer)的字段类型为TimeSpan,而TimeSpan是结构。

因为它是一个结构 - 它被“嵌入”DispatcherTimer的实例。因此DispatcherTimer的实例在内存中看起来像这样:

<various metadata>...<contents of timespan>...

如果是类(引用类型),它将如下所示:

<various metadata>...<reference to timespan>...

当你这样做时

dt.Interval = new TimeSpan( 0, 0, 0, 0, 50); 

分配TimeSpan的实例(在这种情况下为堆栈)并复制到为DispatcherTimer分配的Interval实例的内存区域,覆盖之前的内容。

所以我们有了

<various metadata>...<contents of timespan 1>

现在我们有了

<various metadata>...<contents of timespan 2>

这意味着旧的间隔时间跨度被简单地覆盖,它不会被垃圾收集器收集,因为没有任何东西可以收集(与接受的答案声明相反)。

因此,在紧密循环中执行此操作也不是问题(尽管当然没有理由)。请考虑以下代码:

class ClassWithRefField {
    public TestClass Field;
}

class ClassWithStructField {
    public TestStruct Field;
}

class TestClass {
    public TestClass(int payload) {
        Payload = payload;
    }
    public int Payload;
}

struct TestStruct {
    public TestStruct(int payload)
    {
        Payload = payload;
    }
    public int Payload;
}

然后,如果你这样做:

static void Main(string[] args) {
    var f = new ClassWithRefField();
    while (true) {
        f.Field = new TestClass(1);
    }            
}

即使在Windows中的进程浏览器中也要观察进程内存 - 你会发现内存不断增长和下降。那个TestClass的实例是在堆上创建的,并由垃圾收集器收集。

但是如果你这样做:

static void Main(string[] args) {
    var f = new ClassWithStructField();
    while (true) {
        f.Field = new TestStruct(1);
    }            
}

进程资源管理器中的内存将保持绝对不变,因为相同的内存区域会不断被覆盖,因此无需收集任何内容。这个循环可以永远运行。

是的 - 你可以按照自己的方式去做。

答案 1 :(得分:4)

嗯,这里有很多事情需要考虑。是的,它确实创建了TimeSpan结构的第二个实例。由于第一个没有在任何地方引用,它将超出范围并将被垃圾收集器清理。没有伤害。

如果 TimeSpan是一个类,你可能会更改其中一个属性,但它是一个结构,结构通常是不可变的,这意味着你无法改变他们的价值。 (这是由于多种原因,我建议你阅读)

因此,您在这里唯一的选择是创建一个新的TimeSpan结构实例。如果你没有在紧密的循环中做到这一点(在很短的时间内创建一个低的新实例,消耗大量内存),这没有问题。