Это плохая практика иметь события в интерфейсах? C # - PullRequest
0 голосов
/ 05 сентября 2018

Я пытаюсь использовать интерфейсы чаще, и часто я помещаю события в интерфейс. Это кажется странным, потому что я оставляю за разработчиком возможность узнать, когда вызывать эти события в их реализации.

Например, у меня есть интерфейс ILevellable, который просто заявляет, что объект должен иметь xp, уровень и максимальный уровень, а также некоторые методы, которые изменяют эти значения. Классы, ссылающиеся на ILevellable, вероятно, захотят узнать, когда он выровняется, поэтому я добавляю Action для OnLevelUp ... но реализация может не поместить это в правильное место.

public interface ILevellable
{
    int Level { get; }
    int MaxLevel { get; }
    int XP { get; }
    event Action<bool> OnXPChanged;
    event Action<ILevellable> OnLevelUp;
    event Action OnMaxLevelReached;

    void LevelUp(int newLevel);
}

Должен ли я верить, что пользователи, реализующие этот интерфейс, будут знать, где это реализовать? Я бы предположил, что нет. Тем более, что некоторые события могут иметь аргументы, которые, опять же, пользователь может не знать, что он должен представлять.

Я ценю любое руководство! Я новичок в такой работе.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Это нормально для определения событий в ваших интерфейсах. Однако в соответствии с соглашением в событие передаются и аргументы отправителя, и аргументы события. TypedEventHandler - это простой способ сделать это. Например:

using Windows.Foundation;

public struct LevellableChange {
    public LevellableChange( int dl, int dxp) 
    { 
         this.ChangeInLevel = dl;
         this.ChangeInXP = dxp;
    }
    int ChangeInLevel { get; }
    int ChangeInXP {get;}
}

public interface ILevellable
{
    int Level { get; }
    int MaxLevel { get; }
    int XP { get; }
    event TypedEventHandler< ILevellable, LevellableChange> Changed;
}

Тогда стандартный подход к их реализации будет выглядеть так:

public class Levellable: ILevellable
{
    public event TypedEventHandler<ILevellable, LevellableChange> Changed;

    public int Level {
        get {
            return this._level;
        }
        private set {
            if (value != this._level) {
                int oldLevel = this._level;
                this._level = value;
                this.Changed?.Invoke(this, new LevellableChange(value - oldLevel, 0));
            }
        }
    }
    private int _level;

    // similar for XP. 
}

Еще один комментарий, полезно иметь класс, реализующий INotifyPropertyChanged, а затем привыкнуть к его наследованию. Вместе с фрагментом в Visual Studio все становится намного проще. Это правда, что для приведенного выше примера INotifyPropertyChanged будет недостаточно, поскольку он не дает изменения предыдущего значения.

0 голосов
/ 05 сентября 2018

Прежде всего, вы не имеете типы делегатов в определении вашего интерфейса, но события . Проверьте этот ответ Джона Скита, чтобы увидеть разницу между делегатами и событиями. Сказав, что в вашем интерфейсе вполне нормально иметь события, даже .Net это делает, рассмотрим, например, известный интерфейс INotifyPropertyChanged . Он просто определяет событие ...

Во-вторых:

Должен ли я верить, что пользователи, реализующие этот интерфейс, будут знать, где это реализовать?

Когда вы определяете интерфейс, вы действительно не заботитесь о разработчиках так сильно, как о потребителях, и поэтому для потребителя, который видит определение вашего контракта, полезно знать, что он / она может подписаться на событие с именем OnLevelUp , Допустим, я действительно потребляю ваш интерфейс, я не знаю, как или когда вы запускаете событие OnLevelUp, и мне на самом деле все равно, потому что вы дали этому событию очень семантическое и значимое имя, так что я могу в значительной степени быть убедитесь, что он будет срабатывать при увеличении свойства Level ILevellable.

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