Home / Blog andy@andymcm.com

C# FAQ for C++ programmers

Andy McMullan

The C# FAQ for C++ programmers was first posted in Aug 2000, and is regularly updated. It answers many of the questions that C++ developers have when they first encounter C#.

Let me know if you have any comments, corrections or suggestions.


Latest updates:
   20-Oct-2009: New section on C# 3.0


Contents

1. Introduction

1.1 What is C#?

C# is a general-purpose object-oriented programming language designed by Microsoft. It is loosely based on C/C++, and is very similar to Java. You can download the C# 3.0 spec here.

1.2 How do I develop C# apps?

Visual C# Express is the easiest way to get started. On Linux you can use Mono.

1.3 Does C# replace C++?

There are three options open to the Windows developer from a C++ background:

Each of these options has merits, depending on the developer and the application, but for most general purpose applications C# is a much more productive environment than C++. Where existing C++ code must be used with a new application, the existing code can be wrapped using C++/CLI to allow it to interop with C#.

1.4 Does C# have its own class library?

Not exactly. The .NET Framework has a comprehensive class library, which C# can make use of. C# does not have its own class library.

2. Types

2.1 What standard types does C# use?

C# supports a very similar range of basic types to C++, including int, long, float, double, char, string, arrays, structs and classes. However, don't assume too much. The names may be familiar, but many of the details are different. For example, a long is 64 bits in C#, whereas in C++ the size of a long depends on the platform (typically 32 bits on a 32-bit platform, 64 bits on a 64-bit platform). Also classes and structs are almost the same in C++ - this is not true for C#. Finally, chars and strings in .NET are 16-bit (Unicode/UTF-16), not 8-bit like C++.

2.2 Is it true that all C# types derive from a common base class?

Yes and no. All types can be treated as if they derive from object (System.Object), but in order to treat an instance of a value type (e.g. int, float) as object-derived, the instance must be converted to a reference type using a process called 'boxing'. In theory a developer can forget about this and let the run-time worry about when the conversion is necessary, but in reality this implicit conversion can have side-effects that may trip up the unwary.

2.3 So I can pass an instance of a value type to a method that takes an object as a parameter?

Yes. For example:

    class CApplication
    {
        public static void Main()
        { 
            int x = 25; 
            string s = "fred"; 
        
            DisplayMe( x ); 
            DisplayMe( s ); 
        } 
    
        static void DisplayMe( object o ) 
        { 
            System.Console.WriteLine( "You are {0}", o ); 
        }
    } 

This would display:

    You are 25
    You are fred

2.4 What are the fundamental differences between value types and reference types?

C# divides types into two categories - value types and reference types. Most of the intrinsic types (e.g. int, char) are value types. Structs are also value types. Reference types include classes, arrays and strings. The basic idea is straightforward - an instance of a value type represents the actual data, whereas an instance of a reference type represents a pointer or reference to the data.

The most confusing aspect of this for C++ developers is that C# has predetermined which types are represented as values, and which are represented as references. A C++ developer expects to take responsibility for this decision.

For example, in C++ we can do this:

    int x1 = 3;        // x1 is a value on the stack
    int *x2 = new int(3)    // x2 is a pointer to a value on the heap

but in C# there is no control:

    int x1 = 3;        // x1 is a value on the stack
    int x2 = new int(); 
    x2 = 3;        // x2 is also a value on the stack!

2.5 Okay, so an int is a value type, and a class is a reference type. How can int be derived from object?

It isn't, really. When an int is being used as an int, it is a value. However, when it is being used as an object, it is a reference to an integer value (on the managed heap). In other words, when you treat an int as an object, the runtime automatically converts the int value to an object reference. This process is called boxing. The conversion involves copying the int to the heap, and creating an object instance which refers to it. Unboxing is the reverse process - the object is converted back to a value.

    int x = 3;        // new int value 3 on the stack
    object objx = x;    // new int on heap, set to value 3 - still have x=3 on stack
    int y = (int)objx;    // new value 3 on stack, still got x=3 on stack and objx=3 on heap

2.6 Are C# references the same as C++ references?

Not quite. The basic idea is the same, but one significant difference is that C# references can be null . So you cannot rely on a C# reference pointing to a valid object. In that respect a C# reference is more like a C++ pointer than a C++ reference. If you try to use a null reference, a NullReferenceException is thrown.

For example, look at the following method:

    void displayStringLength( string s )
    {
        Console.WriteLine( "String is length {0}", s.Length );
    }        

The problem with this method is that it will throw a NullReferenceException if called like this:

    string s = null;
    displayStringLength( s );

Of course for some situations you may deem a NullReferenceException to be a perfectly acceptable outcome, but in this case it might be better to re-write the method like this:

    void displayStringLength( string s )
    {
        if( s == null )
            Console.WriteLine( "String is null" );
        else
            Console.WriteLine( "String is length {0}", s.Length );
    }        

2.7 Can I use typedefs in C#?

No, C# has no direct equivalent of the C++ typedef. C# does allow an alias to be specified via the using keyword:

    using IntList = System.Collections.Generic.List<int>;

but the alias only applies in the file in which it is declared. A workaround in some cases is to use inheritance:

    public class IntList : List<int> { }

The pros and cons of this approach are discussed here.

3. Classes and Structs

3.1 Structs are largely redundant in C++. Why does C# have them?

In C++, a struct and a class are pretty much the same thing. The only difference is the default visibility level (public for structs, private for classes). However, in C# structs and classes are very different. In C#, structs are value types (instances stored directly on the stack, or inline within heap-based objects), whereas classes are reference types (instances stored on the heap, accessed indirectly via a reference). Also structs cannot inherit from structs or classes, though they can implement interfaces. Structs cannot have destructors. A C# struct is much more like a C struct than a C++ struct.

3.2 Does C# support multiple inheritance (MI)?

No, though it does support implementation of multiple interfaces on a single class or struct.

3.3 Is a C# interface the same as a C++ abstract class?

No, not quite. An abstract class in C++ cannot be instantiated, but it can (and often does) contain implementation code and/or data members. A C# interface cannot contain any implementation code or data members - it is simply a group of method names & signatures. A C# interface is more like a COM interface than a C++ abstract class.

3.4 Are C# constructors the same as C++ constructors?

Very similar, but there are some significant differences. First, C# supports constructor chaining. This means one constructor can call another:

    class Person
    {
        public Person( string name, int age ) { ... }
        public Person( string name ) : this( name, 0 ) {}
        public Person() : this( "", 0 ) {}
    }

Another difference is that virtual method calls within a constructor are routed to the most derived implementation - see Can I Call a virtual method from a constructor.

Error handling is also somewhat different. If an exception occurs during construction of a C# object, the destuctor (finalizer) will still be called. This is unlike C++ where the destructor is not called if construction is not completed. (Thanks to Jon Jagger for pointing this out.)

Finally, C# has static constructors. The static constructor for a class runs before the first instance of the class is created.

Also note that (like C++) some C# developers prefer the factory method pattern over constructors. See Brad Wilson's article.

3.5 Are C# destructors the same as C++ destructors?

No. They look the same but they are very different. The C# destructor syntax (with the familiar ~ character) is just syntactic sugar for an override of the System.Object Finalize method. This Finalize method is called by the garbage collector when it determines that an object is no longer referenced, before it frees the memory associated with the object. So far this sounds like a C++ destructor. The difference is that the garbage collector makes no guarantees about when this procedure happens. Indeed, the algorithm employed by the CLR garbage collector means that it may be a long time after the application has finished with the object. This lack of certainty is often termed 'non-deterministic finalization', and it means that C# destructors are not suitable for releasing scarce resources such as database connections, file handles etc.

To achieve deterministic destruction, a class must offer a method to be used for the purpose. The standard approach is for the class to implement the IDisposable interface. The user of the object must call the Dispose() method when it has finished with the object. C# offers the 'using' construct to make this easier.

Note that it's rarely necessary to define a destructor for a C# class - it only makes sense where the class holds direct references to unmanaged resources, which is very unusual. Implementing IDisposable is somewhat more commonly required, but still only necessary for a small minority of classes.

3.6 If C# destructors are so different to C++ destructors, why did MS use the same syntax?

Presumably they wanted C++ programmers to feel at home. I think they made a mistake.

3.7 Are all methods virtual in C#?

No. Like C++, methods are non-virtual by default, but can be marked as virtual.

3.8 How do I declare a pure virtual function in C#?

Use the abstract modifier on the method. The class must also be marked as abstract. Note that abstract methods cannot have an implementation (unlike pure virtual C++ methods).

3.9 Can I call a virtual method from a constructor/destructor?

Yes, but it's generally not a good idea. The mechanics of object construction in .NET are quite different from C++, and this affects virtual method calls in constructors.

C++ constructs objects from base to derived, so when the base constructor is executing the object is effectively a base object, and virtual method calls are routed to the base class implementation. By contrast, in .NET the derived constructor is executed first, which means the object is always a derived object and virtual method calls are always routed to the derived implementation. (Note that the C# compiler inserts a call to the base class constructor at the start of the derived constructor, thus preserving standard OO semantics by creating the illusion that the base constructor is executed first.)

The same issue arises when calling virtual methods from C# destructors. A virtual method call in a base destructor will be routed to the derived implementation.

3.10 Should I make my destructor virtual?

A C# destructor is really just an override of the System.Object Finalize method, and so is virtual by definition.

4. Exceptions

4.1 Can I use exceptions in C#?

Yes, in fact exceptions are the recommended error-handling mechanism in C# (and in .NET in general). Most of the .NET framework classes use exceptions to signal errors.

4.2 What types of object can I throw as exceptions?

Only instances of the System.Exception classes, or classes derived from System.Exception. This is in sharp contrast with C++ where instances of almost any type can be thrown.

4.3 Can I define my own exceptions?

Yes, just derive your exception class from System.Exception.

Note that if you want your exception to cross remoting boundaries you'll need to do some extra work - see http://www.thinktecture.com/Resources/RemotingFAQ/CustomExceptions.html for details.

4.4 Does the System.Exception class have any cool features?

Yes - the feature which stands out is the StackTrace property. This provides a call stack which records where the exception was thrown from. For example, the following code:

    using System;

    class CApp 
    {  
        public static void Main() 
        { 
            try 
            { 
                f(); 
            } 
            catch( Exception e ) 
            { 
                Console.WriteLine( "System.Exception stack trace = \n{0}", e.StackTrace ); 
            }
        } 

        static void f() 
        { 
            throw new Exception( "f went pear-shaped" ); 
        } 
    }

produces this output:

    System.Exception stack trace = 
        at CApp.f() 
        at CApp.Main()

Note, however, that this stack trace was produced from a debug build. A release build may optimise away some of the method calls which could mean that the call stack isn't quite what you expect.

4.5 When should I throw an exception?

This is the subject of some debate, and is partly a matter of taste. However, it is accepted by most that exceptions should be thrown only when an 'unexpected' error occurs. How do you decide if an error is expected or unexpected? This is a judgement call, but a straightforward example of an expected error is failing to read from a file because the seek pointer is at the end of the file, whereas an example of an unexpected error is failing to allocate memory from the heap.

4.6 Does C# have a 'throws' clause?

No, unlike Java, C# does not require (or even allow) the developer to specify the exceptions that a method can throw.

5. Run-time Type Information

5.1 How can I check the type of an object at runtime?

You can use the is keyword. For example:

    using System; 

    class CApp
    {
        public static void Main()
        { 
            string s = "fred"; 
            long i = 10; 

            Console.WriteLine( "{0} is {1}an integer", s, (IsInteger(s) ? "" : "not ") ); 
            Console.WriteLine( "{0} is {1}an integer", i, (IsInteger(i) ? "" : "not ") ); 
        }
    
        static bool IsInteger( object obj )
        { 
            if( obj is int || obj is long )
                return true; 
            else 
                return false;
        }
    } 

produces the output:

    fred is not an integer 
    10 is an integer

5.2 Can I get the name of a type at runtime?

Yes, use the GetType method of the object class (which all types inherit from). For example:

    using System; 

    class CTest
    {
        class CApp 
        {
            public static void Main()
            { 
                long i = 10; 
                CTest ctest = new CTest(); 

                DisplayTypeInfo( ctest ); 
                DisplayTypeInfo( i ); 
            }
        
            static void DisplayTypeInfo( object obj ) 
            { 
                Console.WriteLine( "Type name = {0}, full type name = {1}", obj.GetType(), obj.GetType().FullName ); 
            }
        }
    }

produces the following output:

    Type name = CTest, full type name = CTest 
    Type name = Int64, full type name = System.Int64

5.3 What is the difference between typeof and GetType()?

Apart from the obvious (i.e. typeof operates on a type whereas GetType operates on an object), the main thing to watch out for is that GetType returns the underlying type of the object, which may not be the same as the type of the reference to the object. For example:

    class Base { }
    class Derived : Base { }

    class Program
    {
        static void Main()
        {
            ShowType( new Derived() );
        }

        static void ShowType( Base b )
        {
            Console.WriteLine(typeof(Base));
            Console.WriteLine(b.GetType());
        }
    }

gives the following output:

    Base
    Derived

6. Miscellaneous

6.1 How do I do a case-insensitive string comparison?

Use the string.Compare method. Its third parameter specifies case sensitivity.

    "fred" == "Fred"    // false
    string.Compare( "fred", "Fred", StringComparison.CurrentCultureIgnoreCase ) == 0    // true

For more control over the comparison, e.g. exotic features like width-sensitivity, consider using System.Globalization.CompareInfo.Compare(), e.g.

    CultureInfo.CurrentCulture.CompareInfo.Compare(
        "fred", "Fred", 
        CompareOptions.IgnoreCase | 
        CompareOptions.IgnoreKanaType | 
        CompareOptions.IgnoreWidth
        );

6.2 Does C# support a variable number of arguments?

Yes, using the params keyword. The arguments are specified as a list of arguments of a specific type, e.g. int. For ultimate flexibility, the type can be object. The standard example of a method which uses this approach is System.Console.WriteLine().

6.3 How can I process command-line arguments?

Like this:

    using System;

    class CApp
    {
        public static void Main( string[] args )
        {
            Console.WriteLine( "You passed the following arguments:" );
            foreach( string arg in args )
                Console.WriteLine( arg );
        }
    }

(Take a look at Charles Cook's NOptFunc project for easy command-line parsing.)

6.4 Does C# do array bounds checking?

Yes. An IndexOutOfRange exception is used to signal an error.

6.5 How can I make sure my C# classes will interoperate with other .NET languages?

Make sure your C# code conforms to the Common Language Subset (CLS). To help with this, add the [assembly:CLSCompliant(true)] global attribute to your C# source files. The compiler will emit an error if you use a C# feature which is not CLS-compliant.

6.6 How do I use the 'using' keyword with multiple objects?

You can nest using statements, like this:

    using( obj1 )
    {
        using( obj2 )
        {
            ...    
        }
    }

However consider using this more aesthetically pleasing (but functionally identical) formatting:

    using( obj1 )
    using( obj2 )
    {
        ...
    }

6.7 What is the difference between == and object.Equals?

For value types, == and Equals() usually compare two objects by value. For example:

    int x = 10;
    int y = 10;
    Console.WriteLine( x == y );
    Console.WriteLine( x.Equals(y) );

will display:

    True
    True

However things are more complex for reference types. Generally speaking, for reference types == is expected to perform an identity comparison, i.e. it will only return true if both references point to the same object. By contrast, Equals() is expected to perform a value comparison, i.e. it will return true if the references point to objects that are equivalent. For example:

    StringBuilder s1 = new StringBuilder("fred");
    StringBuilder s2 = new StringBuilder("fred");
    Console.WriteLine( s1 == s2 );
    Console.WriteLine( s1.Equals(s2) );

will display:

    False
    True

s1 and s2 are different objects (hence == returns false), but they are equivalent (hence Equals() returns true).

Unfortunately there are exceptions to these rules. The implementation of Equals() in System.Object (the one your class inherits by default) compares identity, i.e. it's the same as operator==. So Equals() only tests for equivalence if the class author overrides the method (and implements it correctly). Another exception is the string class - its operator== compares value rather than identity.

Bottom line: If you want to perform an identity comparison use the ReferenceEquals() method. If you want to perform a value comparison, use Equals() but be aware that it will only work if the type has overridden the default implementation. Avoid operator== with reference types (except perhaps strings), as it's simply too ambiguous.

6.8 How do I enforce const correctness in C#?

You can't - at least not in the same way you do in C++. C# (actually, the CLI) has no real concept of const correctness, For example, there's no way to specify that a method should not modify an argument passed in to it. And there's no way to specify that a method does not modify the object on which it is acting.

To get a feel for the angst this causes among some C++ programmers, read the feedback on this post from Raymond Chen.

There are of course ways of addressing this issue. For example, see Brad Abram's post (and associated feedback) for some ideas on adding optional read-only behaviour to collection classes.

7. C# 2.0

7.1 What are the new features in C# 2.0?

Support for all of the new framework features such as generics, anonymous methods, partial classes, iterators and static classes. See the .NET FAQ for more on these features.

Delegate inference is a new feature of the C# compiler which makes delegate usage a little simpler. It allows you to write this:

    Thread t = new Thread(ThreadFunc);

instead of this:

    Thread t = new Thread( new ThreadStart(ThreadFunc) );

Another minor but welcome addition is the explicit global namespace, which fixes a hole in namespace usage in C# 1.x. You can prefix a type name with global:: to indicate that the type belongs to the global namespace, thus avoiding problems where the compiler infers the namespace and gets it wrong.

Finally C# 2.0 includes some syntactic sugar for the new System.Nullable type. You can use T? as a synonym for System.Nullable<T>, where T is a value type. As suggested by the name, this allows values of the type to be 'null', or 'undefined'.

7.2 Are C# generics the same as C++ templates?

No, not really. There are some similarities, but there are also fundamental differences. See the .NET FAQ for more details.

8. C# 3.0

8.1 What's new in C# 3.0?

Support for LINQ was the driving force behind the main enhancements in C# 3.0. Query expressions are the most obvious example, but lambda expressions, extension methods and anonymous types also fall into this category. However most of these enhancements are useful beyond LINQ.

Query expressions allow a SQL-like query syntax to be used in C#, e.g.

    var nameList = new List<string> { "jack", "jill", "sue" };

    var results = from name in nameList 
        where name.StartsWith("j")
        select name;

Query expressions are just syntactic sugar - they resolve to standard method calls. For example the query expression above can be rewritten as:

    var results = nameList.Where(name => name.StartsWith("j"));

The argument to Where() is a lambda expression, which represents an anonymous method. However a lambda expression is not just a more concise way to represent executable code - it can also be interpreted as a data structure (known as an expression tree), which allows the expression to be easily analysed or transformed at runtime. For example, LINQ-to-SQL makes use of this feature to transform C# queries to SQL queries, which gives much better performance than a simple in-memory filtering of results (as offered by DataTable.Select(), for example).

The Where() method shown above is an example of another new C# 3.0 feature: extension methods. Extension methods allow extra methods to be 'attached' to an existing type - any type, even sealed types or types you don't have the source code for. For example, the Where() method can be applied to any type that implements IEnumerable<T>.


Another new feature of C# 3.0 is the var keyword, which allows the compiler to infer the type of a variable from context, which is required for anonymous types but can be used with standard named types too:

    var list = new List<string>() { "jack", "jill", "sue" };    // optional use with named types

    var person = new { name = "jack", age = 20 };     // anonymous type

These examples also demonstrate the new object initializer and collection initializer syntax.

Finally, there are implicitly typed arrays and auto-implemented properties:

    var names = new [] { "jack", "jill", "sue" };    // implicitly string[] 

    public string Name { get; set; }    // auto-implemented property - private backing field auto-generated

9. Resources

9.1 Books

I recommend the following books, either because I personally like them, or because I think they are well regarded by other C# developers. (Note that I get a commission from Amazon if you buy a book after following one of these links.)

See also my .NET recommended books.