C#泛型重载 - 编译器无法确定正确的调用

时间:2009-06-08 15:21:57

标签: c# generics overloading

我不明白为什么编译器无法解决在这里使用的正确重载。 (下面的代码)只有一个版本的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 个答案:

答案 0 :(得分:6)

通用重载解析不考虑约束,因此它认为Add<T>版本适用,推断T=int

这两种方法都适用,并且两种方法都不比其他方法更好,因为IEnumerable<int>IFoo之间没有转换。虽然泛型方法被认为比非泛型方法“更不具体”,但只有在类型参数替换后参数类型相同时才会变得相关,在这种情况下它们不是。

答案 1 :(得分:1)

在FooContainer中,在第二个“添加”上,您将T限制为IFoo类型。 BigFoo实现了IFoo接口,因此它与Add定义匹配(即使它没有真正,因为它没有实现IEnumable&lt; IFoo&gt;)。

我不确定我完全理解你想要什么,但我怀疑是这样:

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

允许你添加任何对象T,其中T是一组可枚举的IFoo对象。

这就是你想要的吗?

此致 理查德

答案 2 :(得分:0)

编译器应该足够聪明,能够识别BigFoo无法转换为IEnumerable<IFoo>,但事实并非如此。它只是看到它是IEnumerable<T>,并认为它是一个潜在的超载候选者(即使您定义的约束强制执行T必须IFooint不能被投到IFoo)。虽然不方便,但这并不是什么大不了的事。只需将bigFoo转换为IFoo,编译器就会很高兴:

fooContainer.Add((IFoo)bigFoo);

或者,您可以进行Add uglier:

的泛型重载
public void Add<T, U>(U group)
    where T : IFoo
    where U : IEnumerable<T>
{
}

无论哪种方式,你有更多的输入,第二个解决方案消除了对Add的调用的需要,但你必须显式声明对泛型add的调用类型(最终会有更多代码:< / p>

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

答案 3 :(得分:0)

这里的问题是泛型类型约束被编译器完全忽略(它只查看参数类型)。就编译器而言,传递的IEnumerable<T>参数也可以是IEnumerable<IFoo>

有关此主题的完整信息,请参阅{{3>}的 25.6.4类型参数推断部分。请注意,没有提及类型约束的使用。

答案 4 :(得分: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 { }