Проблема разрешения метода с параметрами и дженериками по умолчанию
Используя .NET 4, я смущен неспособностью компилятора разрешить первый вызов метода в примере ниже.
using System;
namespace MethodResolutionTest
{
class Program
{
static void Main(string[] args)
{
NonGeneric foo = null;
// ambiguous
foo.Ext1(x => new NonGeneric());
// resolves to first Ext1
foo.Ext1(x => new NonGeneric(), 1);
// resolves to first Ext2
foo.Ext2(x => new NonGeneric());
// resolves to first Ext2
foo.Ext2(x => new NonGeneric(), 1);
// resolves to second Ext2
foo.Ext2(x => "foo");
// resolves to second Ext2
foo.Ext2(x => "foo", 1);
// resolves to first Ext3
foo.Ext3(x => new NonGeneric());
// resolves to first Ext3
foo.Ext3(x => new NonGeneric(), 1);
// resolves to second Ext3
foo.Ext3(x => "foo");
// resolves to second Ext3
foo.Ext3(x => "foo", 1);
}
}
public class NonGeneric
{
}
public class Generic<T> : NonGeneric
{
}
public static class Extensions1
{
public static NonGeneric Ext1(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0)
{
return null;
}
public static Generic<TNext> Ext1<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0, string s = null)
{
return null;
}
}
// only difference between Extensions2 and Extensions1 is that the second overload no longer has a default string parameter
public static class Extensions2
{
public static NonGeneric Ext2(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0)
{
return null;
}
public static Generic<TNext> Ext2<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0)
{
return null;
}
}
// Extensions3 explicitly defines an overload that does not default the int parameter
public static class Extensions3
{
public static NonGeneric Ext3(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext)
{
return Ext3(first, getNext, default(int));
}
public static NonGeneric Ext3(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext, int i = 0)
{
return null;
}
public static Generic<TNext> Ext3<TNext>(this NonGeneric first, Func<NonGeneric, TNext> getNext, int i = 0)
{
return null;
}
}
}
Может ли кто-нибудь пролить свет на это? Я подозреваю, что у меня действительно нет возможности продвинуться вперед, кроме изменения моих API, чтобы помочь компилятору (согласно Extensions3
выше), но если есть более простой/лучший способ, я бы хотел его услышать.
Ответы
Ответ 1
Это неоднозначно, потому что у вас есть два необязательных параметра во втором методе расширения Ext1
. Поскольку оба параметра опускаются при первом вызове, компилятор не знает, какой из них вы хотите использовать.
От Спецификация языка С# 4.0:
§7.5.3 Разрешение перегрузки:
Учитывая набор применимых элементов-кандидатов-кандидатов, находится лучший член функции в этом наборе. Если набор содержит только один член функции, то этот член функции является лучшим членом функции. В противном случае лучшим членом функции является один член функции, который лучше всех остальных членов функции по отношению к данному списку аргументов при условии, что каждый член функции сравнивается со всеми другими членами функции, используя правила в разделе 7.5.3.2. Если не существует ни одного члена функции, который лучше всех других членов функции, то вызов функции-члена является неоднозначным и возникает ошибка времени привязки.
Кроме того, в разделе §7.5.3.2 Более эффективный член функции:
Необязательные параметры без соответствующих аргументов удаляются из списка параметров
Это означает, что когда вы опускаете два последних аргумента в вызове метода и выводится тип NonGeneric
(читайте о выражении типа в соответствии с §7.5.2), оба метода выглядят следующим образом:
Ext1(this NonGeneric first, Func<NonGeneric, NonGeneric> getNext)
Таким образом, они были бы двусмысленными...
Я бы рекомендовал прочитать дополнительную информацию в §7.5.3.2 или даже в целом в разделе 7.5.5 спецификации.
Решение состоит в том, чтобы либо сменить объявления вашего метода, либо полностью удалить первую перегрузку, а затем выполнить вторую работу:)