MC++ vs. C#

There was a thread internally
on the virtues of using the managed extensions to C++ or using C#...
This is dangerous territory as they are both well supported by the CLR so I
don’t feel like I should recommend one language over another. Core to our “common language” message is
that you can use the language you are most comfortable with.

And I kow this could
kick of a heated debate, but, I
feel stronly that we should be getting as much information out to the community
as we can. Be aware that the statements below are personal opinions and
not the "offical" Microsoft postion. Here is my roll up of the
discussion, I think many of the tehincal details
here will be helpful for you as you decide what language to write
in. None of the views expressed below are necessarily
mine... I will give you mine over a beer sometime
;-)

"urn:schemas-microsoft-com:office:office" />

Question:

Assuming devs who don’t mind writing Monitor.Enter and Monitor.Leave
instead of lock (and other such niceties) and think that explicitly calling box
and unbox is a feature than a nuisance, are their any
advantages to using C# instead of Managed C++ for code that does not care about
being verifiable?

One advantage that comes to my mind is that the C# compiler may be
better at the IL produced because C# is the preferred language of the platform.
Is this valid?

Responses:

No,
this is not valid. The C++ compiler actually uses the C++ optimizer to get
a better IL before it generates the final IL that will be
JITed.


MC++ is great for the times when C# doesn't quite make it. You can do
a lot with runtime interop, but when you have a
complex scenario, you can easily spend a day doing something in C# that would
take you 30 minutes in MC++.

But outside of those times, I find C# to be straightforward and
productive. I generally don't need to write a mixture of managed and unmanaged
code, or explicitly box and unbox items, and I'd
rather my language isn't complicated with those elements. I find that I can
"flow" a lot easier in C# than I could in C++.

A
couple other comments on what others have
said:

  • There is ramp-up time
    for both MC++ and for C#.

  • For many applications, it's not an
    either-or choice. You can choose to write part of the application in one
    language and part in the other. There are a number of C# teams that write a
    bit of interop code in C++.

  • Compilation speed may be
    an issue in your build lab and test scenarios, and the difference in raw speed
    is significant. Whether it matters once you've layered a bunch of process
    around the build depends upon your process.

One
final comment. If your group is thinking about adopting one or
another, you should find other people using that language
and ask them why they made the choice that they did, and what issues they've
found. Don't ask the language team
guys.

1) “C# can produce verifiable
code”

>>So can C++, but unverifiable constructs are not required to
be called out with an “unsafe” keyword. For a future release, we are adding a
>>new compiler switch /clr:safe, which will emit errors on anything that is not
verifiable. For 7.1, writing verifiable code take a bit

>> more effort, but it is still
possible.

2) “C# has better type-safety for
."urn:schemas-microsoft-com:office:smarttags" />NET
code than C++. “

>> Verifiability implies type
safety.

3) “C++ can lead to accidental use of unmanaged types in a managed
way, which will cause memory leaks.”

>>True. But C++ has always been and will always be a low level
power language. You need to know what you’re
doing.

>>Both C++ and C# are both great ways to write
code for .NET.
Language choice always needs to be the result of careful consideration of
>>design goals and the skills of the developers
involved.

C# compiler is FASTER than
C++. At least for the volume we are using.

If your project involves a non-trivial amount of interop, using MC++ is a no-brainer. This is especially true if your code is
interop-ing with pieces that are highly fungible, in
which being able to include native headers, rather than redundantly defining
your interfaces (or using a tool like tlbimp, which
has its own set of problems discussed in the link below), gives you build
robustness not achievable with any current C# technologies. Even if you don’t think you’re going to
be doing much interop, I would urge you to think
things through carefully: I’ve seen many projects start out with C# assemblies
only to find that they need a special MC++ “interop
helper-assembly” later on. Such
projects not only mix managed with native, but also mix programming
languages! No matter what side of
the language debate you’re on, I’m sure you’ll agree that the more languages you
use in a project, the worse things get...

One key cost to consider is porting to 64bit
environments is seamless with verifiable C# (i.e. you run the same image as we
do with the entire .NET
Frameworks on 64bit environments) but painful with mc++ as you have to recompile
and such (as we are learning in porting Avalon). Be aware of this hidden
cost.


I
wouldn't recommend using managed C++ for any significant new library development
unless your code is only for COM Interop or other purposes. Perf anecdotes
aren't interesting without looking a specific scenario, and I doubt there's a
big difference between C# and managed C++ when doing useful things for a managed library via COM
Interop. After talking to the dev team about our Interop support in
managed C++, this has only reaffirmed a little bit of my bias against managed
C++. 

There are really 3 different ways you should think about calling
the Win32 API's in managed C++. 

1) Unmanaged

Here, the compiler just emits whatever X86 is necessary to make a
call to a Win32 method, same as in straight unmanaged code. This has 0
marshaling support for managed data types. You can use something like
#pragma unmanaged in your code to make sections of your code completely native,
etc. It sounds very powerful, but a bit on the complex & limited side
since you can't really use managed types here.

2) Goes through the managed marshaler, but uses unmanaged data
types

By default if you try calling the Win32 API when using the /clr
switch, the compiler makes essentially P/Invoke function prototypes for each
Win32 API you include from a header file. These go through the managed
P/Invoke marshaling layer, but the methods are prototyped the same way they are
prototyped in Win32. So instead of a P/Invoke method taking a String
reference or a StringBuilder, you instead have P/Invoke methods taking an
LPCTSTR or LPTSTR. This is a bit difficult for writing managed code – it
can certainly be done, but you'll have to manually pin your managed String
objects, etc. (I believe there are some templates from the C++ team to
help make this easier, but still, I wouldn't want to force myself to use
them.)

According to Adam Nathan from our managed services team, there is
one huge drawback here for anyone considering writing a library using this
functionality. By default the compiler generates prototypes that don't
store the error code in an interesting place. Because of this, you can't
get useful information from Marshal.GetLastWin32Error(). Additionally, you
can't P/Invoke to Win32's GetLastError because the CLR may call some other
methods that call SetLastError while marshaling the return value or out params
back to managed C++. This sounds like it could be an issue for anyone who
is seriously considering writing a library in MC++. 

3) Marshal managed data types

The third way of calling the Win32 API from managed C++ would be
to define your own function prototype that does what you want. In here,
you'd definitely want to make sure the last error value is preserved, and you
can use real managed data types in your prototype as well. This is pretty
important in my opinion – you get correct string marshaling for ANSI/Unicode
systems, and you can use SafeHandle. As you may know, SafeHandle is our
reliable handle wrapper that guarantees you won't leak resources and solves a
handle recycling bug that is pretty much all object-oriented, multithreaded
sandbox environments will run into (like a JVM or user-written C++ running in
something like ASP with some additional security restrictions, if such a thing
exists).

COM Interop is supposedly easier in managed C++, but I don't want
to use COM at all and fortunately it is not required for my job. (Yes, we
do a few things to support COM Interop, but we don't need to use COM in 99% of
the BCL.) I suspect the COM Interop support in C# is probably powerful
enough and I can read Adam's book & the C# language spec to figure it out if
I ever have to deal with it.

From my perspective library writers will always want to use #3
above. Managed C++ does have a lot of power, but the most useful features
are Interop support and slightly bizarre pointer manipulation things (such as a
reference to a boxed value type like "boxed Int32 *" only with probably nastier
syntax). Given what I do, the Interop support isn't a win for me at all
over C#. There are some obscure things I'd like to do that I can't do in
C#. But C# is just so much easier to use, and I find managed C++ nearly
unreadable. 

We had some other legacy things going on that affected the status
quo:

* In V1 and V1.1, we didn't have a cross-language linker (al works
but only produces multi-module assemblies which we wanted to
avoid).

* On the BCL team, we have ~900 methods that call directly into
the CLR to do tricky or internal things, and about 50 of those couldn't be
written in C# because the language wasn't powerful enough. Managed C++
would have helped us, but C# later added features that put it roughly on par
with Managed C++.

From this, hopefully you can understand why the
.NET Framework is written almost exclusively in C#. (I think we have two
exceptions – a Managed C++ dll for COM Interop custom marshalers, and I think
there's one small piece written directly in IL but I don’t recall where.)
Given a cross-language linker, we might consider rewriting some isolated pieces
in managed C++, but we haven't had a serious need yet to add this complexity to
our build process.