5

我不明白为什么编译器无法解决要在此处使用的正确重载。(下面的代码)只有一个版本的 Add() 是合适的——BigFoo 是一个 IFoo,并且不实现 IEnumerable,其中 T 是一个 IFoo。但它坚持报告模棱两可。有任何想法吗?我尝试添加第二个泛型类型参数 - 添加 where T : IFoo where 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);
    }
  }
}
4

5 回答 5

6

通用重载决议不考虑约束,因此它认为Add<T>版本是适用的,推断T=int.

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

于 2009-06-08T15:27:06.240 回答
1

在 FooContainer 中,在第二个“添加”上,您将 T 限制为 IFoo 类型。BigFoo 实现了 IFoo 接口,因此它与 Add 定义有点匹配(尽管它不是真的,因为它没有实现 IEnumable<IFoo>)。

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

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

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

那是你想要的吗?

问候,理查德

于 2009-06-08T15:33:29.490 回答
0

编译器应该足够聪明,可以识别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,但是您必须在调用泛型 add 时显式声明类型(最终成为更多代码:

fooContainer.Add<IFoo, IEnumerable<IFoo>>(enumerableFoo);
于 2009-06-08T15:28:56.050 回答
0

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

有关此主题的完整信息,请参阅第25.6.4节C# 语言规范的类型参数推断。请注意,没有提及类型约束的使用。

于 2009-06-08T15:40:54.697 回答
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 { }
于 2009-06-08T16:05:53.307 回答