How to Move a class to another dll and change the namespace without altering the method usage

1.8k Views Asked by At

I have a dll where I have one class which logs to a "channel", another Program listens to the "channel" and writes the logs to the screen. The problem with the dll is that its grown over time and now there are many dependecies in it. I want to remove the logging part from this dll and place it in a debugging and logging dll.

The problem is that the debugging and logging dll has another namespace which I want to use in the class or rather I want to use the class in the namespace. My problem with this is that some developers used the namespace nearly exclusively when calling the methods instead of using it in the top of their classes.

It looks like the following for a static method:

Base.Manager.Logging.Send("Log something");

The other Dll has the simple namespace Trace so it should look like this:

Trace.Logging.Send("Log something");

My question is, is there an elegant way to move the class and change the namespace without altering all the uses of the Methods ? I could simply copy the class to the other dll and then use the old class as a wrapper which forwards all the calls to the other class in the Trace.dll but this approach seems a little bit hacky.

I hope this grafik illustrates what I mean because I think the simple text may be somewhat confusing enter image description here

FYI: I don't have ReSharper

1

There are 1 best solutions below

1
On BEST ANSWER

Unfortunately you can't. If you keep same namespace then you can use TypeForwardAttribute but if you also change namespace (not just assembly) then it doesn't work.

What I suggest is to move it to new class, keep old class to don't break code both at run-time & compile-time and forward all calls. Mark all old methods as obsolete (at least they'll be warned that code will change in future). Just to clarify:

namespace Base.Manager {
    public static class Logger {
        public static void Send(string message) { 
            // Do things
        }
    }

Step 1: empty your old deprecated class and forward calls to new class. Mark old methods as obsolete.

Will then be refactored to:

namespace Base.Manager {
    [EditorBrowsable(EditorBrowsableState.Never)]
    public static class Logger {
        [Obsolete("This method is deprecated, please use Trace.Logger.Send")]
        public static void Send(string message) {
            // Delegate work to someone else...
            Trace.Logger.Send(message);
        }
    }

It'll generate a warning (with given message) during compilation. Compile-time compatibility is saved. Note we also added EditorBrowsableAttribute, IntelliSense won't propose that Logger class when they start typing. This reduces (little bit) chances they will still use Base.Manager.Logger for new code.

Step 2: someday in future you'll change that warning to an error:

[Obsolete("This method is obsolete, use Trace.Logger.Send", true)]

This will break compilation but it'll preserve binaries compatibility. Other DLLs will still run (so you don't have to wait other developers complete this refactoring task) but they won't be able to compile unless they update.

Step 3: when all has been done and everyone moved to new class then simply remove Base.Manager.Logger class from your code.

Notes: that all process can be spanned across various releases: in 2.0 you deprecate old methods, in 2.1 you break compilation, in 3.0 you remove it from your code (this is especially useful if releases of your code and its users aren't in sync).

Just a word about name collisions: be aware that same class name (and methods) in two different namespace may lead to annoying name collisions. Imagine they have this code:

using Base.Manager;

void DoSomething() {
    Logger.Send("I'm doing something.");
}

After your change (until Base.Manager.Logger isn't removed) they can't simply add another using:

using Base.Manager;
using Trace;

void DoSomething() {
    Base.Manager.SomethingElse.DoIt();

    // Compiler is confused: Base.Manager.Logger or Trace.Logger?
    Logger.Send("I'm doing something.");
}

They need to full qualify which logger class they want to use:

Trace.Logger.Send("I'm doing something.");

Warning: be aware that if Trace isn't a new namespace then name collision may even break compilation immediately if they had both usings in their code (making ObsoleteAttribute useless).

If it's an issue or not for you/your users...I can't say.