Как реализовать метод Clone в абстрактном классе без использования MemberwiseClone?

У меня есть абстрактный базовый класс, определенный следующим образом:

public abstract class BaseItem: INotifyPropertyChanged, ICloneable
{
    // Various Properties...

    public event public event PropertyChangedEventHandler PropertyChanged;

    public object Clone()
    {
        var clone = (BaseItem)MemberwiseClone();
        return clone;
    }
}

Производные классы наследуют и используют метод Clone базового класса, поэтому им не нужно реализовывать свои собственные методы Clone (плюс некоторые дополнительные, не относящиеся к делу причины).

Как правило, этот код работает так, как задумано. Единственная проблема заключается в том, что есть нежелательный побочный эффект. Поскольку я использую MemberwiseClone для клонирования экземпляра, ссылочные типы копируются неглубоко, что, к сожалению, включает события. Таким образом, любые объекты, которые подписались на события в исходном экземпляре, также будут подписаны на события в экземпляре клона. Это проблема.

Есть ли способ создать клон экземпляра класса, производного от BaseItem, с помощью метода BaseItem.Clone без клонирования событий?


person Andrew Fink    schedule 15.01.2021    source источник
comment
К сожалению, MemberwiseClose - это extern, поэтому было бы довольно сложно скопировать + вставить + изменить его, чтобы делать то, что вы хотите. Я предлагаю вам позвонить MemberwiseClone, а затем использовать эту технику чтобы удалить обработчики событий постфактум.   -  person John Wu    schedule 15.01.2021
comment
Просто сделайте clone.PropertyChanged = null;. Если у вас есть обработчик событий, вам не нужно идти сложным путем, упомянутым @JohnWu.   -  person ckuri    schedule 15.01.2021
comment
Да, я надеялся, что смогу вообще не копировать обработчики событий, а не удалять их впоследствии, но если первое действительно невозможно, то, думаю, второе должно работать. Решение @ckuri работает отлично. Спасибо.   -  person Andrew Fink    schedule 15.01.2021
comment
Вам нужно, чтобы каждый производный класс переопределял Clone с правильной семантикой копирования. Чтобы получить возвращаемое значение DerivedClass, вы можете использовать Удивительно повторяющийся шаблон шаблона public abstract class BaseItem<T> where T : BaseItem<T>   -  person Charlieface    schedule 17.01.2021


Ответы (1)


Как упоминал @JohnWu в комментарии, решение, которое сработало для меня, состояло в том, чтобы установить для обработчика событий значение null, чтобы удалить всех его подписчиков.

public abstract class BaseItem: INotifyPropertyChanged, ICloneable
{
    // Various Properties...

    public event PropertyChangedEventHandler PropertyChanged;

    public object Clone()
    {
        var clone = (BaseItem)MemberwiseClone();
        clone.PropertyChanged = null;
        return clone;
    }
}
person Andrew Fink    schedule 25.01.2021