2009-06-08 3 views
5

Я не понимаю, почему компилятор не может решить правильную перегрузку для использования здесь. (код ниже) Существует только одна версия Add(), которая подходит - BigFoo является IFoo и не реализует IEnumerable, где T является IFoo. Но он настаивает на том, чтобы сообщать о двусмысленности. Есть идеи? Я попытался добавить второй общий тип параметра - Добавить где T: IFoo, где U: IEnumerable. Но тогда перегрузка полностью игнорируется даже для законного использования.C# общая перегрузка - компилятор не может определить правильный вызов

Я знаю, что могу обойти это с помощью литья и определения параметров типового типа, но в этот момент я победил цель перегрузки. Вы можете подвергнуть сомнению перегрузку, но семантика мне кажется верной - поведение, которое я реализую в своем классе, используется как для Add(), так и для добавления объекта в качестве отдельной записи в коллекции. (Второй Add() не должен быть AddRange().)

namespace NS 
{ 
    interface IFoo { } 

    class BigFoo : IFoo, IEnumerable<int> 
    { 
    public IEnumerator<int> GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
    IEnumerator IEnumerable.GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
    } 

    class FooContainer 
    { 
    public void Add(IFoo item) { } 
    public void Add<T>(IEnumerable<T> group) where T : IFoo { } 
    } 

    class DemoClass 
    { 
    void DemoMethod() 
    { 
     BigFoo bigFoo = new BigFoo(); 
     FooContainer fooContainer = new FooContainer(); 
     // error CS0121: The call is ambiguous between the following methods or properties: 
     // 'NS.FooContainer.Add(NS.IFoo)' and 
     // 'NS.FooContainer.Add<int>(System.Collections.Generic.IEnumerable<int>)' 
     fooContainer.Add(bigFoo); 
    } 
    } 
} 

ответ

6

Generic разрешение перегрузки не принимать во внимание ограничения, поэтому он считает версию Add<T> применимой, выводя T=int.

Оба метода применимы, и ни один из них не определен лучше другого, поскольку нет никакого преобразования между IEnumerable<int> и IFoo. В то время как общие методы считаются «менее конкретными», чем не общие методы, это становится актуальным только тогда, когда типы параметров идентичны после замены аргумента типа, которые они не в этом случае.

+0

Джефф Рихтер согласен «компилятор С # предпочитает более явное совпадение по родовым матч» Display («Jeff») будет соответствовать Display (String) над дисплеем (T) только – Gishu

+1

законсервированным нарушением правил применяется, если формальные типы параметров ИДЕНТИФИКАЦИЯ. Например, если у вас есть M (int x) и M (T t), то первый лучше, чем M (int t). –

+0

Ах, спасибо Эрик. Хорошо иметь спецификацию онлайн и вносить свой вклад;) Будет правильно отредактировать. –

0

Компилятор должен быть достаточно умным, чтобы признать, что BigFoo не может быть отлит до IEnumerable<IFoo>, но это не так. Он просто видит, что это IEnumerable<T>, и чувствует, что это потенциальный кандидат на перегрузку (хотя выбранный вами предел, который вы определили, устанавливает, что T должен быть IFoo и int не может быть отлит до IFoo). Хотя это неудобно, дело не в этом. Просто бросить bigFoo в IFoo и компилятор будет счастлив:

fooContainer.Add((IFoo)bigFoo); 

В качестве альтернативы, вы можете сделать свою родовую перегрузку Добавить уродливее:

public void Add<T, U>(U group) 
    where T : IFoo 
    where U : IEnumerable<T> 
{ 
} 

В любом случае, у вас есть более типизация, вторые устраняют решения необходимость бросить вызовы Add, но вам придется явно объявить тип на звонки в родовом оных (который заканчивает тем более код:

fooContainer.Add<IFoo, IEnumerable<IFoo>>(enumerableFoo); 
1

В FooContainer во втором «Добавить» вы сдерживаете T типа IFoo. BigFoo реализует интерфейс IFoo, поэтому он подходит к определению Add Add (хотя это и не реально, потому что он не реализует IEnumable <IFoo>).

Я не уверен, что я полностью то, что вы хотите понять, но я подозреваю, что это:

public void Add<T>(T group) where T : IEnumerable<IFoo> { } 

, который позволит вам добавить любой объект T, где T является перечислимым множеством объектов IFoo.

Это то, что вы хотели?

С уважением, Ричард

+0

ограничение T на IEnumerable вместо IFoo по-прежнему не помогает при разрешении перегрузки. Как сказал Джон Скит, компилятор (к сожалению) не будет учитывать ограничения учетной записи. –

+0

Ах ... Круто. Узнал что-то новое. :-) Спасибо, что нашли время для комментариев - я действительно думал, что компилятор рассмотрит ограничения в предложении «где», даже если он не стал рассматривать их в другом месте подписи метода. –

0

Проблема здесь состоит в том, что общие ограничения типа полностью игнорировали компилятором (он смотрит только на типы параметров). Что касается компилятора, то передаваемый аргумент IEnumerable<T> также может быть IEnumerable<IFoo>.

Для получения полной информации по этому вопросу см. Раздел . 25.6.4 Вывод аргументов типа из C# Language Specification. Обратите внимание, что упоминание об использовании ограничений типа не упоминается.

+0

Это не подходящее место для поиска правил спецификации, которые связаны с этим вопросом. Соответствующий раздел в спецификации 3.0 - 7.5.5.1, бит, начинающийся с «окончательной проверки выбранного наилучшего метода». Как вы можете видеть из этого раздела, проверка на нарушения ограничений выполняется ПОСЛЕ проверки уникальности набора кандидатов. –

+0

Ах, правильно. Разумеется, этот раздел спецификации, по крайней мере, предполагает, что ограничения типа общего типа * не учитываются * во время процесса вывода? – Noldorin

+0

Действительно, это также случай, когда ограничения не используются во время вывода. –

0

Интересно .... Просто попробовал выборку. Дженерики продолжают держать меня на ногах.

//1 - First preference 
public void Add(BigFoo item) { Console.WriteLine("static BigFoo type Add"); } 
//2 - Second Preference 
public void Add<T>(T item) { Console.WriteLine("Generic Add"); } 
//3 - Third preferences 
public void Add(IFoo item) { Console.WriteLine("static IFoo interface Add"); } 
//4 - Compiles if 1-4 exist. Compile error (ambiguity) if only 3-4 exist. Compile error (cannot convert int to IFoo) if only 4 exists 
public void Add<T>(IEnumerable<T> group) where T : IFoo { } 
Смежные вопросы