简体   繁体   中英

Pitfalls of C# to C++/CLI conversion

I've been asked to translate some C# code into C++/CLI. One of my managers has asked about potential pitfalls with the concept of translation between the two.

I'm well aware that both languages are compiled down to the Common Interface Language, and run through the same Common Language Runtime. However, can one guarantee that a piece of C# code which is compiled for the .NET Framework 4.5 and calls File.Exists(someFile); will compile down to the CIL in exactly the same way as a piece of C++/CLI code which is compiled for the .NET Framework 4.5 and calls File::Exists(someFile); ?

In theory the answer is yes, since this seems to be what the .NET framework is built for. However, is there a way of validating this? Is there a technical reason why it must be/cannot be the case?

There certainly are some pitfalls when you write C++/CLI code and you know C# well but not C++/CLI. I'll avoid turning this into a list question and enumerate all of them, just the more common mistakes:

  • C++/CLI requires you to be specific in the language syntax about the difference between value types and reference types. You have to use the hat^ on reference type references. Like String^ foo; . But sometimes you don't, you intentionally omit it. Like StreamReader file; . Which enables "stack semantics", an emulation of the C++ RAII pattern. Or in other words, the compiler will automatically dispose the file variable when it goes out of scope. You know this from the C# using statement. The biggest trap is using the hat when you should not, like int^ x = 42; . Which the compiler will accept without a complaint, but you end up with a boxed int which is extremely inefficient at runtime.

  • Development on the C++/CLI language and toolset stopped in 2005 and has been in maintenance mode since. It thus didn't acquire the goodies that C# got in that time frame, Linq certainly being the most significant addition. No lambda expression support, no anonymous types, no extension methods. You will not enjoy converting C# code that uses these syntax constructs.

  • There is only ever one reason to consider converting C# to C++/CLI, it is to take advantage of its great support for interop with unmanaged code. No need to wrangle pinvoke. A very common mistake is to compile everything to MSIL, including the native C++ code. Which works too well, the C++/CLI compiler is capable of compiling any C++ to IL as long as it is standard C++03 compliant code. But the result is inefficient , it certainly doesn't run as managed code at runtime and is jitted like normal but without the goodies from the native code generator. Which can do a better job optimizing code.

  • The output of the compiler is plain MSIL, the exact same kind that C# generates. Plus native code for the parts of your program where you use C++, producing a mixed-mode assembly. This gives the assembly a dependency on the bitness of the process. Or in other words, the AnyCPU target you might know from C# projects does not apply. This is something you need to be aware of in your EXE project, you must be explicit about the Platform target setting. With "x86" the common choice to ensure that your program will still work when it runs on a 64-bit operating system. If you want to run your program as a 64-bit process on a 64-bit OS then you'll need to create two builds of your C++/CLI assembly and deploy the correct one with your installer.

You have nothing to fear from a File::Exists(somefile) call, there's only one way to call that method in MSIL. The C++/CLI compiler will generate the exact same IL as the C# compiler. If you are really concerned about it then create confidence by looking at the IL with a decompiler like ildasm.exe

[C]an one guarantee that a piece of C# code which is compiled for the .NET Framework 4.5 and calls File.Exists(someFile); will compile down to the CIL in exactly the same way as a piece of C++/CLI code which is compiled for the .NET Framework 4.5 and calls File::Exists(someFile); ?

No, this is not guaranteed.

In fact, it's not even guaranteed that two runs of the same source code through the C# compiler will produce the same binary output . So certainly two different compilers for two different languages can be expected to possibly generate different code.

(Mind you, it might , and in reality it probably often does . But we're talking about guarantees here.)

What I cannot understand is why this would possibly matter. The code will do the same thing, of course, since it's calling the same method. Not only is that guaranteed by the documentation, it's self-verifiable by disassembling the .NET Framework DLLs.

The two programs just aren't guaranteed to have identical binaries. Not that anything will anyway once it passes through the JIT compiler. That's kind of the whole point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM