How to Resolve Error "CS0229: Ambiguity between 'member1' and 'member2'" in C#
The Compiler Error CS0229 is a Resolution Error. The message reads: "Ambiguity between 'member1' and 'member2'".
This error occurs when the compiler attempts to access a member (like a method, property, or event) via an object reference, but it finds two or more candidates with the same name and cannot determine which one to use. This typically happens with complex Interface Inheritance graphs where two interfaces declare a member with the same name, and a third interface inherits from both without resolving the conflict.
This guide explains how these collisions happen and how to resolve them using explicit casting.
Understanding the Ambiguity
C# allows a class or an interface to inherit from multiple interfaces. If InterfaceA and InterfaceB both contain a member named Status, and InterfaceC inherits from both A and B, using InterfaceC to access Status is ambiguous.
The compiler looks at InterfaceC and asks: "Do you mean the Status defined in A, or the Status defined in B?" Since InterfaceC does not override or hide them with its own definition, both are equally valid and visible, causing the crash.
Scenario 1: Interface Aggregation Collisions
This is the most common cause. You create an "umbrella" interface that combines several others, but those upstream interfaces share naming conventions.
Example of error: defining a combined interface without resolving member conflicts.
public delegate void StatusHandler();
public interface IEngine
{
// Member 1
event StatusHandler OnReady;
}
public interface IDisplay
{
// Member 2 - Same name!
event StatusHandler OnReady;
}
// Aggregated Interface
public interface ISystem : IEngine, IDisplay
{
// ISystem inherits BOTH OnReady events, but defines neither.
}
public class Program
{
static void Main()
{
ISystem sys = null;
// ⛔️ Error CS0229: Ambiguity between 'IEngine.OnReady' and 'IDisplay.OnReady'
// The compiler does not know which event subscription list to update.
sys.OnReady += () => System.Console.WriteLine("Ready");
}
}
Solution 1: Explicit Casting
To resolve the ambiguity, you must guide the compiler by casting the object to the specific interface that contains the member you want to use.
Solution: cast the ISystem variable to IEngine or IDisplay before accessing the member.
public class Program
{
static void Main()
{
// We assume we have a concrete object here
ISystem sys = GetSystem();
// ✅ Correct: Cast to IEngine to use the engine's event
((IEngine)sys).OnReady += () => System.Console.WriteLine("Engine Ready");
// ✅ Correct: Cast to IDisplay to use the display's event
((IDisplay)sys).OnReady += () => System.Console.WriteLine("Display Ready");
}
static ISystem GetSystem() => null; // Dummy return for example
}
This solution does not change the structure of your interfaces. It is a client-side fix that forces you to be explicit every time you use the member.
Solution 2: Interface Shadowing
If you control the definition of the aggregated interface (ISystem), you can resolve the ambiguity for all consumers by adding a new member definition. This "shadows" the upstream members.
Solution: define a new member in the child interface.
public interface ISystem : IEngine, IDisplay
{
// ✅ Correct: This 'new' member hides the inherited ones.
// When users access ISystem.OnReady, they get THIS definition.
new event StatusHandler OnReady;
}
public class Program
{
static void Main()
{
ISystem sys = null;
// ✅ Correct: No longer ambiguous. This accesses ISystem.OnReady.
sys.OnReady += () => System.Console.WriteLine("System Ready");
}
}
Implementation Note: When implementing this interface in a concrete class, you will now have to implement three events: IEngine.OnReady, IDisplay.OnReady, and ISystem.OnReady.
Conclusion
CS0229 is the compiler refusing to guess which inheritance path to follow.
- Identify the Source: Look at the error message to see which two interfaces are providing the conflicting member.
- Quick Fix: Use Explicit Casting
((InterfaceA)obj).Memberto pick a specific path. - Design Fix: If you own the interfaces, consider using the
newkeyword in the child interface to consolidate the members or rename one of them to avoid the collision entirely.