我正在收集,显式实现IEnumerable
并尝试从内部迭代它:
public class MyCollection<T> : IEnumerable<T>, IEnumerable
{
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator<T> GetEnumerator() { yield return default(T); } // test
public void Test()
{
foreach (var item in this) { } // here is warning
}
}
我在this
收到编译器警告:
警告 CS0279"我的集合"不实现"集合"模式。'MyCollection.GetEnumerator((' 要么是静态的,要么不是公共的。
地狱是的,它不是公开的。为什么会这样?我可以将其公开,但类型之外的foreach
不需要它:
foreach (var item in new MyCollection<string>()) { } // no warning
我做错了什么吗?
存在此警告是因为 C# 编译器可以通过多种不同的方式处理foreach
。其中一种方法是找到具有合适返回类型的GetEnumerator
方法。在编译器检查表达式的类型是否实现IEnumerable
或IEnumerable<T>
之前,会检查该参数。
在您的情况下,它可以找到单个无参数GetEnumerator
方法,但它不是公共的。C# 规范建议在此时发出警告,因为您可能希望它可用于foreach
。从 C# 5 规范第 8.8.4 节中,强调我的:
- 使用生成的方法组和空参数列表执行重载解析。如果重载解析导致没有适用的方法、导致歧义或导致单个最佳方法,但该方法是静态的或非公共的,请检查可枚举的接口,如下所述。如果重载解析产生除明确公共实例方法之外的任何内容或没有适用方法,建议发出警告。
以下任何一种方法都可以解决问题:
-
将
GetEnumerator
重命名为GetEnumeratorImpl
或类似名称:IEnumerator IEnumerable.GetEnumerator() => GetEnumeratorImpl(); IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumeratorImpl(); IEnumerator<T> GetEnumeratorImpl() { yield return default(T); }
-
不要对
IEnumerable<T>.GetEnumerator()
使用显式接口实现 - 将实现放在那里IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public IEnumerator<T> GetEnumerator() => { yield return default(T); }
-
将实现放在
IEnumerable<T>.GetEnumerator
中,但IEnumerable.GetEnumerator
this
转换为IEnumerable<T>
以调用它:IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<T>) this).GetEnumerator(); IEnumerator<T> IEnumerable<T>.GetEnumerator() => { yield return default(T); }
请参阅编译器警告 CS0279 的说明:https://msdn.microsoft.com/en-us/library/bz2286x8(v=vs.90(.aspx
C# 中有几个语句依赖于定义的模式,例如 作为每个和使用。例如,foreach 依赖于集合 实现可枚举模式的类。当 由于声明了方法,编译器无法进行匹配 静态或不公开。模式中的方法必须是实例 的类,并且是公开的。
(强调我的(