By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
440,077 Members | 1,175 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 440,077 IT Pros & Developers. It's quick & easy.

Same Class, different Types

P: n/a
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
Mar 30 '07 #1
Share this Question
Share on Google+
8 Replies


P: n/a
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.
"Born Bugler" <wo*******@263.netwrote in message
news:eu**********@news.yaako.com...
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

Mar 30 '07 #2

P: n/a
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.
"Kevin Frey" <ke**********@hotmail.comwrote in message
:Om**************@TK2MSFTNGP05.phx.gbl...
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.
"Born Bugler" <wo*******@263.netwrote in message
news:eu**********@news.yaako.com...
>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


Mar 30 '07 #3

P: n/a
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
Mar 30 '07 #4

P: n/a
Oh, no, Jochen.

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

Born
"Jochen Kalmbach [MVP]" <no********************@holzma.deWrote in
message:%2****************@TK2MSFTNGP06.phx.gbl...
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

Mar 30 '07 #5

P: n/a
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
Mar 30 '07 #6

P: n/a
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
"Jochen Kalmbach [MVP]" <no********************@holzma.deWrote in
message:en**************@TK2MSFTNGP02.phx.gbl...
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

Mar 31 '07 #7

P: n/a
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/
Mar 31 '07 #8

P: n/a
Born Bugler wrote:
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
Apr 2 '07 #9

This discussion thread is closed

Replies have been disabled for this discussion.