Использование условного оператора (?:) для выбора метода в С# (3.0)?
Я рефакторинг кода.
В настоящее время существует довольно много мест с такими функциями:
string error;
if (a) {
error = f1(a, long, parameter, list);
}
else {
error = f2(the_same, long, parameter, list);
}
перед рефакторингом f1 и f2 (которые являются большими, но делают подобные вещи), я бы хотел реорганизовать:
string error = (a ? f1 : f2)(a, long, parameter, list);
Как и в C. (Подписи функций идентичны)
Но я получаю сообщение об ошибке:
"Ошибка 13 Тип условного выражения не может быть определен, потому что не существует неявного преобразования между" группой методов "и" группой методов "
Это позволило бы мне признать, что списки параметров идентичны первоначальным рефакторингом, дающим инвариантное поведение, а также рефакторируют вызовы в одном месте, гарантируя, что все в ходе этих различных рефакторингов ничего не сломается при изменении вызывающего интерфейса к методу.
Я пропустил что-то маленькое, что позволило бы синтаксис, близкий к этому, работать (в отличие от целой группы дополнительных определений типов делегатов и т.д.)?
Извините, что редактировал, но на самом деле есть возвращаемое значение, и да, к сожалению, это строка.; - (
Сейчас я соглашаюсь на это:
string error = a ? f1(a, long, parameter, list) : f2(a, long, parameter, list);
Проблема в том, что список параметров действительно очень длинный и собирается получить рефакторинг, и я предпочел бы, чтобы они сначала консолидировались и обрабатывали ошибки компилятора, когда я их изменяю.
Ответы
Ответ 1
Вы можете это сделать, объявив делегата, как вы указали.
Я замечаю, что вы написали, что делаете это в нескольких местах. Другой альтернативой, которая может быть более подходящей, является использование интерфейсов. Создайте один из двух разных типов в зависимости от значения a, затем вызовите метод на этом объекте.
IFoo foo = a ? new Foo1() : new Foo2();
foo.f(a, long, parameter, list);
Если у вас есть несколько методов, которые необходимо изменить одновременно в зависимости от значения a
, тогда вы можете включить их все в один и тот же интерфейс, и вам нужно будет только один раз проверить a
.
Ответ 2
Для работы ?
компилятору нужен явный тип для хотя бы одного из операндов. Вы можете предоставить его здесь с помощью оператора трансляции
(a ? (Action<T1,T2,T3,T4>)f1 : f2)(a, long, parameter, list);
Замените T*
на фактические типы параметров делегата
Ответ 3
Вам необходимо создать экземпляр одного из методов в качестве определенного типа совместимого делегата. Там просто нет пути. Это, к сожалению, будет немного более подробным, чем вы ищете:
(a ? new Action<T1, T2, T3, T4>(f1) : f2)(a, long, parameter, list);
Вам нужно будет сделать параметры явными, будь то с помощью общей перегрузки Action
(или Func
), которая соответствует или объявляет ваш собственный тип делегата.
Это сводится к разрешению типа. Учитывая выражение:
condition ? tVal : fVal
Компилятор не ищет общих совместимых с назначением предков для tVal
и fVal
(и, даже если бы это произошло, может быть огромным множество разных типов делегатов, для которых каждый может быть действительным); если между типами tVal
и fVal
нет совместимости присвоений в любом направлении, компилятор делает вас явным в отношении того, что вы хотите.
Для чего вам стоит знать, что при таком подходе каждый раз при вызове этого метода будет выделен новый делегат либо f1
, либо f2
, тогда этот делегат будет вызываться, а затем отбрасываться. Я выношу это только потому, что вызов делегата медленнее обычного вызова метода ранней привязки (или даже виртуального). Это может не быть соображением, и компромисс может стоить того, но он все равно стоит знать.
Ответ 4
Если вы укажете тип делегата, это позволит вам делать то, что вы просите:
(test ? (Action<int,int>)M1 : M2)(10, 15)
С объявлениями:
void M1(int a, int b)
{
}
void M2(int a, int b)
{
}
Протестировано с .Net 4, но должно применяться для .Net 3.5
Ответ 5
Нет, в основном, не делая его менее эффективным. Если есть возвращаемое значение, вы можете использовать:
var result = cond ? methodA(a,b,c,d) : methodB(a,b,c,d);
но об этом.
Ну, вы можете создать пару делегатов из группы методов, но это добавляет накладные расходы без уважительной причины. Я не буду его одобрять.
Ответ 6
Я думаю, что что-то вроде следующего будет работать лучше:
a? f1 (a, long, параметр, список): f2 (a, long, параметр, список);
Ответ 7
Перенесите результат тройного на Action
((Action<int, int>)(a ? f1 : f2))(int1, int2);