Passing class objects across different assembly versions

3k views Asked by At

The scenario goes like this -

  • I have an assembly say 'MyAssembly'. An interface say 'IMyInterface' is defined in this assembly.
  • In the same assembly i have one class(MyClass) with a method define in it as :

public void MyMethod( IMyInterface object ) { }

  • Now in my project, i have created an interface with the same name and exact properties as exposed by the interface "IMyInterface" in 'MyAssembly'.
  • I have a class which extends this interface(the one which i have created in my project) and i want to pass the object of that class as a parameter to the method "MyMethod" in different assembly using reflection.

Issue is -

  • When i try to invoke the method using reflection, i got this exception that "Cannot convert object to type IMyInterface".

Code -

Assembly myAssembly = Assembly.LoadFrom("MyAssembly");
object classObject = myAssembly.CreateInstance("MyClass");
Type classType = myAssembly.GetType("MyClass");
MethodInfo myMethod = classType.GetMethod("MyMethod", BindingFlags.Instance);

// Creating an object of class in the latest assembly and need to pass this
// to method in assembly with different version.
ClassExtendingMyInterface obj= new ClassExtendingMyInterface ();

myMethod.Invoke(classObject, new object[] { obj});

If, i am getting it right, this is because object created is in different assembly and the parameter expected by the method is of its own assembly.

Another approach i thought of was to create my own dynamic method in the class which will accept the object of my class.

I have tried to google for and come across through Reflection.Emit or RunSharp to dynamically create your own class. But, we can use this only in our dynamically generated Assemblies but can't create dynamic methods or class in already existing assembly.

I know generating assemblies at runtime is ain't good approach. But i can't think of anything now. Thanks for any help.

3

There are 3 answers

8
Hans Passant On BEST ANSWER

You are battling something called "Type identity", a very important DLL Hell counter-measure in the .NET framework. A type isn't just identified by its namespace name and type name, it also includes attributes of the assembly from which it came from. Specifically the assembly display name, [AssemblyVersion], [AssemblyCulture], PublicKeyToken and (indirectly) ProcessorArchitecture. You can see the 'real' type name with the Type.AssemblyQualifiedName property. The System.String class for example is System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

This stops you from faking a type in another assembly unless you can give that assembly the exact same attributes. Much easier is to simply use the existing assembly, should be no problem in your case since you only use an interface.

Notably is that this requirement was relaxed somewhat in .NET 4. Types that were auto-generated from a COM type library are equivalent if their name and [Guid] matches. Which helped eliminate PIAs and implement the "Embed interop types" feature. Nothing that applies in your case.

1
Marc Gravell On

Here:

Now in my project, i have created an interface with the same name and exact properties as exposed by the interface "IMyInterface" in 'MyAssembly'.

There-in is the problem; it is not enough to declare a like-named interface. Types are scoped by their assembly; that is, to the CLR, a completely different interface.

Instead, add a reference to the original assembly, and implement the interface that is already defined. It should only be defined once.

0
XIVSolutions On

If I understand your problem correctly, I believe you want to define your Interfaces in their own assembly, then reference the common interfaces assembly from within each of the other two. That way, each assembly is referencing the SAME Interface definition:

Create and build the MyInterfaces dll:

namespace MyInterfaces
{
    public interface IMyInterface
    {
        void SharedMethod();
    }
}

Then create your first assembly in a different project, and set a reference to the dll created for MyInterfaces. Create your class, noting the "using" statements at the top of the module:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyInterfaces;

namespace MyFirstProject
{
    public class MyClass1
    {
        public void MyMethod(IMyInterface SomeObject) { }
    }
}

Now, I believe your dynamic object creation should work, since both objects implement the same interface defined in "MyInterfaces".