Skip to content

Commit 27a54b7

Browse files
CopilotBillWagner
andauthored
Clarify interface implementation accessibility requirements and provide comprehensive guidance for internal interfaces (#48050)
* Initial plan * Add clarification and example for interface implementation accessibility requirements Co-authored-by: BillWagner <[email protected]> * Address review feedback: clarify internal interface implementation capabilities and add comprehensive example Co-authored-by: BillWagner <[email protected]> * Update docs/csharp/fundamentals/types/interfaces.md --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: BillWagner <[email protected]> Co-authored-by: Bill Wagner <[email protected]>
1 parent 3682fd5 commit 27a54b7

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

docs/csharp/fundamentals/types/interfaces.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ For more information about abstract classes, see [Abstract and Sealed Classes an
2626

2727
Interfaces can contain instance methods, properties, events, indexers, or any combination of those four member types. Interfaces may contain static constructors, fields, constants, or operators. Beginning with C# 11, interface members that aren't fields may be `static abstract`. An interface can't contain instance fields, instance constructors, or finalizers. Interface members are public by default, and you can explicitly specify accessibility modifiers, such as `public`, `protected`, `internal`, `private`, `protected internal`, or `private protected`. A `private` member must have a default implementation.
2828

29-
To implement an interface member, the corresponding member of the implementing class must be public, non-static, and have the same name and signature as the interface member.
29+
To implement an interface member using implicit implementation, the corresponding member of the implementing class must be public, non-static, and have the same name and signature as the interface member. However, when an interface is meant to be internal only or uses internal types in its signature, you can use explicit interface implementation instead, which doesn't require the implementing member to be public.
3030

3131
> [!NOTE]
3232
> When an interface declares static members, a type implementing that interface may also declare static members with the same signature. Those are distinct and uniquely identified by the type declaring the member. The static member declared in a type *doesn't* override the static member declared in the interface.
@@ -43,6 +43,20 @@ Interfaces can inherit from one or more interfaces. The derived interface inheri
4343

4444
A base class can also implement interface members by using virtual members. In that case, a derived class can change the interface behavior by overriding the virtual members. For more information about virtual members, see [Polymorphism](../object-oriented/polymorphism.md).
4545

46+
## Working with internal interfaces
47+
48+
An internal interface can typically be implemented using implicit implementation with public members, as long as all the types in the interface signature are publicly accessible. However, when an interface uses internal types in its member signatures, implicit implementation becomes impossible because the implementing class member would need to be public while exposing internal types. In such cases, you must use explicit interface implementation.
49+
50+
The following example shows both scenarios:
51+
52+
:::code language="csharp" source="./snippets/interfaces/interfaces.cs" ID="InternalInterfaceExample":::
53+
54+
In the preceding example, the `IConfigurable` interface uses an internal type `InternalConfiguration` in its method signature. The `ServiceImplementation` class cannot use implicit implementation because that would require making the `Configure` method public, which isn't allowed when the method signature contains internal types. Instead, explicit interface implementation is used, which doesn't have an access modifier and is only accessible through the interface type.
55+
56+
In contrast, the `ILoggable` interface can be implemented implicitly with public members because all types in its signature (`string`) are publicly accessible, even though the interface itself is internal.
57+
58+
For more information about explicit interface implementation, see [Explicit Interface Implementation](../../programming-guide/interfaces/explicit-interface-implementation.md).
59+
4660
## Interfaces summary
4761

4862
An interface has the following properties:

docs/csharp/fundamentals/types/snippets/interfaces/interfaces.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,46 @@ public bool Equals(Car? car)
2828
}
2929
//</SnippetImplementEquatable>
3030

31+
// <SnippetInternalInterfaceExample>
32+
// Internal type that cannot be exposed publicly
33+
internal class InternalConfiguration
34+
{
35+
public string Setting { get; set; } = "";
36+
}
37+
38+
// Internal interface that CAN be implemented with public members
39+
// because it only uses public types in its signature
40+
internal interface ILoggable
41+
{
42+
void Log(string message); // string is public, so this works with implicit implementation
43+
}
44+
45+
// Interface with internal accessibility using internal types
46+
internal interface IConfigurable
47+
{
48+
void Configure(InternalConfiguration config); // Internal type prevents implicit implementation
49+
}
50+
51+
// This class shows both implicit and explicit interface implementation
52+
public class ServiceImplementation : ILoggable, IConfigurable
53+
{
54+
// Implicit implementation works for ILoggable because string is public
55+
public void Log(string message)
56+
{
57+
Console.WriteLine($"Log: {message}");
58+
}
59+
60+
// Explicit implementation required for IConfigurable because it uses internal types
61+
void IConfigurable.Configure(InternalConfiguration config)
62+
{
63+
// Implementation here
64+
Console.WriteLine($"Configured with: {config.Setting}");
65+
}
66+
67+
// If we tried implicit implementation for IConfigurable, this wouldn't compile:
68+
// public void Configure(InternalConfiguration config) // Error: cannot expose internal type
69+
}
70+
// </SnippetInternalInterfaceExample>
71+
3172

3273
}

0 commit comments

Comments
 (0)