Подтвердить что ты не робот

Проблема разрешения метода с параметрами и дженериками по умолчанию

Используя .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 выше), но если есть более простой/лучший способ, я бы хотел его услышать.

4b9b3361

Ответ 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 спецификации.

Решение состоит в том, чтобы либо сменить объявления вашего метода, либо полностью удалить первую перегрузку, а затем выполнить вторую работу:)