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是正确的,甚至是正确的。
答案 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
结构实例。如果你没有在紧密的循环中做到这一点(在很短的时间内创建一个低的新实例,消耗大量内存),这没有问题。