2016-12-09 3 views
2

Я рефакторинг функции, которая принимает необязательный делегат через конструктор. Делегат запускается, когда события запускаются внутри класса. Если делегат не передается, то локальная функция по умолчанию используется вместо:Каков наилучший способ работы с необязательными делегатами в конструкторе C#?

public class Foo 
{ 
    int _memberVariable; 
    readonly Action _onEventOne; 
    readonly Action _onEventTwo; 

    public Foo(Action onEventOne, Action onEventTwo = null) 
    { 
     _memberVariable = 0; 

     _onEventOne = onEventOne; 
     _onEventTwo = onEventTwo ?? DefaultEventTwo; 

     _onEventOne(); 
    } 

    private void DefaultEventTwo() 
    { 
     ++_memberVariable; 
    } 
} 

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

В идеальном мире я бы использовать конструктор цепочки:

public Foo(Action onEventOne) : this(onEventOne, DefaultEventTwo) 
{ 
    //CS0120 An object reference is required for the non-static field, method, or property 'Foo.DefaultEventTwo() 
} 

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

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

Есть ли лучший способ обработать случай, например, просто передать значение null, а затем перехватить его в главном конструкторе? Он не чувствует себя очень элегантно, и я хотел бы иметь возможность поймать нулевое действие как исключение в идеале (если бы внешний вызывающий использовал значение null вместо использования перегруженного конструктора). Я мог удалить readonly от делегатов, но опять же, это не похоже на отличное решение, поскольку они действительно только для чтения.

Любые мысли будут оценены.

+1

общественности 'Foo (Действие onEventOne): это (onEventOne, нуль)' будет путь или 'public Foo (Action onEventOne): this (onEventOne, new Action (Console.Beep))' или у вас есть func вроде: 'public static void NoEvent() { // DoNothing() }' и обрабатывайте его так: '..., new Action (NoEvent) ' – TripleEEE

+1

И тогда вы просто проверяете на' null', и если он передает вызов 'DefaultEventTwo', а не (не) предоставленный делегат. – HimBromBeere

ответ

1

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

Вы должны поставить статический метод, но этот статический метод может использовать ссылку на это, чтобы получить фактический метод.

Это то, что я придумал.

public Foo(Action onEventOne) : this(onEventOne, self => self.DefaultEventTwo) 
{ 
    //CS0120 An object reference is required for the non-static field, method, or property 'Foo.DefaultEventTwo() 
} 

public Foo(Action onEventOne, Action onEventTwo = null) : this(onEventOne, self => onEventTwo) 
{ } 

// private constructor, just for the sake of getting it working 
private Foo(Action onEventOne, Func<Foo, Action> onEventTwo = null) 
{ 
    _memberVariable = 0; 

    _onEventOne = onEventOne; 
    _onEventTwo = onEventTwo(this); // <-- 

    _onEventOne(); 
} 

self => self.DefaultEventTwo - статическая функция для получения действия. Эта функция используется в вызове onEventTwo(this), чтобы получить событие по умолчанию для экземпляра this.

+0

Я подозреваю, что не будет никакого по-настоящему хорошего решения, мне больше всего интересно узнать, какие идеи у людей были, и это, безусловно, самое интересное. Это определенно хорошо работает для проблемы, как указано, но начинает немного запутываться, если вы добавляете дополнительные необязательные события (очевидно, это не знак против этого здесь, а просто вопрос, который стоит отметить, если кто-то еще случится через это в будущем). –

+0

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

1

Я пропустил что-нибудь?

public class Foo 
{ 
    int _memberVariable; 
    readonly Action _onEventOne; 
    readonly Action _onEventTwo; 

    public Foo(Action onEventOne): this(onEventOne, null) { } 
    public Foo(Action onEventOne, Action onEventTwo) 
    { 
     _memberVariable = 0; 

     _onEventOne = onEventOne; 
     _onEventTwo = onEventTwo ?? DefaultEventTwo; 

     _onEventOne(); 
    } 

    private void DefaultEventTwo() 
    { 
     ++_memberVariable; 
    } 
} 

Все, что вам нужно сделать, это удалить значение по умолчанию и создать новый конструктор, который имеет только один аргумент. Теперь в самом подробном конструкторе (исходном) проверьте, предоставлено ли значение null, и если оно установлено _onEventTwo - DefaultEventTwo.

Чтобы избежать того, чтобы кто-либо использовал уменьшенный конструктор, просто сделайте его internal.

EDIT: Что касается обработки исключений. Что об использовании внутреннего конструктора в качестве «основного» -она, что все остальные называют с парами, указывающее, откуда вызов пришел из:

internal Foo(Action onEventOne): this(onEventOne, null, true) { } 
// public API: NULL not allwoed as param 
public Foo(Action onEventOne, Action onEventTwo) : this(onEventOne, onEventTwo, false) { } 
internal Foo(Action onEventOne, Action onEventTwo, bool internalUse) 
{ 
    _memberVariable = 0; 

    _onEventOne = onEventOne; 
    if(onEventTwo == null) 
    { 
     if(!internalUse) throw new ArgumentNullException("onEventTwo"); 
     else this._onEventTwo = DefaultEventTwo; 
    } 
    _onEventOne(); 
} 
+0

Я думаю, что второе решение лучше всего подходит для вопроса (я мог бы сформулировать вопрос путающе - первое решение было тем, что я впервые сделал, но я ищу альтернативы). Добавление дополнительного аргумента - интересная идея, которую я даже не рассматривал (кажется, сейчас так очевидно), спасибо! –

 Смежные вопросы

  • Нет связанных вопросов^_^