all groups > visual c > march 2007 >
visual c :
Same Class, different Types
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
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. [quoted text, click to view] "Born Bugler" <wondertop@263.net> wrote in message news:eui17o$6dc$1@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 >
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. [quoted text, click to view] "Kevin Frey" <kevin_g_frey@hotmail.com> wrote in message :OmX43pocHHA.1000@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" <wondertop@263.net> wrote in message > news:eui17o$6dc$1@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 >> > >
Hi Born! [quoted text, click to view] > 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
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]" <nospam-Jochen.Kalmbach@holzma.de> Wrote in message:%23FjhY5pcHHA.4888@TK2MSFTNGP06.phx.gbl... [quoted text, click to view] > 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
Hi Born! [quoted text, click to view] > 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
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]" <nospam-Jochen.Kalmbach@holzma.de> Wrote in message:enB0ttqcHHA.2404@TK2MSFTNGP02.phx.gbl... [quoted text, click to view] > 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
Hi Born! [quoted text, click to view] > 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
[quoted text, click to view] 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. [quoted text, click to view] > 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.
Don't see what you're looking for? Try a search.
|
|
|