Событие не запускается при копировании всего объекта. Как это сделать? - PullRequest
0 голосов
/ 20 февраля 2010

Если у меня есть класс, который содержит некоторые свойства и методы, и когда свойство изменяется, оно генерирует событие (в данном случае передавая строку в аргументах):

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;

Я хочу, чтобы все свойства, которые были изменены, запускали свои события.

Как мне добиться чего-то подобного? Конечно, мне не нужно копировать их по одному за раз?

Ответы [ 4 ]

1 голос
/ 20 февраля 2010

Эта строка кода:

SettingsInstance = SomeOtherSettingsInstance;

не копирует ничего внутри объектов, вместо этого он перезаписывает ссылку, сохраненную в SettingsInstance, на ссылку, сохраненную в SomeOtherSettingsInstance.

Сам объект не мудрее.

По сути, после того, как вы выполнили первую из трех последних строк, у вас есть такой сценарий:

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 голосов
/ 21 февраля 2010

Как указал Лассе, присвоение ссылочной переменной просто меняет объект, на который ссылается эта переменная, и ничего не делает с этим объектом.

Значение присваивания жестко контролируется компилятором 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;

Первая строка будет вызывать GainedAssignment в новом экземпляре Settings. Вторая строка будет вызывать LostAssignment в этом экземпляре, а затем GainedAssignment в другом. Идея в том, что вы заставите Settings запустить определенные события в одном или обоих из них.

Но, конечно, это не останавливает ошибочное:

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

Это просто отбрасывает старый Ref<T> объект, и поэтому никто никогда не говорит предыдущему экземпляру настроек, что он больше не назначен для «живой» переменной.

0 голосов
/ 20 февраля 2010

Это потому, что свойства не меняются, вы просто переназначаете ссылки.

0 голосов
/ 20 февраля 2010

Почему бы просто не выполнить инициализацию наоборот?

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

При выполнении задания вы перезапишете свой экземпляр, SettingsInstance, где вы просто настраиваете событие SettingsChanged.

Полагаю, вам все равно нужно будет скопировать все вручную, чтобы убедиться, что все поля в новом экземпляре верны. Вы можете обойтись мелкой копией, используя Object.MemberwiseClone. Более подробное обсуждение мелкой копии и глубокой копии см. По ссылке wikipedia .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...