Same Class, different Types

B

Born Bugler

What I'm actually talking about is, when you put the same class in different
assemblies, you get two different types. This is reasonable (if you would
call it) in most cases as it avoids possible misuse. However, what about if
I do want to consider classes with exactly the same definition in different
assemblies to be the same? I tried interpret_cast, but it works only for
trivial scenarios like below:

ref class ClassA
{
public:
virtual DoSomething(){ Console::WriteLine( "From ClassA" ); }
};

ref class ClassB
{
public:
virtual DoSomething(){ Console::WriteLine( "From ClassB" ); }
};

int main()
{
ClassA^ ca = gcnew ClassA();
ClassB^ cb = reinterpret_cast<ClassB^>(ca);
cb->DoSomething(); // OK, output "From ClassA"
}

However, if you make the scenario more complex by adding some base classes
to ClassA and ClassB, reinterpret_cast fails, as shown below:

interface class InfA
{
DoSomething();
};
interface class InfB
{
DoSomething();
};

ref class ClassA : public InfA
{
public:
virtual DoSomething(){ Console::WriteLine( "From ClassA" ); }
};

ref class ClassB : public InfB
{
public:
virtual DoSomething(){ Console::WriteLine( "From ClassB" ); }
};

int main()
{
InfA^ ca = gcnew ClassA();
InfB^ cb = reinterpret_cast<InfB^>(ca);
cb->DoSomething(); // Oh, an exception "Entry point not found" is thrown
here.
}

In standard C++, I don't think there will be any problem with the above
attempt. However, in C++/CLI, well, I'm waiting for your reply...

Born
 
K

Kevin Frey

Native C++ has no such problem because the types do not have a "strong
name". Types in managed assemblies, on the other hand, have a "strong name"
which means that type X in one assembly is not the same as type X in another
assembly, because their strong names are different.

You mention different assemblies. Are the two assemblies somehow related or
completely unrelated?

If they are completely unrelated then you might want to factor out the
shared types into a third assembly and have both of the other assemblies
depend on types defined within it. That way they are both using the same
strong name, hence referring to the same type.

If they are related then one of the assemblies should control the class
definition and the other assembly should use it. That way they are both
using the same type definition.

Kevin.
 
B

Born Bugler

They are unrelated. I know exactly your solution. But what if I don't want a
third assembly? In many cases the third assembly would be filled with only
interfaces, and it looks silly to maintain a separate assembly for that
purpose.
 
J

Jochen Kalmbach [MVP]

Hi Born!
ref class ClassA
{
public:
virtual DoSomething(){ Console::WriteLine( "From ClassA" ); }
};

ref class ClassB
{
public:
virtual DoSomething(){ Console::WriteLine( "From ClassB" ); }
};

int main()
{
ClassA^ ca = gcnew ClassA();
ClassB^ cb = reinterpret_cast<ClassB^>(ca);
cb->DoSomething(); // OK, output "From ClassA"
}

You should use "Reflection" to call the "DoSomething" method!

Greetings
Jochen
 
B

Born Bugler

Oh, no, Jochen.

Please give me a detailed solution. Personally, I would say it will be much
harder than you have thought.

Born
 
J

Jochen Kalmbach [MVP]

Hi Born!
Oh, no, Jochen.

Please give me a detailed solution. Personally, I would say it will be much
harder than you have thought.


Regardless, that the "correct solution" is to put the "common" class in
a common-assembly, you can use refelction:

namespace Foo
{
ref class A
{
public: void DoSomething() {
System::Console::WriteLine("A::DoSomething"); }
};

ref class B
{
public: void DoSomething() {
System::Console::WriteLine("B::DoSomething"); }
};

ref class General
{
public: void static DoSomething(Object ^instance)
{
if (instance == nullptr) throw gcnew
System::NullReferenceException("'instance' must be set!");
System::Type ^t = instance->GetType();
System::Reflection::MethodInfo ^mi = t->GetMethod("DoSomething",
gcnew array<System::Type^> {});
if (mi == nullptr) throw gcnew
System::MethodAccessException("Method 'DoSomething' not found!");
mi->Invoke(instance, gcnew array<Object^> {});
}
};
}

int main()
{
Foo::A ^a = gcnew Foo::A();
Foo::B ^b = gcnew Foo::B();

Foo::General::DoSomething(a);
Foo::General::DoSomething(b);
}

Greetings
Jochen
 
B

Born Bugler

So you misunderstood me.

I want to change an object from TypeA to TypeB, assuming TypeA and TypeB are
samely structured (with same methods, properties and so on. In most cases,
TypeA and TypeB will be interfaces. )

I'm almost sure this is unfeasible under .NET framework, especially if I
don't want to pay much performance penalty. If the performance is not taken
into consideration, through, there might be some ways:

1) Use wrapper objects.

The wrapper object will be derived from TypeB, and wrap the original TypeA
object.

2) Change the metadata of the original object to make it a real TypeB object
(sounds crazy).

Born
 
J

Jochen Kalmbach [MVP]

Hi Born!
So you misunderstood me.

No. "reinterpret_cast" ist not possible in a "strong typed system".
So the only way is to use reflection, and this works very well (but slow).

But in most cases: if you have a bad design, you mostly must use a bad
solution ;)

--
Greetings
Jochen

My blog about Win32 and .NET
http://blog.kalmbachnet.de/
 
T

Tamas Demjen

Born said:
They are unrelated. I know exactly your solution. But what if I don't want a
third assembly?

For your problem, the right way to do is to have a dedicated assembly
with the interfaces only. In .NET, it's perfectly normal to have an
assembly for a group of classes that belong together. Especially so if
those classes are stable and won't be expected to change.

Without doing that, you have to live with an imperfect and awkward
solution, such as reflection, as others have pointed it out.
In many cases the third assembly would be filled with only
interfaces, and it looks silly to maintain a separate assembly for that
purpose.

It's not silly. The .NET framework itself is broken into 100s of assemblies.

If you want to design a plugin architecture, publish all the interfaces
in an assembly. It's self-contained, and that's *all* plugin
implementors will ever need in order to write custom modules for your
system. It's a good design, it's not silly.

If you have a small group of classes that you consider stable, it's a
very good idea to move them into an assembly, even if that DLL will only
have 3 functions. This approach fosters stability in the long run. Every
time you have to recompile code, you run the risk of breaking something,
and you must retest your entire package from sratch. A well tested,
compiled, self-described binary module that never changes is as stable
as it gets. You can even reuse that in other projects.

Tom
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top