Каков наилучший способ иметь дело с необязательными делегатами в конструкторе aC#?

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

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, а затем перехватить его в главном конструкторе? Это не очень элегантно, и мне бы хотелось, чтобы в идеале можно было ловить нулевое действие как исключение (например, если внешний вызывающий объект использовал нулевое значение вместо использования перегруженного конструктора). Я мог бы удалить readonly от делегатов, но опять же это не похоже на отличное решение, так как они действительно readonly.

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

2 ответа

Решение

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

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

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

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 пример.

Я что-то пропустил?

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,

РЕДАКТИРОВАТЬ: Относительно обработки исключений. А как насчет использования внутреннего конструктора в качестве "главного"- одного, который все остальные вызывают с параметром, указывающим, откуда пришел вызов:

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();
}
Другие вопросы по тегам