Каков наилучший способ иметь дело с необязательными делегатами в конструкторе 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();
}