| ||
|
This discussion group is now closed.
Have a question about .NET development? Try stackoverflow.com, a worldwide community of great developers asking and answering questions 24 hours a day. The archives of .NET Questions contain years of Q&A. Even older .NET Questions are still online, too. |
I am currently learning C#. Coming from a C++ background, I don't really understand how the GetType() function can retrieve the exact runtime type from a current intstance without being virtual. In C++, I would have declared that function like this: public: virtual Type GetType() const; That would polymorphically call the GetType() function of the derived class, if there's any. In C++, Object.GetType would not be able to know the runtime type of its derived classes without being virtual. As far as I know, virtual in C# and virtual in C++ means exactly the same. In C#, GetType is not virtual for some reason, which makes me wonder how that function can access the actual runtime type of the referenced type. How does System.Object.GetType() work?
rumpl Monday, October 02, 2006
Actually, in C++ Object.GetType would probably be declared like this: public: virtual Type GetType() const = 0;
rumpl Monday, October 02, 2006
System.Object.GetType() isn't virtual in C# for the same reason it wouldn't be virtual in C++ - to stop derived classes from overriding the behaviour of it. You can always access public members of a base class, virtual or not. As for how it works - the GetType() function is marked with the special attribute [MethodImpl(MethodImplOptions.InternalCall)]. This means its method body doesn't contain IL but instead is a hook into the internals of the .NET CLR. In this case, it looks at the binary structure of the object's metadata and constructs a System.Type object around it. As far as I know, [MethodImpl(MethodImplOptions.InternalCall)] should be considered "magic" - you can't hook your own unmanaged code up to new functions with this attribute.
Also... the key difference between C# (or any .NET language_ and C++ is that objects in .NET always have an associated piece of metadata that describes the object. Whereas in C++, they don't (unless you use some sort of RTTI library). So, in C#, you can guarantee to ask any object what type it is at runtime and expect it to work. Try this bit of code: using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; public class Test { public static void Main() { object data = new object(); FileStream stream = new FileStream( "object.dat", FileMode.OpenOrCreate, FileAccess.Write ); BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize( stream, data ); stream.Close(); } } Now have a look at object.dat in a binary editor. Notice how the name "System.Object" is in the file, along with some other pieces of metadata. The full spec for this is at http://jilc.sourceforge.net/ecma_p2_cil.shtml if you fancy a little light reading ;-)
Thanks, Ritchie Swann, good to know that GetType() contains "magic" code. But here: "System.Object.GetType() isn't virtual in C# for the same reason it wouldn't be virtual in C++ - to stop derived classes from overriding the behaviour of it." I have to disagree. As far as I know, C++ binaries don't contain any metadata as C# assemblies do. I think the GetType() function would have to be pure virtual so derived classes are actually forced to override it, because they are the only ones who know what their actual runtime type is.
rumpl Monday, October 02, 2006
Think I was just at cross purposes there. What I meant is that the .NET GetType() should not be virtual, and with good reason - it's an internal call and overriding it could cause all sorts of havoc. I see what you're talking about is implementing an equivalent metadata / RTTI system in C++. But even then you don't _have_ to make your GetType() virtual. You could store the type information in some structure in your topmost base class, like this: class Object // Root of all objects. { public: public Object( const std::string& typeName ) : m_typeName( typeName ) {} std::string GetTypeName() { return m_typeName; } protected: const std::string m_typeName; }; class Test : public Object { public: public Test() : Object( "Test" ) {} }; Although this is just storing type names, it should be easy to see how this could be extended to more data. And this is pretty much what the .NET CLR does under the hood.
True. It seems to be even better that way, as you don't have to implement that yourself. The language does that automatically, so to say. Again, thank you very much.
rumpl Monday, October 02, 2006
>>> Thanks, Ritchie Swann, good to know that GetType() contains "magic" code. <<< This is not as important as the fact that all assemblies carry metadata. When you compile C++ project into code, you get just code. There is NO metadata describing types, methods, parameters etc - just code. When you compile C#, the code consists of IL (intermediate language that is Just-In-Time compiled to native code) AND metadata describing ALL aspects of ALL types - their name, number and signature of all methods (including constructors) etc. This metadata is tightly packed into a "package" that assembly resides in (usually an .EXE or .DLL) and can be read either using .NET reflection (there are types in the .NET framework whose sole purpose is to "spy" on properties of other types) or directly reading the metadata tables. The latter is very hard-core but a friend of mine (a very-soon-to-be microISV) used it to implement .NET obfuscator. So the key is not magic code, but presence of metadata.
Actually, C++ does store some data about about classes. You can get the dynamic type of an object using the typeid operator. It only works on classes that have at least one virtual method because it uses the virtual function table to determine the class. It's also not as powerful as .NET because it only allows type comparison and access to the class name.
Conceptually, it makes some sense that GetType would be virtual. It's not hard to imagine an implementation where the compiler generates some internal static Type instance for the type data that's returned by a generated override of GetType. My guess is that it's doing something similar to this but is implemented as a non-virtual native method for performance sake.
| |
Powered by FogBugz
