English 中文(简体)
复制整个物体时没有发射。 如何这样做?
原标题:Event not fired when copying entire object. How to do it?
  • 时间:2010-02-20 20:41:16
  •  标签:
  • c#
  • events

如果我有一个包含一些属性和方法的类,当属性被更改时,它会触发一个事件(在这种情况下将一个字符串传递给args):

public class Settings
{
    public delegate void SettingsChangedHandler(object sender, string e);
    public event SettingsChangedHandler SettingsChanged;

    private string rootfolder;
    public string RootFolder
    {
        get { return rootfolder; }
        set
        {
            rootfolder = value;
            if (SettingsChanged != null)
                SettingsChanged(this, "ROOT_FOLDER");
        }
    }
}

如果在我的法典中有某些地方:

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

我希望所有已经改变的房地产都对其事件开火。

我该如何实现类似这样的效果?我不用一个一个地复制吧?

最佳回答

这行代码:

SettingsInstance = SomeOtherSettingsInstance;

不复制对象内部的任何内容,而是用存储在SomeOtherSettingsInstance中的引用覆盖存储在SettingsInstance中的引用。

该物体本身并不聪明。

基本上,在您执行了最后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
}
问题回答

为什么不采用另一种方式进行初始化呢?

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

你目前执行任务的方式会覆盖你的实例 SettingsInstance,你只是在配置 SettingsChanged 事件。

我相信你仍需要手动复制所有内容以确保新实例上的所有字段都是正确的。你可能可以使用Object.MemberwiseClone进行浅复制。关于浅复制和深复制的更深入讨论,请参阅此wikipedia链接。

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

正如Lasse所指出的那样,将赋值给引用变量只会改变该变量所引用的对象,并且对该对象没有任何影响。

分配的含义由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。其想法是您可以使Settings在其中一个或两个实例中触发某些事件。

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

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

那只是丢弃了旧的Ref<T>对象,因此没有人会告诉先前的Settings实例它不再分配给“活”变量。





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签