С# Почему этот тип метода вывода выводится неоднозначно?
Следующая пара функций пытается реплицировать нулевой условный оператор, доступный в С# 6.0:
public static TResult Bind<T, TResult>(this T obj, Func<T, TResult> func)
where T : class
{
return obj == null ? default(TResult) : func(obj);
}
public static TResult Bind<T, TResult>(this Nullable<T> obj, Func<T, TResult> func)
where T : struct
{
return obj.HasValue ? func(obj.Value) : default(TResult);
}
Первая функция ограничена классами, а для String s
позволяет написать что-то вроде:
var x = s.Bind(a => a.Substring(1));
Вторая функция - это проблема, с которой я сталкиваюсь. Например, для a int? number
я хотел бы написать:
var y = number.Bind(a => a + 1);
Однако это дает мне следующую ошибку:
Вызов неоднозначен между следующими методами или свойствами: 'BindingExtensions.Bind < T, TResult > (T, Func < T, TResult > )' и 'BindingExtensions.Bind < T, TResult > (T?, Func < T, TResult > ) '
Я предполагаю, что это имеет какое-то отношение к взаимодействию между типом вывода анонимной функции и разрешением перегрузки метода. Если я укажу тип a как int
, чем он компилируется просто отлично.
var y = number.Bind((int a) => a + 1);
Однако это явно меньше желаемого. Может ли кто-нибудь сказать мне, почему компилятор считает, что вышеупомянутый вызов привязки неоднозначен и/или предлагает способ исправить это? Я знаю, что могу просто назвать две функции по-разному, но что это за забава?
Ответы
Ответ 1
Перегруженные функции не могут быть устранены ограничениями типа (см. " "Общие ограничения" , где T: struct и где T: class" ). Любой тип N с нулевым значением удовлетворяет N : T
и N : Nullable<T>
, требуемому соответственно последним и последним Bind
. Я предполагаю, что number
имеет тип Nullable<int>
или аналогичный.
var x = s.Bind(a => a.Substring(1));
Это однозначно, потому что s
имеет тип string
и для всех T
не string : Nullable<T>
, поэтому допустима только первая перегрузка.
var y = number.Bind(a => a + 1);
Это неоднозначно, потому что тип a => a + 1
может быть выведен как либо Func<int?,int?>
, либо Func<int,int>
. Если предполагается, что в качестве Func<int?,int?>
применяется первая перегрузка, и если она определена как Func<int,int>
, применяется вторая перегрузка.
var y = number.Bind((int a) => a + 1);
Это недвусмысленно, если number
имеет тип Nullable<int>
, например. Для первой перегрузки для всех T
не T : Nullable<int>
и T : int
, поэтому она не применяется. Для второй перегрузки вам просто нужен T : int
, который легко выполняется T = int
.
Ответ 2
Попробуйте следующее:
public static TResult Bind<T, TResult>(this T? obj, Func<T?, TResult> func)
where T : struct
{
return obj.HasValue ? func(obj.Value) : default(TResult);
}