C # общая перегрузка - компилятор не может определить правильный вызов - PullRequest
5 голосов
/ 08 июня 2009

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

Я знаю, что могу обойти это с приведением и определением параметров универсального типа, но в тот момент я потерпел поражение от цели перегрузки. Вы могли бы подвергнуть сомнению перегрузку, но семантика кажется мне правильной - поведение, которое я реализую в своем классе, заключается в том, что обе 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);
    }
  }
}

Ответы [ 5 ]

6 голосов
/ 08 июня 2009

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

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

1 голос
/ 08 июня 2009

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

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

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

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

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

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

0 голосов
/ 08 июня 2009

Интересно .... Только что попробовал ваш образец. Дженерики продолжают держать меня в напряжении.

//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 { }
0 голосов
/ 08 июня 2009

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

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

0 голосов
/ 08 июня 2009

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

fooContainer.Add((IFoo)bigFoo);

Кроме того, вы можете сделать общую перегрузку Add uglier:

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

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

fooContainer.Add<IFoo, IEnumerable<IFoo>>(enumerableFoo);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...