I have a body of 'normal' C++ code which I'm trying to make usable by a C# client. I have successfully compiled this with /CLR. I now know that this isn't enough: I have to introduce managed wrapper classes ("ref") to make the code callable from managed code. This question is about what happened before I introduced the ref classes.
I found that the native C++ classes were visible from the C# project, and that I could write
MyNativeClass mnc = new MyNativeClass();
... although any attempt to call a method on the instance was rejected by the compiler. I found that when I ran the C# code, the MyNativeClass constructor was never called - indeed the attempt to instantiate mnc seemed to produce no code at all, so completed without error.
How was C# interpreting the native types in my project? Why did the compiler apparently allow me to instantiate an instance? Why were methods treated differently to the types themselves?
You made two mistakes to make this code work. First is that you declared the C++ class public, like this:
public class MyNativeClass {};
This is not syntax that makes sense in C++ but it is allowed by the C++/CLI compiler. Omitting public would have produced an error message in your C# code, CS0122: "Foo is inaccessible due to its protection level".
Second mistake is that you compiled your native C++ class with /clr in effect. Which works fine, any C++03 compliant code can be compiled to IL and gets just-in-time compiled to machine code by the jitter, just like managed code. And executes fine as well. It is however not efficient to do so, you lost the advantage of having the compile-time optimizer available to produce the best possible code. It still gets optimized, but now by the jitter which doesn't have the same luxury of time available to do as good a job as the C++ code optimizer can do. You avoid this by moving the C++ code in a separate source code file so you can compile it without /clr in effect. Or by using #pragma managed in your source code.
Anyhoo, what you ended up with was an assembly that indeed contains a declaration for MyNativeClass that any managed compiler can see. Something you can see for yourself by running ildasm.exe on the assembly. You'll see that it gets embedded as a value type type. Just a blob of bytes with no constructor and no methods. Which is a decent match for a C++ object from a managed point of view, just bytes that can be stored anywhere a value type can be stored. Declaring a variable of that type in C# works but doesn't do anything useful, it just creates the blob with all bytes set to 0. The only possible use of this declaration is that you can declare a typed pointer to the class and pass it around, that's all.