Grant Winney
  • About
  • Code
  • Questions
  • Tools of the Trade
Coding | C# | Attributes | Metadata

Obsolete Attribute on a Class is Ignored When an Interface is Involved

Grant

Grant

Feb 4, 2015
Update: Aug 28, 2024

The Obsolete attribute on a class is ignored when an interface is involved. It caught me by surprise, but makes sense. Let's see why.

While marking some code as obsolete the other day, it seemed that the attribute was being ignored. As it turns out, there's a reasonable explanation, but it took me by surprise at first.

If you'd like to follow along with the code yourself, it's available on GitHub.

Simple Classes and the Obsolete Attribute

Let's start with a simple class that has a Move() method and the Obsolete attribute on that method:

public class Dinosaur
{
    [Obsolete("Dinos don't move anymore", true)]
    public void Move()
    {
        Console.WriteLine("The dino, uh... remained still.");
    }
}

The error flag is set to true, so it'll throw a compiler error:

Now let's say we have a couple other classes with their own Move() methods, without the Obsolete attribute:

public class Penguin
{
    public void Move()
    {
        Console.WriteLine("The penguin waddled.");
    }
}

public class Fish
{
    public void Move()
    {
        Console.WriteLine("The fish swam.");
    }
}

We can instantiate either of these classes and call the Move() method to our heart's content.

Interfaces Hide the Obsolete Attribute

Now that we have several classes with the same method, maybe we want to define an interface that they all implement, perhaps to simplify our code or to support testing. So we create an IAnimal interface that defines a Move() method, and have each of the other classes implement it:

public interface IAnimal
{
    void Move();
}

public class Penguin : IAnimal
{
    public void Move()
    {
        Console.WriteLine("The penguin waddled.");
    }
}

public class Fish : IAnimal
{
    public void Move()
    {
        Console.WriteLine("The fish swam.");
    }
}

public class Dinosaur : IAnimal
{
    [Obsolete("Dinos don't move anymore", true)]
    public void Move()
    {
        Console.WriteLine("The dino, uh... remained still.");
    }
}

If we instantiate each of the animals against the new interface, we don't get a compiler error anymore. The Obsolete attribute seems to be getting ignored for dinos.

In case you're wondering, it doesn't result in a runtime error either:

The penguin waddled.
The fish swam.
The dino, uh... remained still.

After thinking about all this for a bit, what else could it do? Given that a single interface can be implemented by any number of classes, what should happen when one class marks the code obsolete, but the others do not? Should any call to Move() cause an error? Absolutely not... that would break the other classes that don't have the attribute. And so, any presence of the attribute on the classes themselves are ignored in favor of the interface.

In other words, it’s not enough to mark the class itself. If we have interfaces that the class is implementing, its methods may need to be decorated with the attribute too:

public interface IAnimal
{
    [Obsolete("Animals don't move anymore, I decided.", true)]
    void Move();
}

The caveat is that, if we add the Obsolete attribute to the interface, then every class implementing the interface will inherit the attribute too, regardless of whether each of those classes actually has the attribute set on it.

Final Thoughts

When interfaces are involved, the Obsolete attribute on a class is ignored by the compiler. The correct fix depends on the situation. It may mean adding the attribute to the interface itself, or it may mean changing the method to throw a NotSupportedException or some other appropriate exception.

To learn more, check out the MS docs on attributes and interfaces.

Spread the Word

If you found something helpful or interesting here today, and you'd like to share it with others, well then these handy little buttons are just for you. (and thanks!)

Related Articles

Selecting multiple directories with the FolderBrowserDialog in .NET 9

Selecting multiple directories with the FolderBrowserDialog in .NET 9

One of the smaller updates to make it into .NET 9 for WinForms was allowing multi-selection in the FolderBrowserDialog. Let's see how.
Dec 19, 2024
How to Use GetStockIcon for WinForms in .NET 8

How to Use GetStockIcon for WinForms in .NET 8

Buried deep in the list of .NET 8 improvements for WinForms is the GetStockIcon method. It gives us a way to access stock Windows icons at runtime for the OS the app is running on. Let's check it out.
Dec 18, 2024
Using Raw String Literals in C# 11 / .NET 7

Using Raw String Literals in C# 11 / .NET 7

C# 11 added raw string literals, not a life-altering new feature, but they could be useful in the right circumstances. Let's see how to use them.
Dec 14, 2024
Using Primary Constructors with Classes and Structs in C# 12 / .NET 8

Using Primary Constructors with Classes and Structs in C# 12 / .NET 8

As part of C# 12, we got a new feature called primary constructors. Let's see how they work and what we can do with them.
Dec 12, 2024

Comments / Reactions

One of the most enjoyable things about blogging is engaging with and learning from others. Leave a comment below with your questions, comments, or ideas. Let's start a conversation!

Affiliate Links

I occasionally include affiliate links for products and services I find useful and want to share. These links don't increase your cost at all, but using them helps pay for this blog and the time I put into it. Thanks!
Send me new posts: *

  • Connect
  • Privacy
  • License
  • Cross-Posting
Grant Winney © 2025. Powered by Ghost