All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.plugins.csharp.S4023.html Maven / Gradle / Ivy

There is a newer version: 10.2.0.105762
Show newest version

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





© 2015 - 2024 Weber Informatics LLC | Privacy Policy