3

If I want to rename an interface without breaking existing code, would this work?

The old interface:

namespace RenameInterfaceNicely
{
    /// <summary>
    /// The old name of the interface.
    /// </summary>
    [Obsolete("IFoo is replaced by IFooNew.")]
    public interface IFoo
    {
        void Test();
    }
}

The new, renamed, interface:

namespace RenameInterfaceNicely
{
#pragma warning disable 0618
    /// <summary>
    /// IFooNew new name for the interface IFoo.
    /// </summary>
    public interface IFooNew : IFoo
    {
    }
#pragma warning restore 0618

}

So if I have a class Foo that used the old interface, that I now change to use the new interface. Will users of Foo get in trouble?

Foo before the change:

public class Foo : IFoo
{
    public void Test()
    {    
        // Do something       
        return;
    }
}

Foo after the change:

public class Foo : IFooNew
{
    public void Test()
    {    
        // Do something       
        return;
    }
}

Example of existing code:

// Old code
IFoo oldFoo = new Foo();
oldFoo.Test();
IFoo oldFoo2 = new Bar().GetFoo();

...

Bar bar = new Bar();
bar.DoSomethingWithFoo(oldFoo);

Example of new code:

// New code
IFooNew newFoo = new Foo();
newFoo.Test();
IFooNew newFoo2 = new Bar().GetFoo();

...

Bar bar = new Bar();
bar.DoSomethingWithFoo(newFoo);

My question was inspired by this one: A definitive guide to API-breaking changes in .NET and the fact that someone made a breaking change by renaming some interfaces that I was using.

Community
  • 1
  • 1
Nils Lande
  • 860
  • 9
  • 14
  • We can't say without knowing how the type is used. Is it possible for this to break a program, yes. Just about any change of any kind that you make to any public API is a breaking change, in some way, shape, or form. – Servy Aug 12 '14 at 18:13

3 Answers3

2

In the case of a genuine rename: everyone will need to recompile to pick up that change. It is a dangerous change, frankly. Interfaces are contracts, and should not be renamed. If the rename is unavoidable, however, this does at least let them know what to change it to.

--

However, actually: what you have demonstrated isn't a rename; it is a semi-compulsory extension; the IFoo still exists and still has the old features. Presumably you want people to start providing IFooNew implementations. That's reasonable, but you should aim to ensure that if a consumer only implements IFoo, then it continues to work.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
1

Renaming a public type or member is a breaking change.

  • You don't get source compatibility. Source code that explicitly mentions the interface name, it will need to be changed.
  • Depending code in the same solution can be changed automatically via rename refactoring
  • You might be able to keep binary code that depends on the interface working using Type forwarding. This has been designed for moving types between assemblies not for renaming, so I'm not sure if it works.

Deriving a new interface from the old one is still breaking:

  • When implementing a method explicitly, you need to refer to the type the method is on
  • A class implementing the old interface won't implement the new interface.
  • Code returning the old interface will still return the old interface
CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • Type forwarding does not help here. It allows for changing the assembly name of the type, but otherwise, the fully qualified name of the type must remain the same. See this Q&A and the comments there: https://stackoverflow.com/q/4553463/2157640 – Palec Aug 03 '22 at 13:01
-1

You may want to consider copying the definition from IFoo into IFooNew rather than implementing it as a child interface. This may make it easier to remove IFoo later, especially if someone explicitly implements the interface.

Consider the following:

public class Foo : IFoo {
    void IFoo.Test() {}
}

After upgrading to your new version (v2), they will now get your Obsolete compiler warning, and at some point, hopefully they'll change so they implement IFooNew instead of IFoo and change all references. But since they're using explicit implementation, they have to leave the implementation as IFoo.Test instead of IFooNew.Test:

public class Foo : IFooNew {
    void IFoo.Test() {}
}

They no longer get the compiler warning, and everything is fine. The next version of your library (v3), you move the Test method into IFooNew, eliminate IFoo, and now they upgrade again to your newest version - they now have to change their code again.

public class Foo : IFooNew {
    void IFooNew.Test() {}
}

Ideally, you would only force them to change their code once instead of twice.

Joe Enos
  • 39,478
  • 11
  • 80
  • 136
  • If the interfaces are semantically the same but technically unrelated then one could never pass an object implementing the new interface to any methods expecting the old interface, or vice versa. That's almost certainly going to be a non-starter for a widely distributed library, and is a *much, much* more disruptive breaking change than the OP's approach. – Servy Aug 12 '14 at 18:30
  • @Servy Maybe I misunderstood. I was under the impression that the old interface would eventually be removed, in which case, yes, you absolutely will cause massive breaking changes at some point, and force people to switch over all of their code to your new interface in order to upgrade. In that scenario, I'd rather have one round of changes than two. But if the original interface will live forever, then I agree with you, and would leave it alone. – Joe Enos Aug 12 '14 at 18:35
  • The thought was to keep the Test()-method in the old interface so that existing code would work. All users of the old interface would of course get a warning when compiling the code. I also thought about a scenario where the a braking change could be made in a later version (after users had a chance at fixing their code) by removing the IFoo-interface and moving the Test()-method into the new interface, IFooNew. – Nils Lande Aug 13 '14 at 09:34