Wordsmyth's Corner

C++ to C# - Making the Jump

By Linda Naughton

This tutorial provides an introduction to C# intended for C++ programmers.


Topics

Introduction

This tutorial is geared towards C# as used in the .NET Framework 2.0. It was written as a set of talking points for a presentation, so it is not very verbose.

All information is provided without warranty, express or implied.

What is C#?

C++ meets Java meets VB

Garbage collection and memory management

Strings and lists

.NET Framework - queues, XML, sockets, and all sorts of other utilities

Visual Studio .NET Designer - rapid GUI design

Variables and Types

Value Types – int, float, char, string, etc.
Reference Types – objects
No Pointers
C++ Way:   
   SampleObject mySampleObject;        // Instantiates a copy
   mySampleObject.SetName("Foo");  // OK

C# Way:   
    ** Wrong: **
    SampleObject mySampleObject;        // Creates a REFERENCE 
    mySampleObject.SetName("Foo");  // NULL pointer exception

    ** Correct: **
    SampleObject mySampleObject = new SampleObject();    
    mySampleObject.SetName("Foo");
Even value types are objects with useful functions:
    string myString = "Hello world.";
    if (myString.Contains("Hello")) { … }

    int myInt = Integer.Parse("123");

    float myFloat = 12.34;
    Console.WriteLine("Value: " + myFloat.ToString());
Methods can pass back objects as references, but be careful!
    class SampleObjectMgr
    {
      SampleObject m_currentSampleObject = new SampleObject("Foo");
    
      // Returns a reference to the REAL current SampleObject
      SampleObject GetCurrentSampleObject()  
      { 
      return m_currentSampleObject;
      }
    } 
    
   // This will actually CHANGE SampleObjectMgr's SampleObject
   SampleObject myProt = SampleObjectMgr.GetCurrentSampleObject();
   myProt.SetName("Bar");    
A better (safer) way:
    class SampleObjectMgr
    {
       SampleObject m_currentSampleObject = new SampleObject("Foo");
    
       // Return a COPY of the SampleObject
       SampleObject GetCurrentSampleObject()  
       { 
       return new SampleObject(m_currentSampleObject); 
       }
    }

Memory Management

Creation is done using 'new'.
Deletion/cleanup is done automatically when a variable goes out of scope.
(Most of the time, anyway)
void DoSomething()  {
   SampleObject obj = new SampleObject();
   ...
   // obj is auto-scheduled for garbage collection
   }
If the object is passed to another function that stores a reference to it, C# is smart enough to not delete it so long as someone is referencing it.

Exception: Objects that implement the "Dispose" method use “unmanaged” resources. Must call Dispose manually when you're done with it.

void DrawBorder()  {
   Pen myPen = new Pen(Color.Black);
   ...
   // Pen class implements Dispose, so call it
   myPen.Dispose();
   }


Enums

Strongly typed.
   enum VehicleType
   {
   Car,
   Truck,
   Motorcycle
   }

VehicleType type = VehicleType.Car;
Can be converted to and from strings.
    VehicleType vehicleType = VehicleType.Car;
    file.Write(vehicleType.ToString());

    string vehicleTypeString = “Car”;
    VehicleType vehicleType = 
    (VehicleType) Enum.Parse(typeof(VehicleType),
            vehicleTypeString
Can also get a list of names in the enum:
    string[] vehicleTypeNames =
    Enum.GetNames(typeof(VehicleType));

Arrays and Lists (Collections)

Raw arrays are not recommended (in most cases), though they exist.
    string[] myStrings = new string[2];
    myStrings[0] = "ABC";
    myStrings[1] = "DEF";
    Console.WriteLine(myStrings[0]);
Better to use a List collection.
    List<string> myStringList = new List<string>();
    myStringList.Add("ABC");
    myStringList.Add("DEF");
    int numItems = myStringList.Count;
    Console.WriteLine(myStringList[0]);
Various types of lists exist:
    SortedList
    Dictionary
    Etc.
Lists can store any object type.
   List<string> stringList = new List<string>();
   List<int> intList = new List<string>();
   List<Vehicle> vehicleList = new List<Vehicle>();

Loops

Standard loops exist: for, while, etc.
Also new: foreach
    foreach(string myString in myStringList)
    {  Console.WriteLine(myString);  }

Exceptions

Some are issued automatically.
    // Will issue null pointer exception
    SampleObject mySampleObject;
    mySampleObject.SetName("foo");

    // Will issue a parsing exception
    Integer.Parse("ABC");
Can also issue them on demand.
    if (myValue > 174) 
    { throw new Exception("The value is too big! " + myValue); }
Can also issue custom exception types with custom arguments.
    throw new SampleObjectException(SampleObjectError.BadName);

Exception Handling:

Uses try/catch blocks
void Bar()
{
   try
   {
   Foo();
   }
   catch   // Catches ALL exceptions thrown by Foo
   {
   MessageBox.Show(“Error with  Foo.”);
   }
        // Because the catch statement didn't shut down the
        // app or return or anything, execution will
        // continue from here after the message box.
        DoSomethingElse();
}
Can catch only specific exceptions if you want:
void Bar()
{
   try
   {
   Foo();
   }
   // Catch only parsing and socket exceptions.
   catch (ParsingException)
   {
   MessageBox.Show(“Error parsing foo data.”);
   }
   catch (SocketException)
   {
   socket.Close();
   }
        DoSomethingElse();
}
Unhandled exceptions filter “up” to the calling routine, and up and up and up until finally if nobody has handled the exception, the App crashes with the standard windows “FooApplication has suffered an unexpected error and must be closed” message box.

Classes

Same general rules as C++
No Header Files
    public class SampleObject
    {
       string m_name;
       List m_phases;

    public SampleObject()
    {
    m_name = "";
    m_items  = new List();
    }

    public void AddItem(ContentItem item) { … }
    }
Inheritance is also the same:
    public class ChildSampleObject : SampleObject  { … }
Visibility is mostly the same:
    
public
    protected
    private
    internal  (public within namespace; private elsewhere)
Default for members: private
Default for methods: internal Partial classes may be spread across multiple files.
    // In SampleObject.cs
    public partial class SampleObject  { … }

    // In SampleObject.Designer.cs
    public partial class SampleObject  { … }

    // In SampleObject.Events.cs
    public partial class SampleObject  { … }

Properties

New class feature: Properties
Ideal for get/set members
    C++ way:
        string m_name;
        public void SetName(string name) { … };
        public string GetName() { … };

    C# way:

        string m_name;
        public string Name
        {
         get  { return m_name; }

         // Set uses the special "value" parameter
         set { m_name = value; }
        }

Interfaces

New class feature: Interfaces
Ideal for classes that just need to expose an interface, and don't need to inherit data or methods from a base class.
interface ISerializable
{
void Serialize();
void Deserialize();
}

class Message : ISerializable
{
void Serialize() { … }
void Deserialize() { … } 
}
A class can implement multiple interfaces, but can only have one parent class:
class ChildSampleObject: ISerializable, 
                   IComparable, SampleObject {} 

Namespaces and Using

All classes must belong to a namespace.
In SampleObject.cs:
namespace Sample
{
    class SampleObject { … }
}

In ContentItem.cs:
namespace Sample
{
    class ContentItem { … }
}
You can also create sub-namespaces:
    namespace Sample.Test  { … } 
You must 'use' a namespace before you can call things in it. Common .NET utilities exist in the “System” namespace (though there are others).
    using System.Collections;
    using Injector.SampleObject;

Events and Delegates

There's a built-in method for issuing and handling asynchronous events.

1. Define the event, event handler prototype (delegate), and event handlers



2. Receivers subscribe to the event so the Sender knows they want it



3. Sender issues the event, calling the subscribers' event handlers



See also Defining custom events.

Threads

Used for multi-threading.
StatusMgr()
{
Thread m_updateThread = new Thread(StatusUpdateThread);
m_updateThread.IsBackground = true;
m_updateThread.Start();
}

void StatusUpdateThread()
{
…
Thread.Sleep(STATUS_UPDATE_TIME);
}
Thread safety: Built-in semaphores with lock and/or monitor
class Test
    {
    object m_padlock = new Object();
    int m_testValue = 0;

       public TestValue
       {
          get
          { 
          lock (m_padlock)
             { return m_testValue; } 
          }
          set
          { 
          lock (m_padlock)
             { m_testValue = value; }
          }
       }
 
       void TestThead()
          {
          lock (m_padlock)
      {
      m_testValue++;
      }
       }
    }
Monitor works the same as lock, but has an Enter and Exit method, and a TryEnter with timeout:
    if (Monitor.TryEnter(m_padlock, TIMEOUT_MS)
    {
       m_testValue++;
       Monitor.Exit(m_padlock);
    }
    else
    {
    // handle timeout error
    }
C# is smart enough not to deadlock if you lock again from within the same thread.

Threads, Events and Invoke

.NET Thread Model:
There is no queue. The event handler is called directly by Thread2 just like a normal method.
MUST WORRY ABOUT THREAD SAFETY!



You can use "Invoke" or "BeginInvoke" to hold off processing of the event until Thread1 runs.





Sample invoke code:

if (InvokeRequired)  
{
   Invoke(
      (MethodInvoker)delegate()    
         { 
         ProcessStatusChange();
         }
   );
}
else  
{
ProcessStatusChange();
} 
Note:
  • Can't blindly invoke. Only invoke if InvokeRequired, or you'll get an exception.
  • The method name delegate() is just the name of that one-shot method. It could just as easily be foo().
  • If there are more than a couple lines of code called, it's probably better to make a real delegate method rather than the temporary MethodInvoker delegate.