Компилятор сопоставляет расширение, для которого предложение where исключает его [duplicate]

Как будто вы пытаетесь получить доступ к объекту, который является null. Рассмотрим ниже пример:

TypeA objA;

. В это время вы только что объявили этот объект, но не инициализировали или не инициализировали. И всякий раз, когда вы пытаетесь получить доступ к каким-либо свойствам или методам в нем, он будет генерировать NullPointerException, что имеет смысл.

См. Также этот пример:

String a = null;
System.out.println(a.toString()); // NullPointerException will be thrown
4
задан Dennis 25 September 2014 в 13:43
поделиться

3 ответа

Устраняет ли компилятор во всех ограничениях типа типа при разрешении перегрузок?

Нет, поскольку общие ограничения не являются частью сигнатуры функции. Вы можете проверить это, добавив перегрузку Bar, которая идентична, за исключением общих ограничений:

interface IBar { }

static void Bar<T>(IEnumerable<T> value)
    where T : IFoo
{
}

static void Bar<T>(T source)
    where T : IBar
{
    // fails to compile : Type ____ already defines a member called 'Bar' with the same parameter types
}

Причина, по которой ваш код не компилируется, заключается в том, что компилятор выбирает «лучшее» соответствие на основе сигнатура метода , тогда пытается применить общие ограничения.

Одна из возможных причин, по которым это не , состоит в том, что этот вызов будет неоднозначным:

{предположим, что List<T> имеет метод Add<T>(IEnumerable<T> source)}

List<object> junk = new List<object>();
junk.Add(1);   // OK
junk.Add("xyzzy") // OK
junk.Add(new [] {1, 2, 3, 4});  //ambiguous - do you intend to add the _array_ or the _contents_ of the array?

Очевидное исправление заключается в использовании другого имени для метода Bar, который принимает коллекцию (as выполняется в BCL с Add и AddRange.

3
ответ дан D Stanley 20 August 2018 в 09:38
поделиться
  • 1
    Другое исправление заключается в том, чтобы указать параметр T, используя Bar<IFoo>(value); – ken2k 25 September 2014 в 14:06
  • 2
    «общие ограничения не являются частью сигнатуры функции», - правда, к сожалению ... просто набрал подобный образец. – Dennis 25 September 2014 в 14:16

Это проблема ковариации . List<T> не является ковариантным, поэтому между List<FooImpl> и List<IFoo> нет неявного преобразования.

С другой стороны, начиная с C # 4, IEnumerable<T> теперь поддерживает ковариацию, поэтому это работает:

var value = Enumerable.Empty<FooImpl>();
Bar(value);

var value = new List<FooImpl>().AsEnumerable();
Bar(value);

var value = new List<FooImpl>();
Bar((IEnumerable<IFoo>)value);
0
ответ дан ken2k 20 August 2018 в 09:38
поделиться
  • 1
    Интересный момент, но как насчет того, что мы забудем о FooImpl? Я скоро обновлю вопрос. – Dennis 25 September 2014 в 13:33

EDIT: Хорошо, причина, по которой Bar<T>(T source) выбрана над Bar<T>(IEnumerable<T> source), когда передача списка происходит из-за раздела «7.5.3.2 Better function member» ссылки на языке C #. В нем говорится, что при возникновении разрешения перегрузки типы аргументов сопоставляются типам параметров применимых членов функций (раздел 7.5.3.1), а лучший член функции выбирается с помощью следующего набора правил:

• для каждого аргумента неявное преобразование из EX в QX не лучше, чем неявное преобразование из EX в PX и

• по крайней мере для одного аргумента, преобразование из EX в PX лучше, чем преобразование из EX в QX.

(PX - это типы параметров первого метода, QX второго)

Это правило применяется msgstr "после замены расширения и типа аргумента". Поскольку подстановка аргумента типа заменит Bar (источник T) на Bar> (источник IList), этот аргумент метода будет лучше, чем Bar (источник IEnumerable), который нуждается в преобразовании.

Я не мог " t найти онлайн-версию языковой ссылки, но вы можете прочитать ее здесь


РЕДАКТИРОВАТЬ: неправильно понял вопрос, работая над поиском правильного ответа на языке c # спекуляция В основном метод IIRC выбирается с учетом наиболее подходящего типа, и если вы точно не настроили свой параметр на IEnumerable<>, то Bar<T>(T source) будет точно соответствовать типу параметра точно так же, как в этом примере:

public interface ITest { }
public class Test : ITest { }

private static void Main(string[] args)
{
    test(new Test() ); // outputs "anything" because Test is matched to any type T before ITest
    Console.ReadLine();
}


public static void test<T>(T anything)
{
    Console.WriteLine("anything");
}

public static void test(ITest it)
{
    Console.WriteLine("it");
}

Будет ссылаться на него, когда будет найден


Поскольку трансляция между массивом и перечислимым должна быть явной: это компилирует

var value = new FooImpl[0].AsEnumerable();
Bar(value);

, и так это:

var value = new FooImpl[0] as IEnumerable<IFoo>;
Bar(value);

Из doc :

Начиная с .NET Framework 2.0 класс Array реализует System.Collections. Generic.IList, System.Collections.Generic.ICollection и System.Collections.Generic.IEnumerable общие интерфейсы. Реализации предоставляются массивам во время выполнения, и в результате общие интерфейсы не отображаются в синтаксисе объявления для класса Array.

Таким образом, ваш компилятор не знает, что array соответствует сигнатуре для Bar, и вы должны явно использовать его

2
ответ дан samy 20 August 2018 в 09:38
поделиться
  • 1
    Есть ли проблема с ответом, почему downvote? – samy 25 September 2014 в 12:38
  • 2
    Та же проблема возникает, если List & lt; FooImpl & gt; передается. Хотя, я не сторонник – Victor Mukherjee 25 September 2014 в 12:38
  • 3
    @samy: это очень сомнительное предположение. Во-первых, как отметил Виктор, то же самое произойдет с любой другой реализацией IEnumerable<T>. Во-вторых, я понятия не имею, как мой вопрос коррелирует со ссылкой, которую вы предоставили. – Dennis 25 September 2014 в 12:41
  • 4
    Деннис, ссылка объясняет, почему массив не принимается как IEnumerable в Bar, но комментарий ВиктораМухерджи объясняет проблему – samy 25 September 2014 в 12:43
Другие вопросы по тегам:

Похожие вопросы: