Implement Exported Interface

ID

csharp.implement_exported_interface

Severity

critical

Remediation Complexity

medium

Remediation Risk

low

Remediation Effort

medium

Resource

Code Smell

Language

CSharp

Tags

api-design, code_smell, composition, dependency-injection

Description

Reports a class carrying [Export(typeof(IFoo))] that does not implement IFoo. The attribute declares the contract a composition container publishes the part under, and imports are matched against that contract — not against the class — so a mismatch is a composition failure waiting to happen.

Rationale

[Export(typeof(T))] is a promise: whoever imports T may be handed this part. The compiler does not check the promise, because the attribute argument is just a Type. So a class that exports a contract it does not satisfy builds cleanly and then fails when the container composes the graph — either the import is left unsatisfied or the cast into the contract fails. Either way the error surfaces at startup, in container code, with nothing pointing back at the declaration that caused it.

The usual cause is drift: the class used to implement the contract, or a copy-paste brought the wrong typeof along with the attribute.

public interface IGreeter { string Greet(); }
public interface IAuditor { void Audit(string message); }

[Export(typeof(IGreeter))]                  // FLAW — exports IGreeter, implements IAuditor
public class Farewell : IAuditor
{
    public void Audit(string message) { }
}

[Export(typeof(IGreeter))]                  // OK — implements the contract it exports
public class LoudGreeter : IGreeter
{
    public string Greet() => "HELLO";
}

[Export]                                    // OK — no explicit contract, so the part exports its own type
public class SelfExported
{
}

Base types declared in the same file are followed, so a class that inherits the contract from its own base type is not reported. When the contract type, or a base type that might carry it, is declared elsewhere, the rule stays silent rather than guess.

Limitations

The exported contract — the type named by typeof(…​) — must be declared in the same file as the class carrying the [Export] attribute; a contract declared elsewhere is invisible to the rule, and so is anything a same-file base type might inherit from it. Exported classes are commonly declared next to the interfaces they implement in small MEF catalogs, but in larger ones the contract interfaces live in a shared abstractions file or assembly, exactly the layout this rule cannot see into, so it should be read as covering the smaller, single-file case rather than the composition graph as a whole.

Remediation

Make the two agree. If the class is meant to serve the contract, add it to the base list and implement its members. If the typeof names the wrong contract, correct it to the one the class actually implements. When the part is only ever imported by its own type, drop the argument and use a bare [Export].