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
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().
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!
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);
}
uint, int are 32 bit. ulong is 64bit
New type decimal
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.
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();
}
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.)
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();
}
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.
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();
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());
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)
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
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)
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);
| 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 |
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);
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;}
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)
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 ?
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);
}
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
The late binding of a component at run time.
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!