2010-02-20 74 views
0

如果我有一个包含一些属性和方法,当一个属性发生变化时,触发一个事件(在这种情况下,通过在ARGS一个字符串)类:复制整个对象时不会触发事件。怎么做?

​​

,如果我有什么地方我代码:

public Settings SettingsInstance = new Settings(); 
SettingsInstance.SettingsChanged += new SettingsChangedHandler(SettingsInstance_SettingsChanged); 
SettingsInstance = SomeOtherSettingsInstance; 

我想要所有已更改为触发其事件的属性。

我该如何做到这样?当然,我不必一次只复制一份呢?

回答

1

这行代码:

SettingsInstance = SomeOtherSettingsInstance; 

不会复制任何物体内部,而是覆盖存储在SettingsInstance与存储在SomeOtherSettingsInstance参考参考。

对象本身并不聪明。

基本上,你已经执行的第一个最后3行后,你有这样的场景:

SomeOtherSettingsInstance -----> Object 1 in memory of type Settings 

SettingsInstance --------------> Object 2 in memory of type Settings 
         ^
         | 
         +- References 

你执行第三行之后,这是它的外观:

SomeOtherSettingsInstance --+--> Object 1 in memory of type Settings 
         /
SettingsInstance ---------+  Object 2 in memory of type Settings 

现在你有两个对第一个对象的引用,一个通过每个变量,并且你已经留下刚刚创建的新对象以便垃圾收集器稍后再来拾取它。

如果你想复制内部,那么是的,你必须一次复制一个属性。

我定期创建克隆这样的支持:

public Settings Clone() 
{ 
    Settings clone = CreateCloneInstance(); 
    CloneTo(clone); 
    return clone; 
} 

protected virtual Settings CreateCloneInstance() 
{ 
    return new Settings(); 
} 

public virtual void CloneTo(Settings clone) 
{ 
    clone.RootFolder = RootFolder; 
    ... + any other properties you might have 
} 

在你的情况下,你想复制以前的事情挂钩的事件,所以你会这样称呼它:

public Settings SettingsInstance = new Settings(); 
SettingsInstance.SettingsChanged += SettingsInstance_SettingsChanged; 
SomeOtherSettingsInstance.CloneTo(SettingsInstance); 

的我实现克隆支持的原因是由于对象层次结构。如果这不是你的问题(你不打算从设置继承),你可以这样做:

public Settings Clone() 
{ 
    Settings clone = new Settings(); 
    CloneTo(clone); 
    return clone; 
} 

public void CloneTo(Settings clone) 
{ 
    clone.RootFolder = RootFolder; 
    ... + any other properties you might have 
} 
+0

由于事件处理程序设置克隆发生之前,事件将触发每次设置属性。 – smaclell 2010-02-20 20:57:59

+0

好的谢谢,我将不得不一次做一个属性 – Dave 2010-02-20 20:58:40

0

为什么不只是用另一种方式执行初始化?

Settings SettingsInstance = SomeOtherSettingsInstance; 
SettingsInstance.SettingsChanged += new SettingsChangedHandler(SettingsInstance_SettingsChanged); 

目前正在执行将覆盖您的实例,SettingsInstance,在那里你只需配置SettingsChanged事件的分配方式。

我相信你仍然需要手动复制一切,以确保新实例上的所有字段都是正确的。您可以使用Object.MemberwiseClone来获得较浅的副本。有关浅拷贝与深拷贝的更深入讨论,请参阅此wikipedia链接。

+0

MemberwiseClone直接复制字段,因此它会绕过引发SettingsChanged的代码。 – itowlson 2010-02-20 20:52:12

+0

你是对的!嗯,我有点困惑的问题,然后... – smaclell 2010-02-20 20:54:19

0

这是因为属性没有改变,你只是重新分配引用。

0

由于拉塞指出,赋值给一个引用变量只是改变了什么对象变量是指对对象无所作为。

赋值的含义是由C#编译器严格控制的。您可以在属性上覆盖它,但不能在局部变量上覆盖它。所以最接近你可以得到这个模式是:

interface IWantAssignmentNotification 
{ 
    void LostAssignment(); 
    void GainedAssignment(); 
} 

class Ref<T> where T : IWantAssignmentNotification 
{ 
    private T _value; 

    public T Value 
    { 
     get { return _value; } 
     set 
     { 
      if (_value != null) 
       _value.LostAssignment(); 

      _value = value; 

      if (_value != null) 
       _value.GainedAssignment();     
     } 
    } 
} 

现在你Settings类必须实现IWantAssignmentNotification,您可以使用Ref<Settings>持有对它的引用:

Ref<Settings> refSettings = new Ref<Settings> { Value = new Settings() }; 

refSettings.Value = someOtherSettingsInstance; 

第一行将在Settings的新实例上调用GainedAssignment。第二行将在该实例上调用LostAssignment,然后在另一个实例上调用GainedAssignment。这个想法是,你会让其中一个或两个发生某些事件。

但当然,这并不能阻止错误:

refSettings = new Ref<Settings> { Value = someOtherSettingsInstance }; 

,简单地丢弃旧Ref<T>对象,所以没有人告诉以前的设置实例,它不再分配给“活“变量。