Notes on C# for C++ programmers

N.G.Hubbard July 2000

Accessors
Array bounds checking
Boxing
Classes
Component
Control Statements
Delegates
Exceptions
Exception Handler
Finally
ForEach
Indexers
Initialisation
Method Hiding
Method Parameters
Modifiers
Namespace
Platform Invocation Services
Reflection
Statement Checking
Switch
Symbols
Types
Unsafe Code
Using

Initialisation

Array bounds checking

public Color[] m_c = {
    Color.Coral, 
    Color.Black, 
    Color.White, 
    Color.Red, 
    Color.White,
    Color.Blue};

Note the "using" statement - good old Pascal!

using System.Drawing

means I don't have to do this:

System.Drawing.Color.White

in the array initialiser

When I walk off the end of the array - the NGWS (.NET) runtime throws an exception! 
To prevent this I could handle the exception, a bit contrived, use GetUpperBound().

Exceptions

Handling an exception:

try
{
    button1.BackColor= m_c[m_iIndex++];
}
catch (IndexOutOfRangeException)
{
    m_iIndex = 0;
    button1.BackColor= m_c[m_iIndex++];
}
catch (Exception Ex)
{
    // do something...
    throw;
}
finally
{
}

"exceptions should not be used for normal errors" - what's a normal error?

The optional finally block always gets called. It will be even be called if there are no exceptions.
It will also be called if your program "blows up". Even though your user has seen what a pile of shit your application is, after the runtime gives a rude message, you can still tidy up! 

Exception Handler

public class MyVeryOwnException:Exception
{
    public MyVeryOwnException ()
        :base() {}

    public MyVeryOwnException (string sz)
        :base(sz + " Oh bugger!") {}

    public MyVeryOwnException (string sz, Exception inner)
        :base(sz + " Hi there!" , inner) {}
}

and to test...

public bool MoveNext()
{
    try
    {
        if (iIndex == 2)
            throw new MyVeryOwnException("index is 2");
        iIndex++;
    }
    catch (MyVeryOwnException)
    {
        iIndex = 3;
    }
    return iIndex <= m_Names.szCharacterArray.GetUpperBound(0);
}

Types

uint, int are 32 bit. ulong is 64bit

New type decimal

Struct, Class and other Reference Types

In C# struct differs from a C++ struct. 
Struct is a value type where actual data is stored.
Class is a reference type where a pointer to data is stored.

Reference types

Delegates

Example 1

Here's a fragment of code that reports mouse wheel speed:

I use a class field (member variable)

private event MouseEventHandler eventMyHandler;

Initialise it

eventMyHandler = new MouseEventHandler(MyMouseEventHandler);
AddOnMouseWheel(eventMyHandler);

And when we get called we do this:

void MyMouseEventHandler( object sender, MouseEventArgs e)
{
    button1.Text = sender.ToString() + " " + e.Delta.ToString();
}

Example 2

I can do away with the class field. The initialisation now looks like:

AddOnMouseWheel(new MouseEventHandler(MyMouseEventHandler));

(I don't need to keep the "pointer" around for deletion.)

Example 3

A bare bones example

//forward declaration

public delegate decimal MyEventHanderThatDoublesDecimalsDecl(decimal d);

// implementation

decimal MyEventHanderThatDoublesDecimalsImpl(decimal d)
{
    return d * 2;
}

private event MyEventHanderThatDoublesDecimalsDecl MyDoubler;
call it on my OnClick
protected void MyClick(object sender, System.EventArgs e)
{
    if (MyDoubler == null)
    {
        MyDoubler = new MyEventHanderThatDoublesDecimalsDecl(MyEventHanderThatDoublesDecimalsImpl);
    }
    decimal dec = MyDoubler(42);
    button1.Text = dec.ToString(); 
}

Boxing

Everything is an object, only sometimes. (!!)

Boxing is the binding of a value type (e.g. ulong) to an object.

ulong ulVar = 42;
object objVar = ulVar;

Unboxing is the binding of an object to a value type;

ulong ulVar1 = (ulong)objVar;
We get an InvalidcastException for the following:
decimal decVar2 = (decimal) objVar;

... the object objVar doesn't "know" about decimal, only ulong.

Classes

All classes must be newed. You can't do this:

class classmew
{
    public int m_i;
};

classmew dd;
dd.m_i = 42;
button1.Text = dd.ToString(); 
The compiler will whinge at run time - you must new it!
class classmew
{
    public int m_i;
};

classmew dd = new classmew();
dd.m_i = 42;
button1.Text = dd.ToString(); 

Method Parameters

Overriding Methods 

Bit like C++ but the keyword override is used in the derived class.

class cat
{
    protected virtual string MyNameIs()
    {
        return "mew";
    }
    public string AskMeMyName()
    {
        return MyNameIs();
    }
}

class barrel : cat
{
    protected override string MyNameIs()
    {
        return "Barrel";
    }
}

class box : barrel
{
    protected override string MyNameIs()
    {
        return "Box";
    }
}

and to test

cat c = new barrel();
MessageBox.Show(c.AskMeMyName());
MessageBox.Show(new cat().AskMeMyName());
MessageBox.Show(new box().AskMeMyName());

Method Hiding

The scenario - you use someone else's base class. In your derived class you have a method 

void Fluffy(int x);

One day the base class gets that method inserted - the guy writing the base class saw your wonderful Fluffy routine and nicked it. Now when you compile which Fluffy do you want to do?

use yours by doing a:

new public void Fluffy(int x)

or use his by casting

(<HisBaseClass>) xyz.Fluffy(x)

Example:

class HisBaseClass
{
    public string MyNameIs()
    {
        return "mew";
    }

    public string AskMeMyName()
    {
        return MyNameIs();
    }
}

class barrel : HisBaseClass
{
    new public string MyNameIs()
   {
        return "Barrel";
    }
}

class box : barrel
{
    new public string MyNameIs()
    {
        return "Box";
    }
    
    public string MyNameIsTest()
    {
        return ((HisBaseClass)this).MyNameIs();
    }
}

and to test

HisBaseClass c = new barrel();
MessageBox.Show(c.AskMeMyName());

MessageBox.Show(new HisBaseClass().AskMeMyName());
MessageBox.Show(new box().AskMeMyName());
MessageBox.Show(new box().MyNameIs()); // calls mine
MessageBox.Show(new box().MyNameIsTest()); // calls his

Accessors

private string m_szMew;

public string MyAccessor
{
    get {return m_szMew;}
    set
    {
        if (value[0] == 'C')
            m_szMew = value;
    }
}

use "MyAccessor" where you would m_szMew if it were public

(Get a stack overflow if I try to virtualise a get or put.... ho hum)

Indexers

class box 
{
    public HisBaseClass()
    {
        m_szMew = "mew";
    }
    public int GetLength()
    {
        return m_szMew.Length;
    }
    public char this[int iIndex] // this is the Indexer
    {
        get { return m_szMew[iIndex];}
    }

    public string m_szMew;
    ... // see above 
}

And to test...

box BrokenCat = new box();
BrokenCat.m_szMew= "Charlie"; 
string szMewer = "";
for (int x = 0; x < BrokenCat.GetLength(); x++)
    szMewer += BrokenCat[x];

MessageBox.Show(szMewer);

Modifiers

  Class Member Access
abstract makes a class abstract A pure virtual function:
there's no body
 
const   expression is evaluated at compilation  
event   client code binding  
extern   external implementation  
internal     public to the .NET component, private elsewhere
override   used to call base class virtual methods  
private     only available to class
protected     only available to class and derived classes
public     available from everywhere
readonly   field can only be set at declaration or in constructor  
sealed Prevents inheritance    
static   static members belong to the class, not a instance of that class.  
virtual   Method or accessor can be overridden by a derived class  

Control Statements

Conditionals work only on bool data types, unlike C++.

The switch statement does not permit fall through. If you want fall through, you must explicitly code:

case 3: goto case 5;
case 4: goto case 5;
case 5: DoBlarrrh(); break;

Each case block must be terminated by a break, or goto. The default case may be put anywhere in the body. 
The switch conditional may now be a string.

case "Barrel":
case "Box":
case "Indica":
    WriteLine("Hello Fluffy");
    break;

ForEach comes from VB. The class to be iterated must support GetEnumerator, MoveNext, Current

The collection class implementation appears a bit convolved:

class CollectionClassTypeSouthPark
{
    string[] szCharacterArray = {
                    "Stan Marsh", "Kyle Broslofski",
                    "Eric Cartman", "Kenny McCormick"};

    public MyEnumerator GetEnumerator()
    {
        return new MyEnumerator(this);
    }

    public class MyEnumerator
    {
        int iIndex;
        readonly CollectionClassTypeSouthPark m_Names;

        public MyEnumerator(CollectionClassTypeSouthPark col)
        {
            iIndex = -1;
            m_Names = col;
        }
   
        public bool MoveNext()
        {
            iIndex++;
            return iIndex <= m_Names.szCharacterArray.GetUpperBound(0);
        }

        public string Current
        {
            get {return m_Names.szCharacterArray[iIndex];}
        }
    }
}

but the usage is easy enough:

string szBoys = "";
CollectionClassTypeSouthPark col = new CollectionClassTypeSouthPark();

// Display collection items:
foreach (string i in col)
{
    szBoys += i;
    szBoys += "\n";
}
MessageBox.Show(szBoys); 

Statement Checking

Or what to do with arithmetic overflows.

The runtime by default keeps quiet about overflows. The complier switch /checked+ will trap overflows and cause an exception for your code to handle.

There is fine grain control by using

checked { fred = fred * 1000;}

or  if an overflow is OK when you told the compiler they weren't:

unchecked { fred = fred * 1000;}

Namespace and a simple Component

The component:

namespace MyFirstComponentProject
{
using System;

public class ClassObj1XXX
{
    public static string MyToLower(string sz)
    {
        return sz.ToLower();
    }
}
}

The component is exercised:

using System;
using MyFirstComponentProject;
public class Class1
{
    public static int Main(string[] args)
    {
        Console.WriteLine("Hello. Enter some uppercase text"); 
        Console.WriteLine(ClassObj1XXX.MyToLower(Console.ReadLine()));
        Console.WriteLine("Goodbye");
        Console.ReadLine();
        return 0;
    }
}

The compile option/r MyFirstComponentProject.dll is required. (Use the reference dialog on VS7)

Symbols

Symbol definition must occur at the start of the source file, before any other statements.

#define DEBUG
using system;
...

Raise a complier warning with a #warning This is a warning!

Raise a complier error with a #error This is an error!

Conditional method compilation:

[conditional("DEBUG")]
    public void MyMethod()
    {
        ...
    }

only void return types are allowed! 

But then: why not surround the whole block with #if ... #endif ?

Platform Invocation Services

Here I am calling the 32bit SDK API...

[sysimport(dll="user32", name = "MessageBox")]
    public static extern int MyBox(int h, string m, string c, int i);

[sysimport(dll="user32", name = "InvalidateRect")]
    public static extern bool MyInvalidate(int h, int pr, bool b);

[sysimport(dll="kernel32", name = "Sleep")]
    public static extern void MySleep(uint t);

protected void DoClick(object sender, System.EventArgs e)
{
    int i = MyBox(0, "Hello", "C#", 0);
    System.Type t = button1.WindowTarget.GetType();
    System.WinForms.NativeWindow n = (NativeWindow)button1.WindowTarget;
    MySleep(2000);
    bool b = MyInvalidate(n.Handle, 0, true);
}

Unsafe Code

Let's get dirty with pointers. They do exist after all!

Here I'm using a pointer to a Rect...

[sysimport(dll="user32", name = "MessageBox")]
    public static extern int MyBox(int h, string m, string c, int i);

[sysimport(dll="user32", name = "InvalidateRect")]
    public static extern bool MyInvalidate(int h, int pr, bool b);

[sysimport(dll="kernel32", name = "Sleep")]
    public static extern void MySleep(uint t);

int[] MyRect = {50, 50, 400, 400};

protected void DoClick(object sender, System.EventArgs e)
{
    int i = MyBox(0, "Hello", "C#", 0);
    System.Type t = button1.WindowTarget.GetType();
    System.WinForms.NativeWindow n = (NativeWindow)button1.WindowTarget;
    MySleep(2000);

    unsafe
    {
        fixed(int* pRect = MyRect)
        MyInvalidate(n.Handle, (int)pRect, true);
    }
}

Note the keywords: unsafe and fixed 

unsafe - yes we're using pointers.
fixed - tells the garbage collector we're using a chunk of memory

Reflection

The late binding of a component at run time.

Links

Photos Journal
Nick's initial Agenda
Nick's first C# program
More C# program (ettes)
Nick's notes about C#
Andrew's comments on the PDC
Microsoft PDC site

January 2002, and Nick uses C# to write a real application!