
org.sonar.plugins.csharp.S4023.html Maven / Gradle / Ivy
Show all versions of sonarlint-omnisharp-plugin Show documentation
Empty interfaces should be avoided as they do not provide any functional requirements for implementing classes.
Why is this an issue?
Empty interfaces are either useless or used as a marker. Custom attributes are a better alternative to marker
interfaces. See the How to fix it section for more information.
Exceptions
This rule doesn’t raise in any of the following cases:
Aggregation of multiple interfaces
public interface IAggregate: IComparable, IFormattable { } // Compliant: Aggregates two interfaces
Generic specialization
An empty interface with a single base interface is compliant as long as the resulting interface binds a generic parameter or constrains it.
// Compliant: Bound to a concrete type
public interface IStringEquatable: IEquatable<string> { }
// Compliant: Specialized by type parameter constraint
public interface ICreateableEquatable<T>: IEquatable<T> where T: new() { }
Custom attribute
An empty interface is compliant if a custom attribute is applied to the interface.
[Obsolete]
public interface ISorted { } // Compliant: An attribute is applied to the interface declaration
How to fix it
Do any of the following to fix the issue:
- Add members to the interface
- Remove the useless interface
- Replace the usage as a marker interface with custom attributes
Code examples
Noncompliant code example
The empty interface does not add any functionality.
public interface IFoo // Noncompliant
{
}
Compliant solution
Add members to the interface to be compliant.
public interface IFoo
{
void Bar();
}
Noncompliant code example
A typical use case for marker interfaces is doing type inspection via reflection as shown below.
The IIncludeFields
marker interface is used to configure the JSON serialization of an object.
// An example marker interface
public interface IIncludeFields { }
public class OptInToIncludeFields: IIncludeFields { }
Serialize(new OptInToIncludeFields());
void Serialize<T>(T o)
{
// Use reflection to check if the interface is applied to the type
var includeFields = o.GetType()
.GetInterfaces().Any(i => i == typeof(IIncludeFields));
var options = new JsonSerializerOptions()
{
// Take decisions based on the presence of the marker
IncludeFields = includeFields,
};
}
The same example can be rewritten using custom attributes. This approach is preferable because it is more fine-grained, allows parameterization,
and is more flexible in type hierarchies.
// A custom attribute used as a marker
[AttributeUsage(AttributeTargets.Class)]
public sealed class IncludeFieldsAttribute: Attribute { }
[IncludeFields]
public class OptInToIncludeFields { }
Serialize(new OptInToIncludeFields());
void Serialize<T>(T o)
{
// Use reflection to check if the attribute is applied to the type
var includeFields = o.GetType()
.GetCustomAttributes(typeof(IncludeFieldsAttribute), inherit: true).Any();
var options = new JsonSerializerOptions()
{
// Take decisions based on the presence of the marker
IncludeFields = includeFields,
};
}
Resources
Documentation
- Microsoft Learn - Interfaces - define behavior for
multiple types
- Wikipedia - Marker interface pattern
- Microsoft Learn - Writing custom
attributes