-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Description
InternalImplementationOnly attribute
One difficulty that faces API authors is that sometimes an interface is the most appropriate type for an API element (because there are multiple unrelated implementations internally). However, once a public API element is exposed as an interface it would be a breaking change to add additional methods in a future revision of the interface (clients who implement the interface would be broken because they do not implement the new members).
This issue is mitigated when the API is intended to be implemented only by its authoring assemblies. However, there is no language mechanism to enforce that clients do not implement the interface. API authors resort to documentation suggesting that the interface not be implemented in customer code, but customers can fail to notice the documentation resulting in painful compatibility constraints that are unintended.
The Roslyn APIs have an example of this issue with the ISymbol interface and all of its derived interfaces such as IMethodSymbol and INamedTypeSymbol. We intend to evolve these interfaces in the future as the language changes, but we may be prevented from doing so because customer code may extend these interfaces.
To mitigate this compatibility constraint we propose to add a compiler-provided error. Interfaces such as ISymbol that should only be extended or implemented in assemblies that have internal access are marked with an attribute of the name System.Runtime.CompilerServices.InternalImplementationOnlyAttribute.
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Indicates that the interface may only appear in the base clause of a type
/// in the same assembly as the attributed interface or a type in
/// an assembly that has InternalsVisibleTo the attributed interface's
/// assembly.
/// </summary>
[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false)]
internal sealed class InternalImplementationOnlyAttribute : Attribute
{
}
}This interface may be provided as a public type in a platform assembly, or as an internal type (as above) when the referenced platform does not yet provide direct support for the attribute.
When a compiler sees that such an attributed interface is extended or implemented outside of an assembly that has internal access, a compile-time error is produced.
namespace Microsoft.CodeAnalysis
{
/// <summary>
/// Represents a symbol (namespace, class, method, parameter, etc.)
/// exposed by the compiler.
/// </summary>
[InternalImplementationOnly]
public interface ISymbol : IEquatable<ISymbol>
{By adding this attribute to interfaces such as ISymbol, an API author can have some confidence that future additions to the interface will not break compatibility with existing clients, as those clients will have been prevented from implementing the interface.