Cover | Table of Contents | Colophon
http://genamics.com/developer/csharp_comparative.htm.http://www.ecma.ch.namespace FirstProgram {
using System;
class Example {
static void Main () {
Console.WriteLine ("Hello world!");
}
}
}
Example that contains a method named Main,
which has a single statement that writes Hello
world! to
the console window. C# recognizes this method as
the default entry point of execution, so that's where the program begins. Console class encapsulates standard input/output
functionality, providing methods such as WriteLine. To
use types from another namespace, use the using directive.
Since the Console class resides in the System namespace,
we go via System; similarly, types from other namespaces
could use our Example class by using FirstProgram.using System;
class Test {
static void Main () {
...
}
}
namespace FirstProgram {
using System;
class Example {
static void Main () {
Console.WriteLine ("Hello world!");
}
}
}
Example that contains a method named Main,
which has a single statement that writes Hello
world! to
the console window. C# recognizes this method as
the default entry point of execution, so that's where the program begins. Console class encapsulates standard input/output
functionality, providing methods such as WriteLine. To
use types from another namespace, use the using directive.
Since the Console class resides in the System namespace,
we go via System; similarly, types from other namespaces
could use our Example class by using FirstProgram.using System;
class Test {
static void Main () {
...
}
}
@ prefix may be used to avoid a clash
with a keyword, but is not considered part of the identifier. For instance,
the following two identifiers are equivalent:Korn @Korn
abstract |
as |
base |
bool |
break |
byte |
case |
catch |
char |
checked |
class |
const |
continue |
decimal |
default |
delegate |
do |
double |
else |
enum |
event |
explicit |
ToLower method and Length property,
which are function members of that string.using System;
class Test {
static void Main () {
int x = 3;
int y = x; // assign x to y, y is now a copy of x
x++; // increment x to 4
Console.WriteLine (y); // prints 3
}
}
y is updated, while in our previous example, y remained
unchanged:using System;
using System.Text;
class Test {
static void Main () {
StringBuilder x = new StringBuilder ("hello");
StringBuilder y = x;
x.Append (" there");
Console.WriteLine (y); // prints "hello there"
}
}
StringBuilder type is a reference
type, while the int type is a value type. When we declared the StringBuilder variable,
we were actually doing two separate things, which can be separated
into these two lines:sbyte, short, int, long)byte, ushort, uint, ulong)float, decimal, char, bool)System namespace. For example,
there is only a syntactic difference between these two statements:int i = 5; System.Int32 i = 5;
|
C#
type
|
System
type
|
Size
|
Signed
|
|---|---|---|---|
sbyte
|
System.SByte
|
1 byte
|
yes
|
short
|
type [*] + array-name = [ new type [ dimension+ ][*]*; | { value1, value2, ... }; ]
[*] is the set: [] [,] [, ,] ...
char[] vowels = new char[] {`a','e','i','o','u'};
Console.WriteLine(vowels [1]); // Prints "e"
System.Collection classes provide
dynamically sized arrays, as well as other data structures, such as associative
(key/value) arrays.for loop,
which is explained in the statements section. The for loops
here simply iterate through each item in the arrays.// rectangular
int [,,] matrixR = new int [3, 4, 5]; // creates 1 big cube
// jagged
int [][][] matrixJ = new int [3][][];
for (int i = 0; i < 3; i++) {
matrixJ[i] = new int [4][];
for (int j = 0; j < 4; j++)
matrixJ[i][j] = new int [5];
}
// assign an element
matrixR [1,1,1] = matrixJ [1][1][1] = 7;
int[,] array = {{1,2},{3,4}};
GetLength method returns the number of
elements for a given dimension, which is from 0 (the outermost) to the array's using System;
class Test {
int v;
// Constructors that initalize an instance of a Test
public Test() {} // v will be automatically assigned to 0
public Test(int a) { // explicitly assign v a value
v = a;
}
static void Main() {
Test[] tests = new Test [2]; // declare array
Console.WriteLine(tests[1]); // ok, elements assigned to null
Test t;
Console.WriteLine(t); // error, t not assigned before use
}
}
|
Type
|
Default
value
|
|---|---|
|
Numeric types
|
0
|
((1 + 2) / 3)
1 + 2 + 3 * 4
((1 + 2) + (3 * 4))
|
Category
|
Operators
|
|---|---|
|
Primary
|
Grouping:
(x)
Member access:
x.y
|
[variable =]? expr;
invocation, new, ++, --). An expression statement ends in a semicolon. For example:x = 5 + 6; // assign result x++; // side effect y = Math.Min(x, 20); // side effect and assign result Math.Min(x, y); // discards result, but ok, there is a side effect x == y; // error, has no side effect, and does not assign result
type [variable [ = expr ]?]+ ;
const type [variable = constant-expr]+;
bool a = true;
while(a) {
int x = 5;
if (x==5) {
int y = 7;
int x = 2; // error, x already defined
}
Console.WriteLine(y); // error, y is out of scope
}
const double speedOfLight = 2.99792458E08; speedOfLight+=10; // error
namespace name+ // Dot-delimited { using-statement* [namespace-declaration | type-declaration]* // No delimiters }
namespace MyCompany.MyProduct.Drawing {
class Point {int x, y, z;}
delegate void PointInvoker(Point p);
}
namespace MyCompany {
namespace MyProduct {
namespace Drawing {
class Point {int x, y, z;}
delegate void PointInvoker(Point p);
}
}
}
namespace TestProject {
class Test {
static void Main() {
MyCompany.MyProduct.Drawing.Point x;
}
}
}
attributes? unsafe? access-modifier?
new?
[ abstract | sealed ]?
class class-name
[: base-class |
: interface+ |
: base-class, interface+ ]?
{ class-members }
Planet, Astronaut, and Test to test our simulation.Planet class. By convention, we define
the data members of the class at the top of the class declaration. There
are two data members here: the name and gravity fields, which store the name and gravity of a planet. We then define a constructor
for the planet. Constructors are function members that allow you to initialize
an instance of your class. We initialize the data members with values
fed to the parameters of the constructor. Finally, we define two more function
members, which are properties that allow us to get the "Name"
and "Gravity" of a planet. The Planet class looks like this:using System;
class Planet {
string name; // field
double gravity; // field
// constructor
public Planet (string n, double g) {
name = n;
gravity = g;
}
// property
public string Name {
get {return name;}
}
// property
public double Gravity {
get {return gravity;}
}
}
Astronaut class. As with the Planet class,
we first define our data members. Here an astronaut has two fields:
the astronaut's fitness, and the current planet the astronaut is on. We then provide
a constructor, which initializes the fitness of an astronaut. Next we define
a CurrentPlanet property that allows us to get or set the planet
an astronaut is on. Finally we define a jump method that outputs how far
the astronaut jumps, based on the fitness of the astronaut and the planet
he is on.
attributes? unsafe? access-modifier?
new?
[ abstract | sealed ]?
class class-name
[: base-class |
: interface+ |
: base-class, interface+ ]?
{ class-members }
Planet, Astronaut, and Test to test our simulation.Planet class. By convention, we define
the data members of the class at the top of the class declaration. There
are two data members here: the name and gravity fields, which store the name and gravity of a planet. We then define a constructor
for the planet. Constructors are function members that allow you to initialize
an instance of your class. We initialize the data members with values
fed to the parameters of the constructor. Finally, we define two more function
members, which are properties that allow us to get the "Name"
and "Gravity" of a planet. The Planet class looks like this:using System;
class Planet {
string name; // field
double gravity; // field
// constructor
public Planet (string n, double g) {
name = n;
gravity = g;
}
// property
public string Name {
get {return name;}
}
// property
public double Gravity {
get {return gravity;}
}
}
Astronaut class. As with the Planet class,
we first define our data members. Here an astronaut has two fields:
the astronaut's fitness, and the current planet the astronaut is on. We then provide
a constructor, which initializes the fitness of an astronaut. Next we define
a CurrentPlanet property that allows us to get or set the planet
an astronaut is on. Finally we define a jump method that outputs how far
the astronaut jumps, based on the fitness of the astronaut and the planet
he is on.using System;
class Astronaut {
double fitness; // field
Planet currentPlanet; // field
// constructor
public Astronaut (double f) {
fitness = f;
}
// property
public Planet CurrentPlanet {
get {
return currentPlanet;
}
set {
currentPlanet = value;
}
}
// method
public void Jump () {
if (currentPlanet == null)
Console.WriteLine ("Bye Bye!");
else {
double distance = fitness/currentPlanet.Gravity;
Console.WriteLine ("Jumped {0} metres on {1}", distance,
currentPlanet.Name);
}
}
}Imagein the System.Drawing namespace, which the Bitmap, Icon,
and Metafile classes inherit from. All classes are ultimately
part of a single giant class hierarchy, of which the root is the Object class. All classes implicitly inherit from it.Location.
This class is very basic, and provides a location with a name property and
a way to display itself to the console window:class Location { // Implicitly inherits from object
string name;
// The constructor that initializes Location
public Location(string n) {
name = n;
}
public string Name {get {return name;}}
public void Display() {
System.Console.WriteLine(Name);
}
}
URL, which
will inherit from Location. The URL class has all the same members as Location, as well as a
new member, Navigate.
Inheriting from a class requires specifying the class to inherit from
the class declaration, using the C++ colon notation:class URL : Location { // Inherit from Location
public void Navigate() {
System.Console.WriteLine("Navigating to "+Name);
}
// The constructor for URL, which calls Location's constructor
public URL(string name) : base(name) {}
}
URL, then invoke both the Display method
(which is defined in Location) and the navigate method
(which is defined in URL):class Test {
static void Main() {
URL u = new URL("http://microsoft.com");
u.Display();
u.Navigate();
}
}
// Assembly1.dll
using System;
public class A {
private int x=5;
public void Foo() {Console.WriteLine (x);}
protected static void Goo() {}
protected internal class NestedType {}
}
internal class B {
private void Hoo () {
A a1 = new A (); // ok
Console.WriteLine(a1.x); // error, A.x is private
A.NestedType n; // ok, A.NestedType is internal
A.Goo(); // error, A's Goo is protected
}
}
// Assembly2.exe (references Assembly1.dll)
using System;
class C : A { // C defaults to internal
static void Main() { // Main defaults to private
A a1 = new A(); // ok
a1.Foo(); // ok
C.Goo(); // ok, inherits A's protected static member
new A.NestedType(); // ok, A.NestedType is protected
new B(); // error, Assembly 1's B is internal
Console.WriteLine(x); // error, A's x is private
}
}
attributes? unsafe? access-modifier?
new?
struct struct-name [: interface+]?
{ struct-members }
System.ValueType).
Both classes and structs can implement interfaces.
attributes? unsafe? access-modifier?
new?
interface interface-name
[ : base-interface+ ]?
{ interface-members }
public interface IDelete {
void Delete();
}
attributes? access-modifier?
new?
enum enum-name [ : integer-type ]?
{ [attributes? enum-member-name
[ = value ]? ]* }
public enum Direction {North, East, West, South}
Direction walls = Direction.East;
[Flags]
public enum Direction : byte {
North=1, East=2, West=4, South=8
}
Direction walls = Direction.North | Direction.West;
if((walls & Direction.North) != 0)
System.Console.WriteLine("Can't go north!");
[Flags] attribute is optional, and informs the
runtime that the values in the enum can be bit-combined, and should be decoded
accordingly in the debugger or when outputting text to the console. For example:Console.WriteLine(walls.Format()); // Displays "North|West" Console.WriteLine(walls); // Calls walls.ToString, displays "5"
System.Enum type also provides many useful static
methods for enums that allow one to determine the underlying type of an enum,
to check if a specific value is supported, to initialize an enum from a string
constant, to retrieve a list of the valid values, and other common operations
such as conversions. Here is an example of the usage:using System;
public enum Toggle : byte { Off=0, On=1 }
class Test {
static void Main() {
Type t = Enum.GetUnderlyingType(typeof(Toggle));
Console.WriteLine(t); // Prints "Byte"
bool bDimmed = Enum.IsDefined(typeof(Toggle), "Dimmed");
Console.WriteLine(bDimmed); // Prints "False"
Toggle tog =(Toggle)Enum.Parse(typeof(Toggle), "On");
Console.WriteLine(Enum.Format(typeof(Toggle), tog, "D")); // Prints "1"
Console.WriteLine(tog); // Prints "On"
object[] oa = Enum.GetValues(typeof(Toggle));
foreach(Toggle toggle in oa) // Prints "On=1, Off=0"
Console.WriteLine("{0}={1}", toggle,Enum.Format(typeof(Toggle),
toggle, "D"));
}
}
attributes? unsafe? access-modifier?
new?
delegate
[ void | type ]
delegate-name (parameter-list);
using System;
delegate bool Filter (string s);
class Test {
static void Main() {
Filter f = new Filter(FirstHalfOfAlphabet);
Display(new String [] {"Ant","Lion","Yak"}, f);
}
static bool FirstHalfOfAlphabet(string s) {
return "N".CompareTo(s) > 0;
}
static void Display(string[] names, Filter f) {
int count = 0;
foreach(string s in names)
if(f(s)) // invoke delegate
Console.WriteLine("Item {0} is {1}", count++, s);
}
}
params modifier in its
parameter list, which expands
the list of elements that characterize an ordinary method signature. The
actual name of the target method is irrelevant to the delegate.MethodInvoker, which
we use to hold and then invoke the Foo and Goo methods
sequentially. The += method creates a new delegate by
adding the right delegate operand to the left delegate operand:using System;
delegate void MethodInvoker();
class Test {
static void Main() {
new Test(); // prints "Foo","Goo"
}
Test () {
MethodInvoker m = null;
m += new MethodInvoker(Foo);
m += new MethodInvoker(Goo);
m();
}
void Foo() {
Console.WriteLine("Foo");
}
void Goo() {
Console.WriteLine("Goo");
}
}
attributes? unsafe? access-modifier?
new?
delegate
[ void | type ]
delegate-name (parameter-list);
using System;
delegate bool Filter (string s);
class Test {
static void Main() {
Filter f = new Filter(FirstHalfOfAlphabet);
Display(new String [] {"Ant","Lion","Yak"}, f);
}
static bool FirstHalfOfAlphabet(string s) {
return "N".CompareTo(s) > 0;
}
static void Display(string[] names, Filter f) {
int count = 0;
foreach(string s in names)
if(f(s)) // invoke delegate
Console.WriteLine("Item {0} is {1}", count++, s);
}
}
params modifier in its
parameter list, which expands
the list of elements that characterize an ordinary method signature. The
actual name of the target method is irrelevant to the delegate.MethodInvoker, which
we use to hold and then invoke the Foo and Goo methods
sequentially. The += method creates a new delegate by
adding the right delegate operand to the left delegate operand:using System;
delegate void MethodInvoker();
class Test {
static void Main() {
new Test(); // prints "Foo","Goo"
}
Test () {
MethodInvoker m = null;
m += new MethodInvoker(Foo);
m += new MethodInvoker(Goo);
m();
}
void Foo() {
Console.WriteLine("Foo");
}
void Goo() {
Console.WriteLine("Goo");
}
}
-= operator:Test {
MethodInvoker m = null;
m += new MethodInvoker(Foo);
m -= new MethodInvoker(Foo);
// m is now null
}IFilter interface:using System;
interface IFilter {
bool Filter(string s);
}
class Test {
class FirstHalfOfAlphabetFilter : IFilter {
public bool Filter(string s) {
return ("N".CompareTo(s) > 0);
}
}
static void Main() {
FirstHalfOfAlphabetFilter f = new FirstHalfOfAlphabetFilter();
Display(new string [] {"Ant", "Lion", "Yak"}, f);
}
static void Display(string[] names, IFilter f) {
int count = 0;
foreach (string s in names)
if (f.Filter(s))
Console.WriteLine("Item {0} is {1}", count++, s);
}
}