Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct way to use TypeForwardedToAttribute?

I came across to this attribute in this post and this one. It seems that it's very useful when we need to upgrade an old system. Then I create a test solution(with 3 projects in it) in order to use this attribute. Firstly there is a class library project named "Animal".

namespace Animal
{
   public class Dog
   {
      public static string Name = "old version";
   }
}

Then I create a console application project, add "Animal" as a reference, and in the Main method I have:

Console.WriteLine(Animal.Dog.Name);

Now it prints "old version". Great! Now I begin to "upgrade" the existing project. I remove the class Dog in "Animal" add another class library project named "AdvancedAnimal" which contains:

namespace Animal
{
   public class Dog
   {
      public static string Name = "new version";
   }
}

Add "AdvancedAnimal" as a reference in "Animal". Also AssemblyInfo.cs of "Animal" is modified by adding:

[assembly: TypeForwardedTo(typeof(Animal.Dog))]

From the usage of this attribute, from now on all Animal.Dog is forwarded to the Dog class in "AdvancedAnimal"(actually there is no Dog class in Animal any more). I re-compile the whole solution and hope the console application prints "new version". But it gives me a compile error:

The type name 'Dog' could not be found in the namespace 'Animal'. This type has been forwarded to assembly 'AdvancedAnimal, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' Consider adding a reference to that assembly.

Oh, I'm told to add "AdvancedAnimal" as a reference to my console application! But if I do so, I don't need the attribute any more, because if I add "AdvancedAnimal" as a reference in the console application, of course Animal.Dog refers the one in "AdvancedAnimal"! What I expect is that modifying "Animal", Adding "AdvancedAnimal", no necessary to change all other projects/class libraries because the assembly info already provides enough information. That is really convenient when upgrading a system. Otherwise, I have 20 projects referring to "Animal", I need to add "AdvancedAnimal" as a reference to all of them. And more important is, I can't find any usage of "TypeForwardedToAttribute" in this solution, removing it doesn't matter anything. Can you tell me what's wrong with my test/idea?

like image 777
Cheng Chen Avatar asked Dec 29 '10 10:12

Cheng Chen


2 Answers

This is the first time I've heard about that attribute, interesting :)

The way I read the documentation, the attribute can be used in scenario's where you're only delivering a set of library dll's.

Let's say you have some application, that uses a set of widgets from a DLL. Later on, you decide to move parts of that DLL to a separate DLL. Using the TypeForwardedToAttribute, you can do that by having the old DLL reference the new one. You can now deploy both DLL's to the installations of the existing application, without recompiling the application itself

This could be useful in scenario's where you don't have control over the application itself, but are merely supplying a set of library dll's. In order for your codebase to move forward, you can use the TypeForwardedToAttribute, and have users deploy it safely to their application, without breaking anything.

like image 24
Willem van Rumpt Avatar answered Oct 19 '22 18:10

Willem van Rumpt


From the documentation for TypeForwardedToAttribute:

Use the TypeForwardedToAttribute attribute to move a type from one assembly to another without disrupting callers that compiled against the old assembly.

But what you are doing is forwarding the type from the same assembly to another type in the same assembly. It doesn't make any sense.

Lets make it clear. Assume if you have a class dog in assembly oldAssembly.dll

namespace Animal
{
   public class Dog
   { 
      public void printName() {      
           console.writeline("old version");
      }
   }
}

and referenced it in some other assembly (x.dll)

   Dog dg=new Dog();
   dg.printName()

later you wanted to change the printName functionality, but without touching the caller (x.dll) (assume if the dll is deployed and dont want to be touched)

so you create a new assembly (dll), which got

namespace AdvancedAnimal 
{
   public class Dog
   { 
      public void printName() {      
           console.writeline("new version");
      }
   }
}

Now you can now recompile the old dll by adding reference to the new dll and adding

[assembly:TypeForwardedTo(typeof(AdvancedAnimal.Dog))]

Now whatever calls made to the Animal.Dog forwarded to AdvancedAnimal.Dog.

So

! What I expect is that modifying "Animal", Adding "AdvancedAnimal", no necessary to change all other projects/class libraries because the assembly info already provides enough information. That is really convenient when upgrading a system. Otherwise, I have 20 projects referring to "Animal", I need to add "AdvancedAnimal" as a reference to all of them.

You dont have to add AdvancedAnimal to all your 20 projects. All you have to do is adding AdvancedAnimal to Animal.

Hope this clarifies the context where it can be useful

EDIT:

I re-compile the whole solution and hope the console application prints "new version". But it gives me a compile error:

Whole point of this is we can call a new assembly without modifying the caller. You should not recompile the whole solution, because your caller is still pointing to the method int the old assembly. That's why you got the error

The type name 'Dog' could not be found in the namespace 'Animal'. This type has been forwarded to assembly 'AdvancedAnimal

Just recompile your old & new assemblies and put it into the caller bin and run the exe. It'll work like a charm.

like image 158
RameshVel Avatar answered Oct 19 '22 18:10

RameshVel