Skip to main content

How to Resolve Warning "CS0278: 'Type' does not implement the 'collection' pattern. 'GetEnumerator' is ambiguous with 'GetEnumerator'." in C#

The Compiler Warning CS0278 is a pattern matching ambiguity warning. The message reads: "'Type' does not implement the 'collection' pattern. 'GetEnumerator' is ambiguous with 'GetEnumerator'."

In C#, statements like foreach and await foreach rely on Pattern Matching (often called "Duck Typing"). To make a foreach loop work, the compiler looks for a method named GetEnumerator() on the object. If the compiler finds two or more valid GetEnumerator() methods (usually coming from different interfaces) and cannot decide which one is the "correct" one, it issues this warning and refuses to compile the loop logic effectively.

This guide explains why this ambiguity arises in interface hierarchies and how to resolve it.

Understanding the Foreach Pattern

When you write foreach (var item in myCollection), the compiler does not require myCollection to strictly implement IEnumerable. Instead, it looks for:

  1. A method named GetEnumerator().
  2. That returns an object with a MoveNext() method and a Current property.

If myCollection is an interface that inherits from multiple other interfaces, and more than one of those parents defines GetEnumerator(), the compiler sees two available paths. Without a clear "winner" (like an override or shadow), the pattern lookup fails.

Scenario: The Ambiguous Interface

This warning typically occurs when creating a "Composite Interface" that inherits from two independent interfaces, both of which are iterable.

Example of error: ICombined inherits two versions of GetEnumerator, and neither one hides the other.

using System.Collections;

// Interface 1: Has iteration capability
public interface IJobQueue
{
IEnumerator GetEnumerator();
}

// Interface 2: Also has iteration capability
public interface IProcessList
{
IEnumerator GetEnumerator();
}

// The Problem: Inherits BOTH.
// Neither is "more specific" than the other.
public interface ISystem : IJobQueue, IProcessList
{
}

public class Program
{
public static void Run(ISystem sys)
{
// ⛔️ Warning CS0278: 'ISystem' does not implement the 'collection' pattern.
// 'IJobQueue.GetEnumerator()' is ambiguous with 'IProcessList.GetEnumerator()'.
foreach (var item in sys)
{
// ...
}
}
}

Solution 1: Explicit Casting (The Consumer Fix)

If you are consuming a library and cannot change the interface definitions, you must tell the compiler explicitly which path to take. Cast the variable to one of the specific parent interfaces.

Solution:

public class Program
{
public static void Run(ISystem sys)
{
// ✅ Correct: Cast to 'IJobQueue'.
// The compiler now sees only one 'GetEnumerator'.
foreach (var item in (IJobQueue)sys)
{
System.Console.WriteLine(item);
}
}
}

Solution 2: Interface Shadowing (The API Fix)

If you maintain the code for the interfaces, you should resolve the ambiguity in the definition of the child interface (ISystem). You can do this by re-declaring GetEnumerator in the child interface using the new keyword. This "shadows" (hides) the parent versions, giving the compiler a single, preferred method to pick.

Solution: update ISystem to provide a definitive GetEnumerator.

public interface ISystem : IJobQueue, IProcessList
{
// ✅ Correct: The 'new' keyword tells the compiler:
// "When using ISystem, use THIS method, not the parents'."
new IEnumerator GetEnumerator();
}

public class Program
{
public static void Run(ISystem sys)
{
// Now valid without casting, because ISystem.GetEnumerator is the clear winner.
foreach (var item in sys)
{
}
}
}
note

Why does IEnumerable<T> work? IEnumerable<T> inherits from IEnumerable. Why doesn't this cause ambiguity? Because IEnumerable<T> explicitly shadows the non-generic version (essentially doing what Solution 2 describes), allowing the compiler to prefer the generic version automatically.

Conclusion

CS0278 indicates a "Fork in the Road" that the compiler cannot navigate.

  1. Identify the Source: Which two interfaces are providing the conflicting method (usually GetEnumerator)?
  2. Consumer Fix: Use a cast (InterfaceA)variable in the foreach loop to select a specific implementation.
  3. Owner Fix: Add new IEnumerator GetEnumerator(); to the child interface to clarify which method should be the default for iteration.