Sei sulla pagina 1di 132

Hello World Tutorial

Visual Studio .NET 2003 This tutorial shows several versions of a Hello World program in C#.

Sample Files
See Hello World Sample to download and build the sample files discussed in this tutorial.

Further Reading
Main 1.1 Getting started 3.3 Declarations

Tutorial
The following examples show different ways of writing the C# Hello World program. Example 1

// Hello1.cs public class Hello1 { public static void Main() { System.Console.WriteLine("Hello, World!"); } }
Output

Hello, World!
Code Discussion y y Every Main method must be contained inside a class (Hello1 in this case). The System.Console class contains a WriteLine method that can be used to display a string to the console.

Example 2

To avoid fully qualifying classes throughout a program, you can use the using directive as shown below:

// Hello2.cs using System; public class Hello2 { public static void Main() { Console.WriteLine("Hello, World!"); } }
Output

Hello, World!
Example 3 If you need access to the command line parameters passed in to your application, simply change the signature of the Main method to include them as shown below. This example counts and displays the command line arguments.

// Hello3.cs // arguments: A B C D using System; public class Hello3 { public static void Main(string[] args) { Console.WriteLine("Hello, World!"); Console.WriteLine("You entered the following {0} command line arguments:", args.Length ); for (int i=0; i < args.Length; i++) { Console.WriteLine("{0}", args[i]); } } }
Output

Hello, World! You entered the following 4 command line arguments: A B C D


Example 4

To return a return code, change the signature of the Main method as shown below:

// Hello4.cs using System; public class Hello4 { public static int Main(string[] args) { Console.WriteLine("Hello, World!"); return 0; } }
Output

Hello, World!

Command Line Parameters Tutorial


Visual Studio .NET 2003 This tutorial shows how the command line can be accessed and two ways of accessing the array of command line parameters.

Sample Files
See Command Line Parameters Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y The C# Version of Hello World! foreach, in String.Length Property

Tutorial
The following examples show two different approaches to using the command line arguments passed to an application. Example 1 This example demonstrates how to print out the command line arguments.

// cmdline1.cs // arguments: A B C using System; public class CommandLine { public static void Main(string[] args) { // The Length property is used to obtain the length of the array. // Notice that Length is a read-only property: Console.WriteLine("Number of command line parameters = {0}", args.Length); for(int i = 0; i < args.Length; i++) { Console.WriteLine("Arg[{0}] = [{1}]", i, args[i]); } } }
Output Run the program using some arguments like this: cmdline1 A B C. The output will be:

Number Arg[0] Arg[1] Arg[2]

of command line parameters = 3 = [A] = [B] = [C]

Example 2 Another approach to iterating over the array is to use the foreach statement as shown in this example. The foreach statement can be used to iterate over an array or over a .NET Framework collection class. It provides a simple way to iterate over collections.

// cmdline2.cs // arguments: John Paul Mary using System; public class CommandLine2 { public static void Main(string[] args) { Console.WriteLine("Number of command line parameters = {0}", args.Length); foreach(string s in args) { Console.WriteLine(s); } } }

Output Run the program using some arguments like this: cmdline2 John Paul Mary. The output will be:

Number of command line parameters = 3 John Paul Mary

Arrays Tutorial
Visual Studio .NET 2003 This tutorial describes arrays and shows how they work in C#.

Sample Files
See Arrays Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y Arrays 12. Arrays foreach, in Collection Classes Tutorial

Tutorial
This tutorial is divided into the following sections: y y y y y y Arrays in General Declaring Arrays Initializing Arrays Accessing Array Members Arrays are Objects Using foreach with Arrays

Arrays in General
C# arrays are zero indexed; that is, the array indexes start at zero. Arrays in C# work similarly to how arrays work in most other popular languages There are, however, a few differences that you should be aware of. When declaring an array, the square brackets ([]) must come after the type, not the identifier. Placing the brackets after the identifier is not legal syntax in C#.

int[] table; // not int table[];


Another detail is that the size of the array is not part of its type as it is in the C language. This allows you to declare an array and assign any array of int objects to it, regardless of the array's length.

int[] numbers; // declare numbers as an int array of any size numbers = new int[10]; // numbers is a 10-element array numbers = new int[20]; // now it's a 20-element array

Declaring Arrays
C# supports single-dimensional arrays, multidimensional arrays (rectangular arrays), and array-of-arrays (jagged arrays). The following examples show how to declare different kinds of arrays: Single-dimensional arrays:

int[] numbers;
Multidimensional arrays:

string[,] names;
Array-of-arrays (jagged):

byte[][] scores;
Declaring them (as shown above) does not actually create the arrays. In C#, arrays are objects (discussed later in this tutorial) and must be instantiated. The following examples show how to create arrays: Single-dimensional arrays:

int[] numbers = new int[5];


Multidimensional arrays:

string[,] names = new string[5,4];


Array-of-arrays (jagged):

byte[][] scores = new byte[5][];

for (int x = 0; x < scores.Length; x++) { scores[x] = new byte[4]; }


You can also have larger arrays. For example, you can have a three-dimensional rectangular array:

int[,,] buttons = new int[4,5,3];


You can even mix rectangular and jagged arrays. For example, the following code declares a singledimensional array of three-dimensional arrays of two-dimensional arrays of type int:

int[][,,][,] numbers;
Example The following is a complete C# program that declares and instantiates arrays as discussed above.

// arrays.cs using System; class DeclareArraysSample { public static void Main() { // Single-dimensional array int[] numbers = new int[5]; // Multidimensional array string[,] names = new string[5,4]; // Array-of-arrays (jagged array) byte[][] scores = new byte[5][]; // Create the jagged array for (int i = 0; i < scores.Length; i++) { scores[i] = new byte[i+3]; } // Print length of each row for (int i = 0; i < scores.Length; i++) { Console.WriteLine("Length of row {0} is {1}", i, scores[i].Length); } } }
Output

Length of row 0 is 3 Length of row 1 is 4 Length of row 2 is 5

Length of row 3 is 6 Length of row 4 is 7

Initializing Arrays
C# provides simple and straightforward ways to initialize arrays at declaration time by enclosing the initial values in curly braces ({}). The following examples show different ways to initialize different kinds of arrays. Note If you do not initialize an array at the time of declaration, the array members are automatically initialized to the default initial value for the array type. Also, if you declare the array as a field of a type, it will be set to the default value null when you instantiate the type. Single-Dimensional Array

int[] numbers = new int[5] {1, 2, 3, 4, 5}; string[] names = new string[3] {"Matt", "Joanne", "Robert"};
You can omit the size of the array, like this:

int[] numbers = new int[] {1, 2, 3, 4, 5}; string[] names = new string[] {"Matt", "Joanne", "Robert"};
You can also omit the new operator if an initializer is provided, like this:

int[] numbers = {1, 2, 3, 4, 5}; string[] names = {"Matt", "Joanne", "Robert"};


Multidimensional Array

int[,] numbers = new int[3, 2] { {1, 2}, {3, 4}, {5, 6} }; string[,] siblings = new string[2, 2] { {"Mike","Amy"}, {"Mary","Albert"} };
You can omit the size of the array, like this:

int[,] numbers = new int[,] { {1, 2}, {3, 4}, {5, 6} }; string[,] siblings = new string[,] { {"Mike","Amy"}, {"Mary","Albert"} };
You can also omit the new operator if an initializer is provided, like this:

int[,] numbers = { {1, 2}, {3, 4}, {5, 6} }; string[,] siblings = { {"Mike", "Amy"}, {"Mary", "Albert"} };
Jagged Array (Array-of-Arrays) You can initialize jagged arrays like this example:

int[][] numbers = new int[2][] { new int[] {2,3,4}, new int[] {5,6,7,8,9} };
You can also omit the size of the first array, like this:

int[][] numbers = new int[][] { new int[] {2,3,4}, new int[] {5,6,7,8,9} };
-or-

int[][] numbers = { new int[] {2,3,4}, new int[] {5,6,7,8,9} };


Notice that there is no initialization syntax for the elements of a jagged array.

Accessing Array Members


Accessing array members is straightforward and similar to how you access array members in C/C++. For example, the following code creates an array called numbers and then assigns a 5 to the fifth element of the array:

int[] numbers = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; numbers[4] = 5;


The following code declares a multidimensional array and assigns 5 to the member located at [1, 1]:

int[,] numbers = { {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10} }; numbers[1, 1] = 5;
The following is a declaration of a single-dimension jagged array that contains two elements. The first element is an array of two integers, and the second is an array of three integers:

int[][] numbers = new int[][] { new int[] {1, 2}, new int[] {3, 4, 5} };
The following statements assign 58 to the first element of the first array and 667 to the second element of the second array:

numbers[0][0] = 58; numbers[1][1] = 667;

Arrays are Objects


In C#, arrays are actually objects. System.Array is the abstract base type of all array types. You can use the properties, and other class members, that System.Array has. An example of this would be using the Lengthproperty to get the length of an array. The following code assigns the length of the numbers array, which is 5, to a variable called LengthOfNumbers:

int[] numbers = {1, 2, 3, 4, 5}; int LengthOfNumbers = numbers.Length;


The System.Array class provides many other useful methods/properties, such as methods for sorting, searching, and copying arrays.

Using foreach on Arrays


C# also provides the foreach statement. This statement provides a simple, clean way to iterate through the elements of an array. For example, the following code creates an array called numbers and iterates through it with the foreach statement:

int[] numbers = {4, 5, 6, 1, 2, 3, -2, -1, 0}; foreach (int i in numbers) { System.Console.WriteLine(i); }
With multidimensional arrays, you can use the same method to iterate through the elements, for example:

int[,] numbers = new int[3, 2] {{9, 99}, {3, 33}, {5, 55}}; foreach(int i in numbers) { Console.Write("{0} ", i); }
The output of this example is:

9 99 3 33 5 55
However, with multidimensional arrays, using a nested for loop gives you more control over the array elements.

Properties Tutorial
Visual Studio .NET 2003 This tutorial shows how properties are an integral part of the C# programming language. It demonstrates how properties are declared and used.

Sample Files
See Properties Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y Properties Comparison Between Properties and Indexers 10.6 Properties

Tutorial
This tutorial includes two examples. The first example shows how to declare and use read/write properties. The second example demonstrates abstract properties and shows how to override these properties in subclasses. Example 1 This sample shows a Person class that has two properties: Name (string) and Age (int). Both properties are read/write.

// person.cs using System; class Person { private string myName ="N/A"; private int myAge = 0; // Declare a Name property of type string: public string Name { get { return myName; } set { myName = value; } } // Declare an Age property of type int: public int Age { get { return myAge; } set { myAge = value; } } public override string ToString() { return "Name = " + Name + ", Age = " + Age; } public static void Main() { Console.WriteLine("Simple Properties");

// Create a new Person object: Person person = new Person(); // Print out the name and the age associated with the person: Console.WriteLine("Person details - {0}", person); // Set some values on the person object: person.Name = "Joe"; person.Age = 99; Console.WriteLine("Person details - {0}", person); // Increment the Age property: person.Age += 1; Console.WriteLine("Person details - {0}", person); } }
Output

Simple Person Person Person

Properties details - Name = N/A, Age = 0 details - Name = Joe, Age = 99 details - Name = Joe, Age = 100

Code Discussion y y y y y y y y y y y y Notice the way that the properties are declared, for example, consider the Name property: public string Name { get { return myName; } set { myName = value; } } The Set and Get methods of a property are contained inside the property declaration. You can control whether a property is read/write, read-only, or write-only by controlling whether a Get or Set method is included. y Once the properties are declared, they can be used as if they were fields of the class. This allows for a very natural syntax when both getting and setting the value of a property, as in the following statements: person.Name = "Joe"; person.Age = 99;

y y

y y y y

Note that in a property Set method a special value variable is available. This variable contains the value that the user specified, for example: myName = value; Notice the clean syntax for incrementing the Age property on a Person object: person.Age += 1; If separate Set and Get methods were used to model properties, the equivalent code might look like this:

person.SetAge(person.GetAge() + 1);
y y y y y The ToString method is overridden in this example: public override string ToString() { return "Name = " + Name + ", Age = " + Age; } Notice that ToString is not explicitly used in the program. It is invoked by default by the WriteLine calls. Example 2 The following example shows how to define abstract properties. An abstract property declaration does not provide an implementation of the property accessors. The example demonstrates how to override these properties in subclasses. This sample consists of three files. In the Properties Sample, these files are compiled into a single compilation but in this tutorial, each file is compiled individually and its resulting assembly referenced by the next compilation: y y y abstractshape.cs: The Shape class that contains an abstract Area property. shapes.cs: The subclasses of the Shape class. shapetest.cs: A test program to display the areas of some Shape-derived objects.

To compile the example, use the command line:

csc abstractshape.cs shapes.cs shapetest.cs


This will create the executable file shapetest.exe. File 1 - abstractshape.cs This file declares the Shape class that contains the Area property of the type double

// abstractshape.cs // compile with: /target:library // csc /target:library abstractshape.cs using System;

public abstract class Shape { private string myId; public Shape(string s) { Id = s; // calling the set accessor of the Id property } public string Id { get { return myId; } set { myId = value; } } // Area is a read-only property - only a get accessor is needed: public abstract double Area { get; } public override string ToString() { return Id + " Area = " + string.Format("{0:F2}",Area); } }
Code Discussion y y y Modifiers on the property are placed on the property declaration itself, for example: public abstract double Area When declaring an abstract property (such as Area in this example), you simply indicate what property accessors are available, but do not implement them. In this example, only a Get accessor is available, so the property is read-only.

File 2 - shapes.cs The following code shows three subclasses of Shape and how they override the Area property to provide their own implementation.

// shapes.cs // compile with: /target:library /reference:abstractshape.dll public class Square : Shape {

private int mySide; public Square(int side, string id) : base(id) { mySide = side; } public override double Area { get { // Given the side, return the area of a square: return mySide * mySide; } } } public class Circle : Shape { private int myRadius; public Circle(int radius, string id) : base(id) { myRadius = radius; } public override double Area { get { // Given the radius, return the area of a circle: return myRadius * myRadius * System.Math.PI; } } } public class Rectangle : Shape { private int myWidth; private int myHeight; public Rectangle(int width, int height, string id) : base(id) { myWidth = width; myHeight = height; } public override double Area { get { // Given the width and height, return the area of a rectangle: return myWidth * myHeight; } }

}
File 3 - shapetest.cs The following code shows a test program that creates a number of Shape-derived objects and prints out their areas.

// shapetest.cs // compile with: /reference:abstractshape.dll;shapes.dll public class TestClass { public static void Main() { Shape[] shapes = { new Square(5, "Square #1"), new Circle(3, "Circle #1"), new Rectangle( 4, 5, "Rectangle #1") }; System.Console.WriteLine("Shapes Collection"); foreach(Shape s in shapes) { System.Console.WriteLine(s); } } }
Output

Shapes Collection Square #1 Area = 25.00 Circle #1 Area = 28.27 Rectangle #1 Area = 20.00

Libraries Tutorial
Visual Studio .NET 2003 This tutorial shows how to create and use libraries in C#.

Sample Files
See Libraries Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y Creating and Using C# DLLs C# Compiler Options

Tutorial
This tutorial demonstrates how to create a managed DLL file by using the necessary compiler options, and how to use the library by a client program. Example This example uses the following modules: y The DLL library (Functions.dll), which is built from the following source files: Factorial.cs: Calculates and returns the factorial of a number. DigitCounter.cs: Counts the number of digits in the passed string. y The client program (FunctionTest.exe), which uses the DLL, is compiled from the source file FunctionClient.cs. The program displays the factorial of the input arguments.

Building the Library To build the library, make Functions the current directory and type the following at the command prompt:

csc /target:library /out:Functions.dll Factorial.cs DigitCounter.cs


where:

/out:Functions.dll

Factorial.cs DigitCounter.cs Compiling the Client

To compile the program, make FunctionTest the current directory and type the following at the command prompt:

copy ..\Functions\Functions.dll . csc /out:FunctionTest.exe /R:Functions.DLL FunctionClient.cs

target:library

Specifies that the output is a DLL and not an executable

Specifies that the output file name is Functions.dll. No line (in this example, Factorial). Specifies the files to compile and place in the DLL.

where: /out:FunctionTest.exe /R:Functions.DLL

Specifies that the output file name is FunctionTest.

Specifies that Functions.DLL must be included whe fully qualified path. Specifies the client source code.

FunctionClient.cs This creates the executable file FunctionTest.exe. File 1 - Factorial.cs

The following code calculates the factorial of the integer passed to the method (unlike Libraries Sample, compile this to a library).

// Factorial.cs // compile with: /target:library using System; // Declares a namespace. You need to package your libraries according // to their namespace so the .NET runtime can correctly load the classes. namespace Functions { public class Factorial { // The "Calc" static method calculates the factorial value for the // specified integer passed in: public static int Calc(int i) { return((i <= 1) ? 1 : (i * Calc(i-1))); } } }
File 2 - DigitCounter.cs The following code is used to count the number of digit characters in the passed string:

// DigitCounter.cs // compile with: /target:library /out:Functions.dll /reference:Factorial.dll using System; // Declare the same namespace as the one in Factorial.cs. This simply // allows types to be added to the same namespace. namespace Functions { public class DigitCount { // The NumberOfDigits static method calculates the number of // digit characters in the passed string:

public static int NumberOfDigits(string theString) { int count = 0; for ( int i = 0; i < theString.Length; i++ ) { if ( Char.IsDigit(theString[i]) ) { count++; } } return count; } } }
File 3 - FunctionClient.cs Once you build a library, it can be used by other programs. The following client program uses the classes defined in the library. The basic function of the program is to take each command-line parameter and attempt to compute the factorial value for each argument.

// FunctionClient.cs // compile with: /reference:Functions.dll,Factorial.dll /out:FunctionTest.exe // arguments: 3 5 10 using System; // The following using directive makes the types defined in the Functions // namespace available in this compilation unit: using Functions; class FunctionClient { public static void Main(string[] args) { Console.WriteLine("Function Client"); if ( args.Length == 0 ) { Console.WriteLine("Usage: FunctionTest ... "); return; } for ( int i = 0; i < args.Length; i++ ) { int num = Int32.Parse(args[i]); Console.WriteLine( "The Digit Count for String [{0}] is [{1}]", args[i], // Invoke the NumberOfDigits static method in the // DigitCount class: DigitCount.NumberOfDigits(args[i])); Console.WriteLine( "The Factorial for [{0}] is [{1}]", num, // Invoke the Calc static method in the Factorial class: Factorial.Calc(num) );

} } }
Output The command line FunctionTest 3 5 10 uses the program FunctionTest to calculate the factorial of the three integers 3, 5, and 10. It also displays the number of digits for each argument. This run gives the output:

Function Client The Digit Count for String [3] is [1] The Factorial for [3] is [6] The Digit Count for String [5] is [1] The Factorial for [5] is [120] The Digit Count for String [10] is [2] The Factorial for [10] is [3628800]
Note To run the client executable (FunctionTest.exe), the file Functions.DLL must be in the current directory, a child directory, or in the Global Assembly Cache. For more information see Global Assembly Cache.

Versioning Tutorial
Visual Studio .NET 2003 This tutorial demonstrates versioning in C# through the use of the override and new keywords. Versioning maintains compatibility between base and derived classes as they evolve.

Sample Files
See Versioning Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y virtual override new 10.5.3 Virtual methods

Tutorial

The C# language is designed such that versioning between base and derived classes in different libraries can evolve and maintain backwards compatibility. This means, for example, that the introduction of a new member in a base class with the same name as a member in a derived class is not an error. It also means that a class must explicitly state whether a method is intended to override an inherited method, or whether a method is a new method that simply hides a similarly named inherited method. In C#, methods are by default, not virtual. To make a method virtual, the virtual modifier has to be used in the method declaration of the base class. The derived class can then override the base virtual method by using the override keyword or hide the virtual method in the base class by using the new keyword. If neither the override keyword nor the new keyword is specified, the compiler will issue a warning and the method in the derived class will hide the method in the base class. The following example shows these concepts in action. Example

// versioning.cs // CS0114 expected public class MyBase { public virtual string Meth1() { return "MyBase-Meth1"; } public virtual string Meth2() { return "MyBase-Meth2"; } public virtual string Meth3() { return "MyBase-Meth3"; } } class MyDerived : MyBase { // Overrides the virtual method Meth1 using the override keyword: public override string Meth1() { return "MyDerived-Meth1"; } // Explicitly hide the virtual method Meth2 using the new // keyword: public new string Meth2() { return "MyDerived-Meth2"; } // Because no keyword is specified in the following declaration // a warning will be issued to alert the programmer that // the method hides the inherited member MyBase.Meth3(): public string Meth3() { return "MyDerived-Meth3"; }

public static void Main() { MyDerived mD = new MyDerived(); MyBase mB = (MyBase) mD; System.Console.WriteLine(mB.Meth1()); System.Console.WriteLine(mB.Meth2()); System.Console.WriteLine(mB.Meth3()); } }
Output

MyDerived-Meth1 MyBase-Meth2 MyBase-Meth3


Code Discussion Hiding a base class member from a derived class isn't an error in C#. This feature enables you to make changes in the base class without breaking other libraries that inherit this base class. For example, at some point you could have the following classes:

class Base {} class Derived: Base { public void F() {} }


At some later point, the base class could evolve to add a void method F() as follows:

class Base { public void F() {} } class Derived: Base { public void F() {} }
Thus, in C#, both the base and derived classes can evolve freely and maintain binary compatibility.

Collection Classes Tutorial


Visual Studio .NET 2003

This tutorial shows how to implement a collection class that can be used with the foreach statement.

Sample Files
See Collection Classes Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y foreach, in 8.8.4 The foreach statement Command Line Parameters Tutorial Arrays Tutorial

Tutorial
The foreach statement is a convenient way to iterate over the elements of an array. It can also enumerate the elements of a collection, provided that the collection class has implemented theSystem.Collections.IEnumerator and System.Collections.IEnumerable interfaces. Example 1 The following code sample illustrates how to write a collection class that can be used with foreach. The class is a string tokenizer, similar to the C run-time function strtok.

// tokens.cs using System; // The System.Collections namespace is made available: using System.Collections; // Declare the Tokens class: public class Tokens : IEnumerable { private string[] elements; Tokens(string source, char[] delimiters) { // Parse the string into tokens: elements = source.Split(delimiters); } // IEnumerable Interface Implementation: // Declaration of the GetEnumerator() method // required by IEnumerable public IEnumerator GetEnumerator() { return new TokenEnumerator(this); } // Inner class implements IEnumerator interface:

private class TokenEnumerator : IEnumerator { private int position = -1; private Tokens t; public TokenEnumerator(Tokens t) { this.t = t; } // Declare the MoveNext method required by IEnumerator: public bool MoveNext() { if (position < t.elements.Length - 1) { position++; return true; } else { return false; } } // Declare the Reset method required by IEnumerator: public void Reset() { position = -1; } // Declare the Current property required by IEnumerator: public object Current { get { return t.elements[position]; } } } // Test Tokens, TokenEnumerator static void Main() { // Testing Tokens by breaking the string into tokens: Tokens f = new Tokens("This is a well-done program.", new char[] {' ','-'}); foreach (string item in f) { Console.WriteLine(item); } } }
Output

This is a well done program.


Code Discussion In the preceding example, the following code is used to Tokens by breaking "This is a well-done program." into tokens (using ' ' and '-' as separators) and enumerating those tokens with the foreach statement:

Tokens f = new Tokens("This is a well-done program.", new char[] {' ','-'}); foreach (string item in f) { Console.WriteLine(item); }
Notice that, internally, Tokens uses an array, which implements IEnumerator and IEnumerable itself. The code sample could have leveraged the array's enumeration methods as its own, but that would have defeated the purpose of this example. In C#, it is not strictly necessary for a collection class to inherit from IEnumerable and IEnumerator in order to be compatible with foreach; as long as the class has the required GetEnumerator, MoveNext, Reset, andCurrent members, it will work with foreach. Omitting the interfaces has the advantage of allowing you to define the return type of Current to be more specific than object, thereby providing type-safety. For example, starting with the sample code above, change the following lines:

public public public public

class Tokens // no longer inherits from IEnumerable TokenEnumerator GetEnumerator() // doesn't return an IEnumerator class TokenEnumerator // no longer inherits from IEnumerator string Current // type-safe: returns string, not object

Now, because Current returns a string, the compiler can detect when an incompatible type is used in a foreach statement:

foreach (int item in f)

// Error: cannot convert string to int

The disadvantage of omitting IEnumerable and IEnumerator is that the collection class is no longer interoperable with the foreach statements (or equivalents) of other common language runtimecompatible languages. You can have the best of both worlds type-safety within C# and interoperability with other common language runtime-compatible languages by inheriting from IEnumerable and IEnumerator and using explicit interface implementation, as demonstrated in the following example.

Example 2 This sample is equivalent in function to Example 1, but it provides additional type-safety in C# while maintaining interoperability with other languages.

// tokens2.cs using System; using System.Collections; public class Tokens: IEnumerable { private string[] elements; Tokens(string source, char[] delimiters) { elements = source.Split(delimiters); } // IEnumerable Interface Implementation: public TokenEnumerator GetEnumerator() // non-IEnumerable version { return new TokenEnumerator(this); } IEnumerator IEnumerable.GetEnumerator() // IEnumerable version { return (IEnumerator) new TokenEnumerator(this); } // Inner class implements IEnumerator interface: public class TokenEnumerator: IEnumerator { private int position = -1; private Tokens t; public TokenEnumerator(Tokens t) { this.t = t; } public bool MoveNext() { if (position < t.elements.Length - 1) { position++; return true; } else { return false; } }

public void Reset() { position = -1; } public string Current // non-IEnumerator version: type-safe { get { return t.elements[position]; } } object IEnumerator.Current // IEnumerator version: returns object { get { return t.elements[position]; } } } // Test Tokens, TokenEnumerator static void Main() { Tokens f = new Tokens("This is a well-done program.", new char [] {' ','-'}); foreach (string item in f) // try changing string to int { Console.WriteLine(item); } } }

Structs Tutorial
Visual Studio .NET 2003 This tutorial presents the syntax and usage of structs. It also covers the important differences between classes and structs.

Sample Files
See Structs Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y y y struct 1.8 Structs Structs Grammar in C. Grammar Value Types C# Attributes 17. Attributes StructLayoutAttribute Class

Tutorial
This tutorial includes two examples. The first example shows you how to declare and use structs, and the second example demonstrates the difference between structs and classes when instances are passed to methods. You are also introduced to the following topics: y y y y Structs vs. Classes Heap or Stack? Constructors and Inheritance Attributes on Structs

Example 1 This example declares a struct with three members: a property, a method, and a private field. It creates an instance of the struct and puts it to use:

// struct1.cs using System; struct SimpleStruct { private int xval; public int X { get { return xval; } set { if (value < 100) xval = value; } } public void DisplayX() { Console.WriteLine("The stored value is: {0}", xval); } } class TestClass

{ public static void Main() { SimpleStruct ss = new SimpleStruct(); ss.X = 5; ss.DisplayX(); } }


Output

The stored value is: 5

Structs vs. Classes


Structs may seem similar to classes, but there are important differences that you should be aware of. First of all, classes are reference types and structs are value types. By using structs, you can create objects that behave like the built-in types and enjoy their benefits as well.

Heap or Stack?
When you call the New operator on a class, it will be allocated on the heap. However, when you instantiate a struct, it gets created on the stack. This will yield performance gains. Also, you will not be dealing with references to an instance of a struct as you would with classes. You will be working directly with the struct instance. Because of this, when passing a struct to a method, it's passed by value instead of as a reference. Example 2 This example shows that when a struct is passed to a method, a copy of the struct is passed, but when a class instance is passed, a reference is passed.

// struct2.cs using System; class TheClass { public int x; } struct TheStruct { public int x; } class TestClass { public static void structtaker(TheStruct s) { s.x = 5; }

public static void classtaker(TheClass c) { c.x = 5; } public static void Main() { TheStruct a = new TheStruct(); TheClass b = new TheClass(); a.x = 1; b.x = 1; structtaker(a); classtaker(b); Console.WriteLine("a.x = {0}", a.x); Console.WriteLine("b.x = {0}", b.x); } }
Output

a.x = 1 b.x = 5
Code Discussion The output of the example shows that only the value of the class field was changed when the class instance was passed to the classtaker method. The struct field, however, did not change by passing its instance to thestructtaker method. This is because a copy of the struct was passed to the structtaker method, while a reference to the class was passed to the classtaker method.

Constructors and Inheritance


Structs can declare constructors, but they must take parameters. It is an error to declare a default (parameterless) constructor for a struct. Struct members cannot have initializers. A default constructor is always provided to initialize the struct members to their default values. When you create a struct object using the New operator, it gets created and the appropriate constructor is called. Unlike classes, structs can be instantiated without using the New operator. If you do not use New, the fields will remain unassigned and the object cannot be used until all the fields are initialized. There is no inheritance for structs as there is for classes. A struct cannot inherit from another struct or class, and it cannot be the base of a class. Structs, however, inherit from the base class object. A struct can implement interfaces, and it does that exactly as classes do. Here's a code snippet of a struct implementing an interface:

interface IImage { void Paint(); } struct Picture : IImage

{ public void Paint() { // painting code goes here } private int x, y, z; // other struct members }

Attributes on Structs
By using attributes you can customize how structs are laid out in memory. For example, you can create what's known as a union in C/C++ by using the StructLayout(LayoutKind.Explicit) and FieldOffset attributes.

using System.Runtime.InteropServices; [StructLayout(LayoutKind.Explicit)] struct TestUnion { [FieldOffset(0)] public int i; [FieldOffset(0)] public double d; [FieldOffset(0)] public char c; [FieldOffset(0)] public byte b1; }
In the preceding code segment, all of the fields of TestUnion start at the same location in memory. The following is another example where fields start at different explicitly set locations:

using System.Runtime.InteropServices; [StructLayout(LayoutKind.Explicit)] struct TestExplicit { [FieldOffset(0)] public long lg; [FieldOffset(0)] public int i1; [FieldOffset(4)] public int i2; [FieldOffset(8)] public double d; [FieldOffset(12)] public char c; [FieldOffset(14)] public byte b1; }
The two int fields, i1 and i2, share the same memory locations as lg. This sort of control over struct layout is useful when using platform invocation.

Conclusion
Structs are simple to use and can prove to be useful at times. Just keep in mind that they're created on the stack and that you're not dealing with references to them but dealing directly with them. Whenever you have a need for a type that will be used often and is mostly just a piece of data, structs might be a good option.

Indexers Tutorial
Visual Studio .NET 2003 This tutorial shows how C# classes can declare indexers to provide array-like access to the classes.

Sample Files
See Indexers Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y Indexers Accessors Comparison Between Properties and Indexers 10.8 Indexers 10.8.1 Indexer overloading

Tutorial
Defining an indexer allows you to create classes that act like "virtual arrays." Instances of that class can be accessed using the [] array access operator. Defining an indexer in C# is similar to defining operator [] in C++, but is considerably more flexible. For classes that encapsulate array- or collection-like functionality, using an indexer allows the users of that class to use the array syntax to access the class. For example, suppose you want to define a class that makes a file appear as an array of bytes. If the file were very large, it would be impractical to read the entire file into memory, especially if you only wanted to read or change a few bytes. By defining a FileByteArray class, you could make the file appear similar to an array of bytes, but actually do file input and output when a byte was read or written. In addition to the example below, an advanced topic on Creating an Indexed Property is discussed in this tutorial. Example

In this example, the class FileByteArray makes it possible to access a file as if it were a byte array. The Reverse class reverses the bytes of the file. You can run this program to reverse the bytes of any text file including the program source file itself. To change the reversed file back to normal, run the program on the same file again.

// indexer.cs // arguments: indexer.txt using System; using System.IO; // Class to provide access to a large file // as if it were a byte array. public class FileByteArray { Stream stream; // Holds the underlying stream // used to access the file. // Create a new FileByteArray encapsulating a particular file. public FileByteArray(string fileName) { stream = new FileStream(fileName, FileMode.Open); } // Close the stream. This should be the last thing done // when you are finished. public void Close() { stream.Close(); stream = null; } // Indexer to provide read/write access to the file. public byte this[long index] // long is a 64-bit integer { // Read one byte at offset index and return it. get { byte[] buffer = new byte[1]; stream.Seek(index, SeekOrigin.Begin); stream.Read(buffer, 0, 1); return buffer[0]; } // Write one byte at offset index and return it. set { byte[] buffer = new byte[1] {value}; stream.Seek(index, SeekOrigin.Begin); stream.Write(buffer, 0, 1); } } // Get the total length of the file. public long Length { get

{ return stream.Seek(0, SeekOrigin.End); } } } // Demonstrate the FileByteArray class. // Reverses the bytes in a file. public class Reverse { public static void Main(String[] args) { // Check for arguments. if (args.Length == 0) { Console.WriteLine("indexer <filename>"); return; } FileByteArray file = new FileByteArray(args[0]); long len = file.Length; // Swap bytes in the file to reverse it. for (long i = 0; i < len / 2; ++i) { byte t; // Note that indexing the "file" variable invokes the // indexer on the FileByteStream class, which reads // and writes the bytes in the file. t = file[i]; file[i] = file[len - i - 1]; file[len - i - 1] = t; } file.Close(); } }
Input: indexer.txt To test the program you can use a text file with the following contents (this file is called Test.txt in the Indexers Sample).

public class Hello1 { public static void Main() { System.Console.WriteLine("Hello, World!"); } }


To reverse the bytes of this file, compile the program and then use the command line:

indexer indexer.txt
To display the reversed file, enter the command:

Type indexer.txt
Sample Output

} } ;)"!dlroW ,olleH"(eniLetirW.elosnoC.metsyS { )(niaM diov citats cilbup { 1olleH ssalc cilbup


Code Discussion y y Since an indexer is accessed using the [] operator, it does not have a name. For indexer declaration syntax, see Indexers. In the example above, the indexer is of type byte and takes a single index of type long (64-bit integer). The Get accessor defines the code to read a byte from the file, while the Set accessor defines the code to write a byte to the file. Inside the Set accessor, the predefined parameter value has the value that is being assigned to the virtual array element. An indexer must have at least one parameter. Although it is comparatively rare, an indexer can have more than one parameter in order to simulate a multidimensional "virtual array." Although integral parameters are the most common, the indexer parameter can be of any type. For example, the standard Dictionary class provides an indexer with a parameter of type Object. Although indexers are a powerful feature, it is important to use them only when the array-like abstraction makes sense. Always carefully consider whether using regular method(s) would be just as clear. For example, the following is a bad use of an indexer: class Employee { // VERY BAD STYLE: using an indexer to access // the salary of an employee. public double this[int year] { get { // return employee's salary for a given year. } } } Although legal, an indexer with only a Get accessor is rarely good style. Strongly consider using a method in this case. y Indexers can be overloaded (for more information, see 10.8.1 Indexer overloading).

y y y y y y y y y y y y

Indexed Properties Tutorial


Visual Studio .NET 2003 This tutorial shows how to implement a class that uses indexed properties. Indexed properties allow you to use a class that represents an array-like collection of several different kinds of things. You should complete theIndexers Tutorial before working through this tutorial.

Sample Files
See Indexed Properties Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y Indexers Accessors Indexers Tutorial 10.8 Indexers 10.8.1 Indexer overloading

Tutorial
Suppose you want to write a class, Document, which encapsulates a lengthy section of text. To allow easy implementation of various operations such as checking spelling, you might want to view the document as a virtual array of words, as well as of characters. The following example shows a technique for implementing such a class. For each "indexed property," you define a nested class, which contains a reference back to the main class instance. A readonly field on the main class provides access to an instance of the nested class that defines each virtual array. Each of the nested classes defines an indexer, as well as other collection-like methods (a Count property, for example). The following example shows this for "Words" and "Characters." Note Use this technique sparingly! Only use this pattern if the abstraction provided by using array indexing operations significantly clarifies code that uses your class, and if the indexers have both Get and Set accessors. Example In this example the Document class is defined. Two indexed properties, Words and Characters, are used to perform some text operations on the Document object.

// indexedproperty.cs using System; public class Document {

// Type allowing the document to be viewed like an array of words: public class WordCollection { readonly Document document; // The containing document internal WordCollection(Document d) { document = d; } // Helper function -- search character array "text", starting at // character "begin", for word number "wordCount." Returns false // if there are less than wordCount words. Sets "start" and // length" to the position and length of the word within text: private bool GetWord(char[] text, int begin, int wordCount, out int start, out int length) { int end = text.Length; int count = 0; int inWord = -1; start = length = 0; for (int i = begin; i <= end; ++i) { bool isLetter = i < end && Char.IsLetterOrDigit(text[i]); if (inWord >= 0) { if (!isLetter) { if (count++ == wordCount) { start = inWord; length = i - inWord; return true; } inWord = -1; } } else { if (isLetter) inWord = i; } } return false; } // Indexer to get and set words of the containing document: public string this[int index] { get { int start, length; if (GetWord(document.TextArray, 0, index, out start,

out length)) return new string(document.TextArray, start, length); else throw new IndexOutOfRangeException(); } set { int start, length; if (GetWord(document.TextArray, 0, index, out start, out length)) { // Replace the word at start/length with the // string "value": if (length == value.Length) { Array.Copy(value.ToCharArray(), 0, document.TextArray, start, length); } else { char[] newText = new char[document.TextArray.Length + value.Length - length]; Array.Copy(document.TextArray, 0, newText, 0, start); Array.Copy(value.ToCharArray(), 0, newText, start, value.Length); Array.Copy(document.TextArray, start + length, newText, start + value.Length, document.TextArray.Length - start - length); document.TextArray = newText; } } else throw new IndexOutOfRangeException(); } } // Get the count of words in the containing document: public int Count { get { int count = 0, start = 0, length = 0; while (GetWord(document.TextArray, start + length, 0, out start, out length)) ++count; return count; } } } // Type allowing the document to be viewed like an "array" // of characters:

public class CharacterCollection { readonly Document document; // The containing document internal CharacterCollection(Document d) { document = d; } // Indexer to get and set characters in the containing document: public char this[int index] { get { return document.TextArray[index]; } set { document.TextArray[index] = value; } } // Get the count of characters in the containing document: public int Count { get { return document.TextArray.Length; } } } // Because the types of the fields have indexers, // these fields appear as "indexed properties": public readonly WordCollection Words; public readonly CharacterCollection Characters; private char[] TextArray; // The text of the document.

public Document(string initialText) { TextArray = initialText.ToCharArray(); Words = new WordCollection(this); Characters = new CharacterCollection(this); } public string Text { get { return new string(TextArray); } } }

class Test { static void Main() { Document d = new Document( "peter piper picked a peck of pickled peppers. How many pickled peppers did peter piper pick?" ); // Change word "peter" to "penelope": for (int i = 0; i < d.Words.Count; ++i) { if (d.Words[i] == "peter") d.Words[i] = "penelope"; } // Change character "p" to "P" for (int i = 0; i < d.Characters.Count; ++i) { if (d.Characters[i] == 'p') d.Characters[i] = 'P'; } Console.WriteLine(d.Text); } }
Output

PeneloPe PiPer Picked a Peck of Pickled PePPers. How many Pickled PePPers did PeneloPe PiPer Pick?

User-Defined Conversions Tutorial


Visual Studio .NET 2003 This tutorial shows how to define and use conversions to or from classes or structs.

Sample Files
See User-Defined Conversions Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y explicit implicit () Operator Implicit Numeric Conversions Table

y y y

operator 6.4 User-defined conversions 6.4.4 User-defined explicit conversions

Tutorial
C# allows programmers to declare conversions on classes or structs so that classes or structs can be converted to and/or from other classes or structs, or basic types. Conversions are defined like operators and are named for the type to which they convert. In C#, conversions can be declared either as implicit, which occur automatically when required, or explicit, which require a cast to be called. All conversions must be static, and must either take the type the conversion is defined on, or return that type. This tutorial introduces two examples. The first example shows how to declare and use conversions, and the second example demonstrates conversions between structs. Example 1 In this example, a RomanNumeral type is declared, and several conversions to and from it are defined.

// conversion.cs using System; struct RomanNumeral { public RomanNumeral(int value) { this.value = value; } // Declare a conversion from an int to a RomanNumeral. Note the // the use of the operator keyword. This is a conversion // operator named RomanNumeral: static public implicit operator RomanNumeral(int value) { // Note that because RomanNumeral is declared as a struct, // calling new on the struct merely calls the constructor // rather than allocating an object on the heap: return new RomanNumeral(value); } // Declare an explicit conversion from a RomanNumeral to an int: static public explicit operator int(RomanNumeral roman) { return roman.value; } // Declare an implicit conversion from a RomanNumeral to // a string: static public implicit operator string(RomanNumeral roman) { return("Conversion not yet implemented"); }

private int value; } class Test { static public void Main() { RomanNumeral numeral; numeral = 10; // Call the explicit conversion from numeral to int. Because it is // an explicit conversion, a cast must be used: Console.WriteLine((int)numeral); // Call the implicit conversion to string. Because there is no // cast, the implicit conversion to string is the only // conversion that is considered: Console.WriteLine(numeral); // Call the explicit conversion from numeral to int and // then the explicit conversion from int to short: short s = (short)numeral; Console.WriteLine(s); } }
Output

10 Conversion not yet implemented 10


Example 2 This example defines two structs, RomanNumeral and BinaryNumeral, and demonstrates conversions between them.

// structconversion.cs using System; struct RomanNumeral { public RomanNumeral(int value) { this.value = value; } static public implicit operator RomanNumeral(int value) { return new RomanNumeral(value); } static public implicit operator RomanNumeral(BinaryNumeral binary)

{ return new RomanNumeral((int)binary); } static public explicit operator int(RomanNumeral roman) { return roman.value; } static public implicit operator string(RomanNumeral roman) { return("Conversion not yet implemented"); } private int value; } struct BinaryNumeral { public BinaryNumeral(int value) { this.value = value; } static public implicit operator BinaryNumeral(int value) { return new BinaryNumeral(value); } static public implicit operator string(BinaryNumeral binary) { return("Conversion not yet implemented"); } static public explicit operator int(BinaryNumeral binary) { return(binary.value); } private int value; } class Test { static public void Main() { RomanNumeral roman; roman = 10; BinaryNumeral binary; // Perform a conversion from a RomanNumeral to a // BinaryNumeral: binary = (BinaryNumeral)(int)roman; // Performs a conversion from a BinaryNumeral to a RomanNumeral. // No cast is required: roman = binary; Console.WriteLine((int)binary); Console.WriteLine(binary); } }
Output

10 Conversion not yet implemented


Code Discussion y y In the preceding example, the statement: binary = (BinaryNumeral)(int)roman; performs a conversion from a RomanNumeral to a BinaryNumeral. Because there is no direct conversion from RomanNumeral to BinaryNumeral, a cast is used to convert from a RomanNumeral to an int, and another cast to convert from an int to a BinaryNumeral. y y Also the statement: roman = binary; performs a conversion from a BinaryNumeral to a RomanNumeral. Because RomanNumeral defines an implicit conversion from BinaryNumeral, no cast is required.

Operator Overloading Tutorial


Visual Studio .NET 2003 This tutorial demonstrates how user-defined classes can overload operators.

Sample Files
See Operator Overloading Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y y 7.2.2 Operator overloading C# Operators Overloadable Operators true false bool

Tutorial
Operator overloading permits user-defined operator implementations to be specified for operations where one or both of the operands are of a user-defined class or struct type. The tutorial contains two

examples. The first example shows how to use operator overloading to create a complex number class that defines complex addition. The second example shows how to use operator overloading to implement a three-valued logical type. Example 1 This example shows how you can use operator overloading to create a complex number class Complex that defines complex addition. The program displays the imaginary and the real parts of the numbers and the addition result using an override of the ToString method.

// complex.cs using System; public struct Complex { public int real; public int imaginary; public Complex(int real, int imaginary) { this.real = real; this.imaginary = imaginary; } // Declare which operator to overload (+), the types // that can be added (two Complex objects), and the // return type (Complex): public static Complex operator +(Complex c1, Complex c2) { return new Complex(c1.real + c2.real, c1.imaginary + c2.imaginary); } // Override the ToString method to display an complex number in the suitable format: public override string ToString() { return(String.Format("{0} + {1}i", real, imaginary)); } public static void Main() { Complex num1 = new Complex(2,3); Complex num2 = new Complex(3,4); // Add two Complex objects (num1 and num2) through the // overloaded plus operator: Complex sum = num1 + num2; // Print the numbers and the sum using the overriden ToString method: Console.WriteLine("First complex number: {0}",num1); Console.WriteLine("Second complex number: {0}",num2); Console.WriteLine("The sum of the two numbers: {0}",sum); }

}
Output

First complex number: 2 + 3i Second complex number: 3 + 4i The sum of the two numbers: 5 + 7i
Example 2 This example shows how operator overloading can be used to implement a three-valued logical type. The possible values of this type are DBBool.dbTrue, DBBool.dbFalse, and DBBool.dbNull, where the dbNullmember indicates an unknown value. Note Defining the True and False operators is only useful for types that represent True, False, and Null (neither True nor False), as used in databases.

// dbbool.cs using System; public struct DBBool { // The three possible DBBool values: public static readonly DBBool dbNull = new DBBool(0); public static readonly DBBool dbFalse = new DBBool(-1); public static readonly DBBool dbTrue = new DBBool(1); // Private field that stores -1, 0, 1 for dbFalse, dbNull, dbTrue: int value; // Private constructor. The value parameter must be -1, 0, or 1: DBBool(int value) { this.value = value; } // Implicit conversion from bool to DBBool. Maps true to // DBBool.dbTrue and false to DBBool.dbFalse: public static implicit operator DBBool(bool x) { return x? dbTrue: dbFalse; } // Explicit conversion from DBBool to bool. Throws an // exception if the given DBBool is dbNull, otherwise returns // true or false: public static explicit operator bool(DBBool x) { if (x.value == 0) throw new InvalidOperationException(); return x.value > 0; } // Equality operator. Returns dbNull if either operand is dbNull, // otherwise returns dbTrue or dbFalse:

public static DBBool operator ==(DBBool x, DBBool y) { if (x.value == 0 || y.value == 0) return dbNull; return x.value == y.value? dbTrue: dbFalse; } // Inequality operator. Returns dbNull if either operand is // dbNull, otherwise returns dbTrue or dbFalse: public static DBBool operator !=(DBBool x, DBBool y) { if (x.value == 0 || y.value == 0) return dbNull; return x.value != y.value? dbTrue: dbFalse; } // Logical negation operator. Returns dbTrue if the operand is // dbFalse, dbNull if the operand is dbNull, or dbFalse if the // operand is dbTrue: public static DBBool operator !(DBBool x) { return new DBBool(-x.value); } // Logical AND operator. Returns dbFalse if either operand is // dbFalse, dbNull if either operand is dbNull, otherwise dbTrue: public static DBBool operator &(DBBool x, DBBool y) { return new DBBool(x.value < y.value? x.value: y.value); } // Logical OR operator. Returns dbTrue if either operand is // dbTrue, dbNull if either operand is dbNull, otherwise dbFalse: public static DBBool operator |(DBBool x, DBBool y) { return new DBBool(x.value > y.value? x.value: y.value); } // Definitely true operator. Returns true if the operand is // dbTrue, false otherwise: public static bool operator true(DBBool x) { return x.value > 0; } // Definitely false operator. Returns true if the operand is // dbFalse, false otherwise: public static bool operator false(DBBool x) { return x.value < 0; } // Overload the conversion from DBBool to string: public static implicit operator string(DBBool x) { return x.value > 0 ? "dbTrue" : x.value < 0 ? "dbFalse"

: "dbNull"; } // Override the Object.Equals(object o) method: public override bool Equals(object o) { try { return (bool) (this == (DBBool) o); } catch { return false; } } // Override the Object.GetHashCode() method: public override int GetHashCode() { return value; } // Override the ToString method to convert DBBool to a string: public override string ToString() { switch (value) { case -1: return "DBBool.False"; case 0: return "DBBool.Null"; case 1: return "DBBool.True"; default: throw new InvalidOperationException(); } } } class Test { static void Main() { DBBool a, b; a = DBBool.dbTrue; b = DBBool.dbNull; Console.WriteLine( "!{0} = {1}", a, !a); Console.WriteLine( "!{0} = {1}", b, !b); Console.WriteLine( "{0} & {1} = {2}", a, b, a & b); Console.WriteLine( "{0} | {1} = {2}", a, b, a | b); // Invoke the true operator to determine the Boolean // value of the DBBool variable: if (b) Console.WriteLine("b is definitely true");

else Console.WriteLine("b is not definitely true"); } }


Output

!DBBool.True = DBBool.False !DBBool.Null = DBBool.Null DBBool.True & DBBool.Null = DBBool.Null DBBool.True | DBBool.Null = DBBool.True b is not definitely true

Delegates Tutorial
Visual Studio .NET 2003 This tutorial demonstrates the delegate types. It shows how to map delegates to static and instance methods, and how to combine them (multicast).

Sample Files
See Delegates Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y delegate 15. Delegates Events Tutorial Asynchronous Delegates

Tutorial
A delegate in C# is similar to a function pointer in C or C++. Using a delegate allows the programmer to encapsulate a reference to a method inside a delegate object. The delegate object can then be passed to code which can call the referenced method, without having to know at compile time which method will be invoked. Unlike function pointers in C or C++, delegates are object-oriented, type-safe, and secure. A delegate declaration defines a type that encapsulates a method with a particular set of arguments and return type. For static methods, a delegate object encapsulates the method to be called. For instance methods, a delegate object encapsulates both an instance and a method on the instance. If you have a delegate object and an appropriate set of arguments, you can invoke the delegate with the arguments.

An interesting and useful property of a delegate is that it does not know or care about the class of the object that it references. Any object will do; all that matters is that the method's argument types and return type match the delegate's. This makes delegates perfectly suited for "anonymous" invocation. Note Delegates run under the caller's security permissions, not the declarer's permissions. This tutorial includes two examples: y y Example 1 shows how to declare, instantiate, and call a delegate. Example 2 shows how to combine two delegates.

In addition, it discusses the following topics: y y Delegates and Events Delegates vs. Interfaces

Example 1 The following example illustrates declaring, instantiating, and using a delegate. The BookDB class encapsulates a bookstore database that maintains a database of books. It exposes a method ProcessPaperbackBooks, which finds all paperback books in the database and calls a delegate for each one. The delegate type used is called ProcessBookDelegate. The Test class uses this class to print out the titles and average price of the paperback books. The use of delegates promotes good separation of functionality between the bookstore database and the client code. The client code has no knowledge of how the books are stored or how the bookstore code finds paperback books. The bookstore code has no knowledge of what processing is done on the paperback books after it finds them.

// bookstore.cs using System; // A set of classes for handling a bookstore: namespace Bookstore { using System.Collections; // Describes a book in the book list: public struct Book { public string Title; // Title of the book. public string Author; // Author of the book. public decimal Price; // Price of the book. public bool Paperback; // Is it paperback? public Book(string title, string author, decimal price, bool paperBack) { Title = title; Author = author; Price = price;

Paperback = paperBack; } } // Declare a delegate type for processing a book: public delegate void ProcessBookDelegate(Book book); // Maintains a book database. public class BookDB { // List of all books in the database: ArrayList list = new ArrayList(); // Add a book to the database: public void AddBook(string title, string author, decimal price, bool paperBack) { list.Add(new Book(title, author, price, paperBack)); } // Call a passed-in delegate on each paperback book to process it: public void ProcessPaperbackBooks(ProcessBookDelegate processBook) { foreach (Book b in list) { if (b.Paperback) // Calling the delegate: processBook(b); } } } } // Using the Bookstore classes: namespace BookTestClient { using Bookstore; // Class to total and average prices of books: class PriceTotaller { int countBooks = 0; decimal priceBooks = 0.0m; internal void AddBookToTotal(Book book) { countBooks += 1; priceBooks += book.Price; } internal decimal AveragePrice() { return priceBooks / countBooks; } }

// Class to test the book database: class Test { // Print the title of the book. static void PrintTitle(Book b) { Console.WriteLine(" {0}", b.Title); } // Execution starts here. static void Main() { BookDB bookDB = new BookDB(); // Initialize the database with some books: AddBooks(bookDB); // Print all the titles of paperbacks: Console.WriteLine("Paperback Book Titles:"); // Create a new delegate object associated with the static // method Test.PrintTitle: bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle)); // Get the average price of a paperback by using // a PriceTotaller object: PriceTotaller totaller = new PriceTotaller(); // Create a new delegate object associated with the nonstatic // method AddBookToTotal on the object totaller: bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal)); Console.WriteLine("Average Paperback Book Price: ${0:#.##}", totaller.AveragePrice()); } // Initialize the book database with some test books: static void AddBooks(BookDB bookDB) { bookDB.AddBook("The C Programming Language", "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true); bookDB.AddBook("The Unicode Standard 2.0", "The Unicode Consortium", 39.95m, true); bookDB.AddBook("The MS-DOS Encyclopedia", "Ray Duncan", 129.95m, false); bookDB.AddBook("Dogbert's Clues for the Clueless", "Scott Adams", 12.00m, true); } } }
Output

Paperback Book Titles: The C Programming Language The Unicode Standard 2.0

Dogbert's Clues for the Clueless Average Paperback Book Price: $23.97
Code Discussion y y Declaring a delegate The following statement: public delegate void ProcessBookDelegate(Book book); declares a new delegate type. Each delegate type describes the number and types of the arguments, and the type of the return value of methods that it can encapsulate. Whenever a new set of argument types or return value type is needed, a new delegate type must be declared. y Instantiating a delegate Once a delegate type has been declared, a delegate object must be created and associated with a particular method. Like all other objects, a new delegate object is created with a newexpression. When creating a delegate, however, the argument passed to the new expression is special it is written like a method call, but without the arguments to the method. The following statement:

bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));
creates a new delegate object associated with the static method Test.PrintTitle. The following statement:

bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));
creates a new delegate object associated with the nonstatic method AddBookToTotal on the object totaller. In both cases, this new delegate object is immediately passed to the ProcessPaperbackBooksmethod. Note that once a delegate is created, the method it is associated with never changes delegate objects are immutable. y Calling a delegate Once a delegate object is created, the delegate object is typically passed to other code that will call the delegate. A delegate object is called by using the name of the delegate object, followed by the parenthesized arguments to be passed to the delegate. An example of a delegate call is: processBook(b); A delegate can either be called synchronously, as in this example, or asynchronously by using BeginInvoke and EndInvoke methods. Example 2

This example demonstrates composing delegates. A useful property of delegate objects is that they can be composed using the "+" operator. A composed delegate calls the two delegates it was composed from. Only delegates of the same type can be composed. The "-" operator can be used to remove a component delegate from a composed delegate.

// compose.cs using System; delegate void MyDelegate(string s); class MyClass { public static void Hello(string s) { Console.WriteLine(" Hello, {0}!", s); } public static void Goodbye(string s) { Console.WriteLine(" Goodbye, {0}!", s); } public static void Main() { MyDelegate a, b, c, d; // Create the delegate object a that references // the method Hello: a = new MyDelegate(Hello); // Create the delegate object b that references // the method Goodbye: b = new MyDelegate(Goodbye); // The two delegates, a and b, are composed to form c: c = a + b; // Remove a from the composed delegate, leaving d, // which calls only the method Goodbye: d = c - a; Console.WriteLine("Invoking a("A"); Console.WriteLine("Invoking b("B"); Console.WriteLine("Invoking c("C"); Console.WriteLine("Invoking d("D"); } }
Output

delegate a:"); delegate b:"); delegate c:"); delegate d:");

Invoking delegate a:

Hello, A! Invoking delegate b: Goodbye, B! Invoking delegate c: Hello, C! Goodbye, C! Invoking delegate d: Goodbye, D!

Delegates and Events


Delegates are ideally suited for use as events notifications from one component to "listeners" about changes in that component. For more information on the use of delegates for events, see the Events Tutorial.

Delegates vs. Interfaces


Delegates and interfaces are similar in that they enable the separation of specification and implementation. Multiple independent authors can produce implementations that are compatible with an interface specification. Similarly, a delegate specifies the signature of a method, and authors can write methods that are compatible with the delegate specification. When should you use interfaces, and when should you use delegates? Delegates are useful when: y y y y y y y A single method is being called. A class may want to have multiple implementations of the method specification. It is desirable to allow using a static method to implement the specification. An event-like design pattern is desired (for more information, see the Events Tutorial). The caller has no need to know or obtain the object that the method is defined on. The provider of the implementation wants to "hand out" the implementation of the specification to only a few select components. Easy composition is desired.

Interfaces are useful when: y y y The specification defines a set of related methods that will be called. A class typically implements the specification only once. The caller of the interface wants to cast to or from the interface type to obtain other interfaces or classes.

Events Tutorial
Visual Studio .NET 2003

This tutorial shows how to declare, invoke, and hook up to events in C#.

Sample Files
See Events Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y event delegate Handling and Raising Events Delegates Tutorial Introduction to Event Handlers in Windows Forms

Tutorial
An event in C# is a way for a class to provide notifications to clients of that class when some interesting thing happens to an object. The most familiar use for events is in graphical user interfaces; typically, the classes that represent controls in the interface have events that are notified when the user does something to the control (for example, click a button). Events, however, need not be used only for graphical interfaces. Events provide a generally useful way for objects to signal state changes that may be useful to clients of that object. Events are an important building block for creating classes that can be reused in a large number of different programs. Events are declared using delegates. If you have not yet studied the Delegates Tutorial, you should do so before continuing. Recall that a delegate object encapsulates a method so that it can be called anonymously. An event is a way for a class to allow clients to give it delegates to methods that should be called when the event occurs. When the event occurs, the delegate(s) given to it by its clients are invoked. In addition to the examples on declaring, invoking, and hooking up to events, this tutorial also introduces the following topics: y y y Events and Inheritance Events in Interfaces .NET Framework Guidelines

Example 1 The following simple example shows a class, ListWithChangedEvent, which is similar to the standard ArrayList class, but also invokes a Changed event whenever the contents of the list change. Such a general-purpose class could be used in numerous ways in a large program. For example, a word processor might maintain a list of the open documents. Whenever this list changes, many different objects in the word processor might need to be notified so that the user interface could be updated. By using events, the code that maintains the list of documents doesn't need to know who needs

to be notified once the list of documents is changed, the event is automatically invoked and every object that needs to be notified is correctly notified. By using events, the modularity of the program is increased.

// events1.cs using System; namespace MyCollections { using System.Collections; // A delegate type for hooking up change notifications. public delegate void ChangedEventHandler(object sender, EventArgs e); // A class that works just like ArrayList, but sends event // notifications whenever the list changes. public class ListWithChangedEvent: ArrayList { // An event that clients can use to be notified whenever the // elements of the list change. public event ChangedEventHandler Changed; // Invoke the Changed event; called whenever list changes protected virtual void OnChanged(EventArgs e) { if (Changed != null) Changed(this, e); } // Override some of the methods that can change the list; // invoke event after each public override int Add(object value) { int i = base.Add(value); OnChanged(EventArgs.Empty); return i; } public override void Clear() { base.Clear(); OnChanged(EventArgs.Empty); } public override object this[int index] { set { base[index] = value; OnChanged(EventArgs.Empty); } } } }

namespace TestEvents { using MyCollections; class EventListener { private ListWithChangedEvent List; public EventListener(ListWithChangedEvent list) { List = list; // Add "ListChanged" to the Changed event on "List". List.Changed += new ChangedEventHandler(ListChanged); } // This will be called whenever the list changes. private void ListChanged(object sender, EventArgs e) { Console.WriteLine("This is called when the event fires."); } public void Detach() { // Detach the event and delete the list List.Changed -= new ChangedEventHandler(ListChanged); List = null; } } class Test { // Test the ListWithChangedEvent class. public static void Main() { // Create a new list. ListWithChangedEvent list = new ListWithChangedEvent(); // Create a class that listens to the list's change event. EventListener listener = new EventListener(list); // Add and remove items from the list. list.Add("item 1"); list.Clear(); listener.Detach(); } } }
Output

This is called when the event fires. This is called when the event fires.
Code Discussion

y y

Declaring an event To declare an event inside a class, first a delegate type for the event must be declared, if none is already declared. public delegate void ChangedEventHandler(object sender, EventArgs e); The delegate type defines the set of arguments that are passed to the method that handles the event. Multiple events can share the same delegate type, so this step is only necessary if no suitable delegate type has already been declared. Next, the event itself is declared.

public event ChangedEventHandler Changed;


An event is declared like a field of delegate type, except that the keyword event precedes the event declaration, following the modifiers. Events usually are declared public, but any accessibility modifier is allowed. y Invoking an event Once a class has declared an event, it can treat that event just like a field of the indicated delegate type. The field will either be null, if no client has hooked up a delegate to the event, or else it refers to a delegate that should be called when the event is invoked. Thus, invoking an event is generally done by first checking for null and then calling the event. if (Changed != null) Changed(this, e); Invoking an event can only be done from within the class that declared the event. y Hooking up to an event From outside the class that declared it, an event looks like a field, but access to that field is very restricted. The only things that can be done are: y Compose a new delegate onto that field. y Remove a delegate from a (possibly composite) field. This is done with the += and -= operators. To begin receiving event invocations, client code first creates a delegate of the event type that refers to the method that should be invoked from the event. Then it composes that delegate onto any other delegates that the event might be connected to using +=.

y y

// Add "ListChanged" to the Changed event on "List": List.Changed += new ChangedEventHandler(ListChanged);


When the client code is done receiving event invocations, it removes its delegate from the event by using operator -=.

// Detach the event and delete the list: List.Changed -= new ChangedEventHandler(ListChanged);

Events and Inheritance


When creating a general component that can be derived from, what seems to be a problem sometimes arises with events. Since events can only be invoked from within the class that declared them, derived

classes cannot directly invoke events declared within the base class. Although this is sometimes what is desired, often it is appropriate to give the derived class the freedom to invoke the event. This is typically done by creating a protected invoking method for the event. By calling this invoking method, derived classes can invoke the event. For even more flexibility, the invoking method is often declared as virtual, which allows the derived class to override it. This allows the derived class to intercept the events that the base class is invoking, possibly doing its own processing of them. In the preceding example, this has been done with the OnChanged method. A derived class could call or override this method if it needed to.

Events in Interfaces
One other difference between events and fields is that an event can be placed in an interface while a field cannot. When implementing the interface, the implementing class must supply a corresponding event in the class that implements the interface.

.NET Framework Guidelines


Although the C# language allows events to use any delegate type, the .NET Framework has some stricter guidelines on the delegate types that should be used for events. If you intend for your component to be used with the .NET Framework, you probably will want to follow these guidelines. The .NET Framework guidelines indicate that the delegate type used for an event should take two parameters, an "object source" parameter indicating the source of the event, and an "e" parameter that encapsulates any additional information about the event. The type of the "e" parameter should derive from the EventArgs class. For events that do not use any additional information, the .NET Framework has already defined an appropriate delegate type: EventHandler. Example 2 The following example is a modified version of Example 1 that follows the .NET Framework guidelines. The example uses the EventHandler delegate type.

// events2.cs using System; namespace MyCollections { using System.Collections; // A class that works just like ArrayList, but sends event // notifications whenever the list changes: public class ListWithChangedEvent: ArrayList { // An event that clients can use to be notified whenever the // elements of the list change: public event EventHandler Changed; // Invoke the Changed event; called whenever list changes: protected virtual void OnChanged(EventArgs e)

{ if (Changed != null) Changed(this,e); } // Override some of the methods that can change the list; // invoke event after each: public override int Add(object value) { int i = base.Add(value); OnChanged(EventArgs.Empty); return i; } public override void Clear() { base.Clear(); OnChanged(EventArgs.Empty); } public override object this[int index] { set { base[index] = value; OnChanged(EventArgs.Empty); } } } } namespace TestEvents { using MyCollections; class EventListener { private ListWithChangedEvent List; public EventListener(ListWithChangedEvent list) { List = list; // Add "ListChanged" to the Changed event on "List": List.Changed += new EventHandler(ListChanged); } // This will be called whenever the list changes: private void ListChanged(object sender, EventArgs e) { Console.WriteLine("This is called when the event fires."); } public void Detach() { // Detach the event and delete the list:

List.Changed -= new EventHandler(ListChanged); List = null; } } class Test { // Test the ListWithChangedEvent class: public static void Main() { // Create a new list: ListWithChangedEvent list = new ListWithChangedEvent(); // Create a class that listens to the list's change event: EventListener listener = new EventListener(list); // Add and remove items from the list: list.Add("item 1"); list.Clear(); listener.Detach(); } } }
Output

This is called when the event fires. This is called when the event fires.

Explicit Interface Implementation Tutorial


Visual Studio .NET 2003 This tutorial demonstrates how to explicitly implement interface members and how to access those members from the interface instances.

Sample Files
See Explicit Interface Implementation Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y interface 13.4 Interface implementations

13.4.1 Explicit interface member implementations

Tutorial
A class that implements an interface can explicitly implement a member of that interface. When a member is explicitly implemented, it cannot be accessed through a class instance, but only through an instance of the interface. This tutorial contains two examples. The first example illustrates how to explicitly implement and access interface members. The second example shows how to implement two interfaces that have the same member names. Example 1 This example declares an interface IDimensions, and a class Box, which explicitly implements the interface members Length and Width. The members are accessed through the interface instance myDimensions.

// explicit1.cs interface IDimensions { float Length(); float Width(); } class Box : IDimensions { float lengthInches; float widthInches; public Box(float length, float width) { lengthInches = length; widthInches = width; } // Explicit interface member implementation: float IDimensions.Length() { return lengthInches; } // Explicit interface member implementation: float IDimensions.Width() { return widthInches; } public static void Main() { // Declare a class instance "myBox": Box myBox = new Box(30.0f, 20.0f); // Declare an interface instance "myDimensions": IDimensions myDimensions = (IDimensions) myBox; // Print out the dimensions of the box: /* The following commented lines would produce compilation

errors because they try to access an explicitly implemented interface member from a class instance: */ //System.Console.WriteLine("Length: {0}", myBox.Length()); //System.Console.WriteLine("Width: {0}", myBox.Width()); /* Print out the dimensions of the box by calling the methods from an instance of the interface: */ System.Console.WriteLine("Length: {0}", myDimensions.Length()); System.Console.WriteLine("Width: {0}", myDimensions.Width()); } }
Output

Length: 30 Width: 20
Code Discussion y Notice that the following lines, in the Main method, are commented out because they would produce compilation errors. An interface member that is explicitly implemented cannot be accessed from a class instance: //System.Console.WriteLine("Length: {0}", myBox.Length()); //System.Console.WriteLine("Width: {0}", myBox.Width()); Notice also that the following lines, in the Main method, successfully print out the dimensions of the box because the methods are being called from an instance of the interface: System.Console.WriteLine("Length: {0}", myDimensions.Length()); System.Console.WriteLine("Width: {0}", myDimensions.Width());

y y y y y

Example 2 Explicit interface implementation also allows the programmer to inherit two interfaces that share the same member names and give each interface member a separate implementation. This example displays the dimensions of a box in both metric and English units. The Box class inherits two interfaces IEnglishDimensions and IMetricDimensions, which represent the different measurement systems. Both interfaces have identical member names, Length and Width.

// explicit2.cs // Declare the English units interface: interface IEnglishDimensions { float Length(); float Width(); } // Declare the metric units interface: interface IMetricDimensions { float Length(); float Width(); } // Declare the "Box" class that implements the two interfaces: // IEnglishDimensions and IMetricDimensions:

class Box : IEnglishDimensions, IMetricDimensions { float lengthInches; float widthInches; public Box(float length, float width) { lengthInches = length; widthInches = width; } // Explicitly implement the members of IEnglishDimensions: float IEnglishDimensions.Length() { return lengthInches; } float IEnglishDimensions.Width() { return widthInches; } // Explicitly implement the members of IMetricDimensions: float IMetricDimensions.Length() { return lengthInches * 2.54f; } float IMetricDimensions.Width() { return widthInches * 2.54f; } public static void Main() { // Declare a class instance "myBox": Box myBox = new Box(30.0f, 20.0f); // Declare an instance of the English units interface: IEnglishDimensions eDimensions = (IEnglishDimensions) myBox; // Declare an instance of the metric units interface: IMetricDimensions mDimensions = (IMetricDimensions) myBox; // Print dimensions in English units: System.Console.WriteLine("Length(in): {0}", eDimensions.Length()); System.Console.WriteLine("Width (in): {0}", eDimensions.Width()); // Print dimensions in metric units: System.Console.WriteLine("Length(cm): {0}", mDimensions.Length()); System.Console.WriteLine("Width (cm): {0}", mDimensions.Width()); } }
Output

Length(in): Width (in): Length(cm): Width (cm):

30 20 76.2 50.8

Code Discussion

If you want to make the default measurements in English units, implement the methods Length and Width normally, and explicitly implement the Length and Width methods from the IMetricDimensions interface:

// Normal implementation: public float Length() { return lengthInches; } public float Width() { return widthInches; } // Explicit implementation: float IMetricDimensions.Length() { return lengthInches * 2.54f; } float IMetricDimensions.Width() { return widthInches * 2.54f; }
In this case, you can access the English units from the class instance and access the metric units from the interface instance:

System.Console.WriteLine("Length(in): System.Console.WriteLine("Width (in): System.Console.WriteLine("Length(cm): System.Console.WriteLine("Width (cm):

{0}", {0}", {0}", {0}",

myBox.Length()); myBox.Width()); mDimensions.Length()); mDimensions.Width());

Conditional Methods Tutorial


Visual Studio .NET 2003 This tutorial demonstrates conditional methods, which provide a powerful mechanism by which calls to methods can be included or omitted depending on whether a preprocessor symbol is defined.

Sample Files
See Conditional Methods Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y Command-Line Arguments 17.4.2 The Conditional attribute Conditional

y y y y y

#define /define (Preprocessor Definition) System.Diagnostics Namespace Debug.Assert Method Assertions in Managed Code

Tutorial
Conditional methods allow developers to create methods whose calls can be placed in the code and then either included or omitted during compilation based on a preprocessing symbol. Suppose that you want to enable some assertion code in the debug builds and disable it in the retail builds. In C++, there is more than one way to have this functionality in your code, for example: y Using #ifdef, define both debug and release versions of a macro. The debug version calls the tracing code, and the release version does nothing. Because C# doesn't support macros, this method doesn't work. Have two implementations of the code being called. That is, in the debug version, have full functionality, and in the retail version, have empty stubs for the methods. Users then choose which one to include when linking the project. The problem with this approach is that the retail builds contain calls to empty methods, and the configuration is more complex.

C# conditional methods provide a simple solution to this problem that is similar to the first approach listed above. There are two basic mechanisms to do this: y y #define the preprocessing identifier in the source code directly. (See an example of this approach in Conditional.) Define the preprocessing identifier on the C# command line via the /define option (/d). This approach is used in the following example.

Conditional methods are used in the .NET Framework. The System.Diagnostics namespace contains a number of classes that support tracing and debugging in applications. Use the System.Diagnostics.Trace andSystem.Diagnostics.Debug classes to add sophisticated tracing and debugging to your application (functionality that can be compiled out of your retail builds through the use of conditional methods). The example below shows how to implement a very simple tracing mechanism using conditional methods. System.Diagnostics.Trace provides much more sophisticated tracing mechanisms, but it uses the fundamental mechanism below to provide this functionality. Example This example consists of two source files: the first file is the library that provides a tracing mechanism, and the second file is the client program that uses this library. File #1: Creating Conditional Methods

The code below shows a simple library that provides a tracing mechanism that displays trace messages to the system console. Clients can embed trace calls in the code and then be able to control whether the tracing gets called by defining symbols in their own compilation phase.

// CondMethod.cs // compile with: /target:library /d:DEBUG using System; using System.Diagnostics; namespace TraceFunctions { public class Trace { [Conditional("DEBUG")] public static void Message(string traceMessage) { Console.WriteLine("[TRACE] - " + traceMessage); } } }
Code Discussion The following line:

[Conditional("DEBUG")]
marks the Message method as being conditional (via the Conditional attribute). The Conditional attribute takes one parameter the preprocessing identifier that controls whether the method call is included when clients are compiled. If the preprocessing identifier is defined, the method is called; otherwise, the call is never inserted in the client's code. There are restrictions on which methods can be marked as conditional; see 17.4.2 The Conditional attribute in the C# Language Specification for more information. File #2: Using the Conditional Method The following client program uses the Trace class defined in file #1 to do some simple tracing.

// TraceTest.cs // compile with: /reference:CondMethod.dll // arguments: A B C using System; using TraceFunctions; public class TraceClient { public static void Main(string[] args) { Trace.Message("Main Starting"); if (args.Length == 0)

{ Console.WriteLine("No arguments have been passed"); } else { for( int i=0; i < args.Length; i++) { Console.WriteLine("Arg[{0}] is [{1}]",i,args[i]); } } Trace.Message("Main Ending"); } }
Code Discussion Conditional code is included in client code depending on whether the preprocessing identifier is defined when the client code gets compiled. Compiling with client code with the /d:DEBUG flag means that the compiler inserts the call to the Trace method. If the symbol is not defined, the call is never made. Sample Run The command:

tracetest A B C
gives the following output:

[TRACE] Arg[0] is Arg[1] is Arg[2] is [TRACE] -

Main Starting [A] [B] [C] Main Ending

The command:

tracetest
gives the following output:

[TRACE] - Main Starting No arguments have been passed [TRACE] - Main Ending

XML Documentation Tutorial


Visual Studio .NET 2003 This tutorial shows how to document code using XML.

Sample Files
See XML Documentation Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y /doc (Process Documentation Comments) Recommended Tags for Documentation Comments Delimiters for Documentation Tags Processing the XML File XML Glossary

Tutorial
C# provides a mechanism for developers to document their code using XML. In source code files, lines that begin with /// and that precede a user-defined type such as a class, delegate, or interface; a member such as a field, event, property, or method; or a namespace declaration can be processed as comments and placed in a file. Example The following sample provides a basic overview of a type that has been documented. To compile the example, type the following command line:

csc XMLsample.cs /doc:XMLsample.xml


This will create the XML file XMLsample.xml, which you can view in your browser or by using the TYPE command.

// XMLsample.cs // compile with: /doc:XMLsample.xml using System; /// <summary> /// Class level summary documentation goes here.</summary> /// <remarks> /// Longer comments can be associated with a type or member /// through the remarks tag</remarks> public class SomeClass { /// <summary>

/// Store for the name property</summary> private string myName = null; /// <summary> /// The class constructor. </summary> public SomeClass() { // TODO: Add Constructor Logic here } /// <summary> /// Name property </summary> /// <value> /// A value tag is used to describe the property value</value> public string Name { get { if ( myName == null ) { throw new Exception("Name is null"); } return myName; } } /// <summary> /// Description for SomeMethod.</summary> /// <param name="s"> Parameter description for s goes here</param> /// <seealso cref="String"> /// You can use the cref attribute on any tag to reference a type or member /// and the compiler will check that the reference exists. </seealso> public void SomeMethod(string s) { } /// <summary> /// Some other method. </summary> /// <returns> /// Return results are described through the returns tag.</returns> /// <seealso cref="SomeMethod(string)"> /// Notice the use of the cref attribute to reference a specific method </seealso> public int SomeOtherMethod() { return 0; } /// <summary> /// The entry point for the application. /// </summary> /// <param name="args"> A list of command line arguments</param> public static int Main(String[] args) { // TODO: Add code to start application here

return 0; } }
Code Discussion XML documentation starts with ///. When you create a new project, the wizards put some starter /// lines in for you. The processing of these comments has some restrictions: y The documentation must be well-formed XML. If the XML is not well-formed, a warning is generated and the documentation file will contain a comment saying that an error was encountered. For more information on well-formed XML, see XML Glossary. Developers are free to create their own set of tags. There is a recommended set of tags (see the Further Reading section). Some of the recommended tags have special meanings: y The <param> tag is used to describe parameters. If used, the compiler will verify that the parameter exists and that all parameters are described in the documentation. If the verification failed, the compiler issues a warning. y The cref attribute can be attached to any tag to provide a reference to a code element. The compiler will verify that this code element exists. If the verification failed, the compiler issues a warning. The compiler also respects any using statements when looking for a type described in the cref attribute. y The <summary> tag is used by IntelliSense inside Visual Studio to display additional information about a type or member.

Sample Output Here is the resulting XML file from the class above:

<?xml version="1.0"?> <doc> <assembly> <name>xmlsample</name> </assembly> <members> <member name="T:SomeClass"> <summary> Class level summary documentation goes here.</summary> <remarks> Longer comments can be associated with a type or member through the remarks tag</remarks> </member> <member name="F:SomeClass.myName"> <summary> Store for the name property</summary> </member> <member name="M:SomeClass.#ctor"> <summary>The class constructor.</summary> </member> <member name="M:SomeClass.SomeMethod(System.String)"> <summary>

Description for SomeMethod.</summary> <param name="s"> Parameter description for s goes here</param> <seealso cref="T:System.String"> You can use the cref attribute on any tag to reference a type or member and the compiler will check that the reference exists. </seealso> </member> <member name="M:SomeClass.SomeOtherMethod"> <summary> Some other method. </summary> <returns> Return results are described through the returns tag.</returns> <seealso cref="M:SomeClass.SomeMethod(System.String)"> Notice the use of the cref attribute to reference a specific method </seealso> </member> <member name="M:SomeClass.Main(System.String[])"> <summary> The entry point for the application. </summary> <param name="args"> A list of command line arguments</param> </member> <member name="P:SomeClass.Name"> <summary> Name property </summary> <value> A value tag is used to describe the property value</value> </member> </members> </doc>
Note The XML file does not provide full information about the type and members (for example, it does not contain any type information). To get full information about a type or member, the documentation file must be used in conjunction with reflection on the actual type or member.

Platform Invoke Tutorial


Visual Studio .NET 2003 Platform Invocation Services (PInvoke) allows managed code to call unmanaged functions that are implemented in a DLL. This tutorial shows you what you need to do to be able to call unmanaged DLL functions from C#. The attributes discussed in the tutorial allow you to call these functions and have data types be marshaled correctly.

Sample Files

See Platform Invoke Sample to download and build the sample files discussed in this tutorial. Platform Invoke

Further Reading
y y y y y y y A Closer Look at Platform Invoke Using Attributes DllImportAttribute Class MarshalAsAttribute Class StructLayoutAttribute Class InAttribute Class OutAttribute Class

Tutorial
There are two ways that C# code can directly call unmanaged code: y y Directly call a function exported from a DLL. Call an interface method on a COM object (for more information, see COM Interop Part 1: C# Client Tutorial).

For both techniques, you must provide the C# compiler with a declaration of the unmanaged function, and you may also need to provide the C# compiler with a description of how to marshal the parameters and return value to and from the unmanaged code. The tutorial consists of the following topics: y y y y Calling a DLL Export Directly from C# Default Marshaling and Specifying Custom Marshaling for Parameters to Unmanaged Methods Specifying Custom Marshaling for User-Defined Structs Registering Callback Methods

The tutorial includes the following examples: y y y Example 1 Using DllImport Example 2 Overriding Default Marshaling Example 3 Specifying Custom Marshaling

Calling a DLL Export Directly from C#


To declare a method as having an implementation from a DLL export, do the following: y Declare the method with the static and extern C# keywords.

Attach the DllImport attribute to the method. The DllImport attribute allows you to specify the name of the DLL that contains the method. The common practice is to name the C# method the same as the exported method, but you can also use a different name for the C# method. Optionally, specify custom marshaling information for the method's parameters and return value, which will override the .NET Framework default marshaling.

Example 1 This example shows you how to use the DllImport attribute to output a message by calling puts from msvcrt.dll.

// PInvokeTest.cs using System; using System.Runtime.InteropServices; class PlatformInvokeTest { [DllImport("msvcrt.dll")] public static extern int puts(string c); [DllImport("msvcrt.dll")] internal static extern int _flushall(); public static void Main() { puts("Test"); _flushall(); } }
Output

Test
Code Discussion The preceding example shows the minimum requirements for declaring a C# method that is implemented in an unmanaged DLL. The method PlatformInvokeTest.puts is declared with the static and extern modifiers and has the DllImport attribute which tells the compiler that the implementation comes from msvcrt.dll, using the default name of puts. To use a different name for the C# method such as putstring, you must use the EntryPoint option in the DllImport attribute, that is:

[DllImport("msvcrt.dll", EntryPoint="puts")]
For more information on the syntax of the DllImport attribute, see DllImportAttribute Class.

Default Marshaling and Specifying Custom Marshaling for Parameters to Unmanaged Methods
When calling an unmanaged function from C# code, the common language runtime must marshal the parameters and return values. For every .NET Framework type there is a default unmanaged type, which the common language runtime will use to marshal data across a managed to unmanaged function call. For example, the default marshaling for C# string values is to the type LPTSTR (pointer to TCHAR char buffer). You can override the default marshaling using the MarshalAs attribute in the C# declaration of the unmanaged function. Example 2 This example uses the DllImport attribute to output a string. It also shows you how to override the default marshaling of the function parameters by using the MarshalAs attribute.

// Marshal.cs using System; using System.Runtime.InteropServices; class PlatformInvokeTest { [DllImport("msvcrt.dll")] public static extern int puts( [MarshalAs(UnmanagedType.LPStr)] string m); [DllImport("msvcrt.dll")] internal static extern int _flushall(); public static void Main() { puts("Hello World!"); _flushall(); } }
Output When you run this example, the string,

Hello World!
will display at the console. Code Discussion In the preceding example, the default marshaling for the parameter to the puts function has been overridden from the default of LPTSTR to LPSTR.

The MarshalAs attribute can be placed on method parameters, method return values, and fields of structs and classes. To set the marshaling of a method return value, place the MarshalAs attribute in an attribute block on the method with the return attribute location override. For example, to explicitly set the marshaling for the return value of the puts method:

... [DllImport("msvcrt.dll")] [return : MarshalAs(UnmanagedType.I4)] public static extern int puts( ...
For more information on the syntax of the MarshalAs attribute, see MarshalAsAttribute Class. Note The In and Out attributes can be used to annotate parameters to unmanaged methods. They behave in a similar manner to the in and out modifiers in MIDL source files. Note that the Out attribute is different from the C# parameter modifier, out. For more information on the In and Out attributes, see InAttribute Class and OutAttribute Class.

Specifying Custom Marshaling for User-Defined Structs


You can specify custom marshaling attributes for fields of structs and classes passed to or from unmanaged functions. You do this by adding MarshalAs attributes to the fields of the struct or class. You must also use the StructLayout attribute to set the layout of the struct, optionally to control the default marshaling of string members, and to set the default packing size. Example 3 This example demonstrates how to specify custom marshaling attributes for a struct. Consider the following C structure:

typedef struct tagLOGFONT { LONG lfHeight; LONG lfWidth; LONG lfEscapement; LONG lfOrientation; LONG lfWeight; BYTE lfItalic; BYTE lfUnderline; BYTE lfStrikeOut; BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality; BYTE lfPitchAndFamily; TCHAR lfFaceName[LF_FACESIZE]; } LOGFONT;

In C#, you can describe the preceding struct by using the StructLayout and MarshalAs attributes as follows:

// logfont.cs // compile with: /target:module using System; using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential)] public class LOGFONT { public const int LF_FACESIZE = 32; public int lfHeight; public int lfWidth; public int lfEscapement; public int lfOrientation; public int lfWeight; public byte lfItalic; public byte lfUnderline; public byte lfStrikeOut; public byte lfCharSet; public byte lfOutPrecision; public byte lfClipPrecision; public byte lfQuality; public byte lfPitchAndFamily; [MarshalAs(UnmanagedType.ByValTStr, SizeConst=LF_FACESIZE)] public string lfFaceName; }
For more information on the syntax of the StructLayout attribute, see StructLayoutAttribute Class. The structure can then be used in C# code as shown below:

// pinvoke.cs // compile with: /addmodule:logfont.netmodule using System; using System.Runtime.InteropServices; class PlatformInvokeTest { [DllImport("gdi32.dll", CharSet=CharSet.Auto)] public static extern IntPtr CreateFontIndirect( [In, MarshalAs(UnmanagedType.LPStruct)] LOGFONT lplf // characteristics ); [DllImport("gdi32.dll")] public static extern bool DeleteObject( IntPtr handle ); public static void Main() { LOGFONT lf = new LOGFONT();

lf.lfHeight = 9; lf.lfFaceName = "Arial"; IntPtr handle = CreateFontIndirect(lf); if (IntPtr.Zero == handle) { Console.WriteLine("Can't creates a logical font."); } else { if (IntPtr.Size == 4) Console.WriteLine("{0:X}", handle.ToInt32()); else Console.WriteLine("{0:X}", handle.ToInt64()); // Delete the logical font created. if (!DeleteObject(handle)) Console.WriteLine("Can't delete the logical font"); } } }
Sample Run

C30A0AE5
Code Discussion In the preceding example, the CreateFontIndirect method is using a parameter of the type LOGFONT. The MarshalAs and In attributes are used to qualify the parameter. The program displays the numeric value returned by the method as a hexadecimal uppercase string.

Registering Callback Methods


To register a managed callback that calls an unmanaged function, declare a delegate with the same argument list and pass an instance of it via PInvoke. On the unmanaged side it will appear as a function pointer. For more information about PInvoke and callback, see A Closer Look at Platform Invoke. For example, consider the following unmanaged function, MyFunction, which requires callback as one of the arguments:

typedef void (__stdcall *PFN_MYCALLBACK)(); int __stdcall MyFunction(PFN_ MYCALLBACK callback);


To call MyFunction from managed code, declare the delegate, attach DllImport to the function declaration, and optionally marshal any parameters or the return value:

public delegate void MyCallback(); [DllImport("MYDLL.DLL")] public static extern void MyFunction(MyCallback callback);

Also, make sure the lifetime of the delegate instance covers the lifetime of the unmanaged code; otherwise, the delegate will not be available after it is garbage-collected.

COM Interop Tutorials


Visual Studio .NET 2003 This section introduces two tutorials on interoperability between managed code and COM components. COM Interop provides backward compatibility to let you access existing COM components without requiring that the original component be modified. The following tutorial demonstrates backward compatibility: y COM Interop Part 1: C# Client Tutorial

COM Interop also allows COM developers to access managed code as easily as they access other COM objects. The following tutorial demonstrates accessing a C# server from a COM client: y COM Interop Part 2: C# Server Tutorial

COM Interop Part 1: C# Client Tutorial


Visual Studio .NET 2003 COM Interop provides access to existing COM components without requiring that the original component be modified. When you want to incorporate COM code into a managed application, import the relevant COM types by using a COM Interop utility (TlbImp.exe) for that purpose. Once imported, the COM types are ready to use. In addition, COM Interop allows COM developers to access managed objects as easily as they access other COM objects. Again, COM Interop provides a specialized utility (RegAsm.exe) that exports the managed types into a type library and registers the managed component as a traditional COM component. At run time, the common language runtime marshals data between COM objects and managed objects as needed. This tutorial shows how to use C# to interoperate with COM objects. COM Interop Part 2: C# Server Tutorial covers using a C# server with a C++ COM client. For an overview of both tutorials, see COM Interop Tutorials.

Sample Files
See COM Interop Part 1 Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y y y y y y C# Attributes 17. Attributes Advanced COM Interop System.Runtime.InteropServices Namespace Type Library to Assembly Conversion Summary Type Library Importer (Tlbimp.exe) MSIL Disassembler (Ildasm.exe) Assembly Registration Tool (Regasm.exe) Interop Marshaling COM Interop Part 2: C# Server Tutorial

Tutorial
C# uses .NET Framework facilities to perform COM Interop. C# has support for: y y y y Creating COM objects. Determining if a COM interface is implemented by an object. Calling methods on COM interfaces. Implementing objects and interfaces that can be called by COM clients.

The .NET Framework handles reference-counting issues with COM Interop so there is no need to call or implement AddRef and Release. This tutorial covers the following topics: y y y y y y Creating a COM Class Wrapper Declaring a COM coclass Creating a COM Object Declaring a COM Interface Using Casts Instead of QueryInterface Putting It All Together

Creating a COM Class Wrapper


For C# code to reference COM objects and interfaces, you need to include a .NET Framework definition for the COM interfaces in your C# build. The easiest way to do this is to use TlbImp.exe (Type Library Importer), a command-line tool included in the .NET Framework SDK. TlbImp converts a COM type library into .NET Framework metadata effectively creating a managed wrapper that can be called from any managed language. .NET Framework metadata created with TlbImp can be included in a C# build via the

/R compiler option. If you are using the Visual Studio development environment, you only need to add a reference to the COM type library and the conversion is done for you automatically. TlbImp performs the following conversions: y y COM coclasses are converted to C# classes with a parameterless constructor. COM structs are converted to C# structs with public fields.

A great way to check the output of TlbImp is to run the .NET Framework SDK command-line tool Ildasm.exe (Microsoft Intermediate Language Disassembler) to view the result of the conversion. Although TlbImp is the preferred method for converting COM definitions to C#, it is not always possible to use it (for example, if there is no typelib for the COM definitions, or if TlbImp cannot handle the definitions in the typelib). In these cases, the alternative is to manually define the COM definitions in C# source code using C# attributes. Once you have created the C# source mapping, you simply compile the C# source code to produce the managed wrapper. The main attributes you need to understand to perform COM mapping are: y y y y ComImport - Marks a class as an externally implemented COM class. Guid Used to specify a universally unique identifier (UUID) for a class or an interface. InterfaceType specifies whether an interface derives from IUnknown or IDispatch. PreserveSig specifies whether the native return value should be converted from an HRESULT to a .NET Framework exception.

Each of these attributes is shown in the context of an actual example in this tutorial.

Declaring a COM coclass


COM coclasses are represented in C# as classes. These classes must have the ComImport attribute associated with them. The following restrictions apply to these classes: y y y The class must not inherit from any other class. The class must implement no interfaces. The class must also have a Guid attribute that sets the globally unique identifier (GUID) for the class.

The following example declares a coclass in C#:

// // declare FilgraphManager as a COM coclass // [ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")] class FilgraphManager { }

The C# compiler will add a parameterless constructor that you can call to create an instance of the COM coclass.

Creating a COM Object


COM coclasses are represented in C# as classes with a parameterless constructor. Creating an instance of this class using the new operator is the C# equivalent of calling CoCreateInstance. Using the class defined above, it is simple to instantiate the class:

class MainClass { public static void Main() { // // Create an instance of a COM coclass - calls // // CoCreateInstance(E436EBB3-524F-11CE-9F53-0020AF0BA770, // NULL, CLSCTX_ALL, // IID_IUnknown, &f) // // returns null on failure. // FilgraphManager f = new FilgraphManager(); } }

Declaring a COM Interface


COM interfaces are represented in C# as interfaces with ComImport and Guid attributes. They cannot include any interfaces in their base interface list, and they must declare the interface member functions in the order that the methods appear in the COM interface. COM interfaces declared in C# must include declarations for all members of their base interfaces with the exception of members of IUnknown and IDispatch the .NET Framework automatically adds these. COM interfaces which derive from IDispatch must be marked with the InterfaceType attribute. When calling a COM interface method from C# code, the common language runtime must marshal the parameters and return values to/from the COM object. For every .NET Framework type, there is a default type that the common language runtime will use to marshal when marshaling across a COM call. For example, the default marshaling for C# string values is to the native type LPTSTR (pointer to TCHAR char buffer). You can override the default marshaling using the MarshalAs attribute in the C# declaration of the COM interface. In COM, a common way to return success or failure is to return an HRESULT and have an out parameter marked as "retval" in MIDL for the real return value of the method. In C# (and the .NET Framework), the standard way to indicate an error has occurred is to throw an exception. By default, the .NET Framework provides an automatic mapping between the two styles of exception handling for COM interface methods called by the .NET Framework.

y y

The return value changes to the signature of the parameter marked retval (void if the method has no parameter marked as retval). The parameter marked as retval is left off of the argument list of the method.

Any non-success return value will cause a System.COMException exception to be thrown. This example shows a COM interface declared in MIDL and the same interface declared in C# (note that the methods use the COM error-handling approach). Here is the original MIDL version of the interface:

[ odl, uuid(56A868B1-0AD4-11CE-B03A-0020AF0BA770), helpstring("IMediaControl interface"), dual, oleautomation ] interface IMediaControl : IDispatch { [id(0x60020000)] HRESULT Run(); [id(0x60020001)] HRESULT Pause(); [id(0x60020002)] HRESULT Stop(); [id(0x60020003)] HRESULT GetState( [in] long msTimeout, [out] long* pfs); [id(0x60020004)] HRESULT RenderFile([in] BSTR strFilename); [id(0x60020005)] HRESULT AddSourceFilter( [in] BSTR strFilename, [out] IDispatch** ppUnk); [id(0x60020006), propget] HRESULT FilterCollection([out, retval] IDispatch** ppUnk); [id(0x60020007), propget] HRESULT RegFilterCollection([out, retval] IDispatch** ppUnk); [id(0x60020008)] HRESULT StopWhenReady(); };
Here is the C# equivalent of this interface:

using System.Runtime.InteropServices; // Declare IMediaControl as a COM interface which

// derives from the IDispatch interface. [Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"), InterfaceType(ComInterfaceType.InterfaceIsDual)] interface IMediaControl // cannot list any base interfaces here { // Note that the members of IUnknown and Interface are NOT // listed here // void Run(); void Pause(); void Stop(); void GetState( [In] int msTimeout, [Out] out int pfs); void RenderFile( [In, MarshalAs(UnmanagedType.BStr)] string strFilename); void AddSourceFilter( [In, MarshalAs(UnmanagedType.BStr)] string strFilename, [Out, MarshalAs(UnmanagedType.Interface)] out object ppUnk); [return : MarshalAs(UnmanagedType.Interface)] object FilterCollection(); [return : MarshalAs(UnmanagedType.Interface)] object RegFilterCollection(); void StopWhenReady(); }
Note how the C# interface has mapped the error-handling cases. If the COM method returns an error, an exception will be raised on the C# side. To prevent the translation of HRESULTs to COMExceptions, attach the PreserveSig(true) attribute to the method in the C# declaration. For details, see PreserveSigAttribute Class.

Using Casts Instead of QueryInterface


A C# coclass is not very useful until you can access an interface that it implements. In C++ you would navigate an object's interfaces using the QueryInterface method on the IUnknown interface. In C# you can do the same thing by explicitly casting the COM object to the desired COM interface. If the cast fails, then an invalid cast exception is thrown:

// Create an instance of a COM coclass: FilgraphManager graphManager = new FilgraphManager(); // // // // See if it supports the IMediaControl COM interface. Note that this will throw a System.InvalidCastException if the cast fails. This is equivalent to QueryInterface for COM objects:

IMediaControl mc = (IMediaControl) graphManager; // Now you call a method on a COM interface: mc.Run();

Putting It All Together


Here is a complete example that creates an AVI file viewer using C#. The program creates an instance of a COM coclass, casts it to a COM interface, and then calls methods on the COM interface. The examples in this section represent two approaches: y y Example 1 Using TlbImp to create the .NET Framework class. Example 2 Writing C# code that manually does the COM mapping.

Example 1: Using TlbImp This example shows you how to create an AVI viewer using TlbImp. The program reads an AVI filename from the command line, creates an instance of the Quartz COM object, then uses the methods RenderFile and Runto display the AVI file. These are the steps to build the program: y Run TlbImp over the TLB. The Media Player used in this example is contained in Quartz.dll, which should be in your Windows system directory. Use the following command to create the .NET Framework DLL: tlbimp c:\winnt\system32\quartz.dll /out:QuartzTypeLib.dll Note that the resulting DLL needs to be named QuartzTypeLib, so the .NET Framework can load the containing types correctly when running. y y y You can use the Ildasm tool to examine the resulting DLL. For example, to display the contents of the file QuartzTypeLib.dll, use the following command: Ildasm QuartzTypeLib.dll Build the program using the C# compiler option /R to include the QuartzTypeLib.dll file.

You can then use the program to display a movie (an example movie to try is Clock.avi, which resides in your Windows directory).

// interop1.cs // compile with: /R:QuartzTypeLib.dll using System; class MainClass { /************************************************************ Abstract: This method collects the file name of an AVI to show then creates an instance of the Quartz COM object. To show the AVI, the program calls RenderFile and Run on IMediaControl. Quartz uses its own thread and window to

display the AVI.The main thread blocks on a ReadLine until the user presses ENTER. Input Parameters: the location of the AVI file it is going to display Returns: void **************************************************************/ public static void Main(string[] args) { // Check to see if the user passed in a filename if (args.Length != 1) { DisplayUsage(); return; } if (args[0] == "/?") { DisplayUsage(); return; } string filename = args[0]; // Check to see if the file exists if (!System.IO.File.Exists(filename)) { Console.WriteLine("File " + filename + " not found."); DisplayUsage(); return; } // Create instance of Quartz // (Calls CoCreateInstance(E436EBB3-524F-11CE-9F53-0020AF0BA770, // NULL, CLSCTX_ALL, IID_IUnknown, &graphManager).): try { QuartzTypeLib.FilgraphManager graphManager = new QuartzTypeLib.FilgraphManager(); // QueryInterface for the IMediaControl interface: QuartzTypeLib.IMediaControl mc = (QuartzTypeLib.IMediaControl)graphManager; // Call some methods on a COM interface // Pass in file to RenderFile method on COM object. mc.RenderFile(filename); // Show file. mc.Run(); } catch(Exception ex) { Console.WriteLine("Unexpected COM exception: " + ex.Message); }

// Wait for completion. Console.WriteLine("Press Enter to continue."); Console.ReadLine(); } private static void DisplayUsage() { // User did not provide enough parameters. // Display usage: Console.WriteLine("VideoPlayer: Plays AVI files."); Console.WriteLine("Usage: VIDEOPLAYER.EXE filename"); Console.WriteLine("where filename is the full path and"); Console.WriteLine("file name of the AVI to display."); } }
Sample Run To display the example movie, Clock.avi, use the following command:

interop1 %windir%\clock.avi
This will display the movie on your screen after you press ENTER. Example 2: The C# Code Approach This example uses the same Main method as Example 1, but instead of running TlbImp, it simply maps the Media Player COM object using C#.

// interop2.cs using System; using System.Runtime.InteropServices; namespace QuartzTypeLib { // Declare IMediaControl as a COM interface which // derives from IDispatch interface: [Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"), InterfaceType(ComInterfaceType.InterfaceIsDual)] interface IMediaControl // Cannot list any base interfaces here { // Note that IUnknown Interface members are NOT listed here: void Run(); void Pause(); void Stop(); void GetState( [In] int msTimeout, [Out] out int pfs); void RenderFile(

[In, MarshalAs(UnmanagedType.BStr)] string strFilename); void AddSourceFilter( [In, MarshalAs(UnmanagedType.BStr)] string strFilename, [Out, MarshalAs(UnmanagedType.Interface)] out object ppUnk); [return: MarshalAs(UnmanagedType.Interface)] object FilterCollection(); [return: MarshalAs(UnmanagedType.Interface)] object RegFilterCollection(); void StopWhenReady(); } // Declare FilgraphManager as a COM coclass: [ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")] class FilgraphManager // Cannot have a base class or // interface list here. { // Cannot have any members here // NOTE that the C# compiler will add a default constructor // for you (no parameters). } } class MainClass { /********************************************************** Abstract: This method collects the file name of an AVI to show then creates an instance of the Quartz COM object. To show the AVI, the program calls RenderFile and Run on IMediaControl. Quartz uses its own thread and window to display the AVI.The main thread blocks on a ReadLine until the user presses ENTER. Input Parameters: the location of the AVI file it is going to display Returns: void *************************************************************/ public static void Main(string[] args) { // Check to see if the user passed in a filename: if (args.Length != 1) { DisplayUsage(); return; } if (args[0] == "/?") { DisplayUsage(); return; }

String filename = args[0]; // Check to see if the file exists if (!System.IO.File.Exists(filename)) { Console.WriteLine("File " + filename + " not found."); DisplayUsage(); return; } // Create instance of Quartz // (Calls CoCreateInstance(E436EBB3-524F-11CE-9F53-0020AF0BA770, // NULL, CLSCTX_ALL, IID_IUnknown, // &graphManager).): try { QuartzTypeLib.FilgraphManager graphManager = new QuartzTypeLib.FilgraphManager(); // QueryInterface for the IMediaControl interface: QuartzTypeLib.IMediaControl mc = (QuartzTypeLib.IMediaControl)graphManager; // Call some methods on a COM interface. // Pass in file to RenderFile method on COM object. mc.RenderFile(filename); // Show file. mc.Run(); } catch(Exception ex) { Console.WriteLine("Unexpected COM exception: " + ex.Message); } // Wait for completion. Console.WriteLine("Press Enter to continue."); Console.ReadLine(); } private static void DisplayUsage() { // User did not provide enough parameters. // Display usage. Console.WriteLine("VideoPlayer: Plays AVI files."); Console.WriteLine("Usage: VIDEOPLAYER.EXE filename"); Console.WriteLine("where filename is the full path and"); Console.WriteLine("file name of the AVI to display."); } }
Sample Run To display the example movie, Clock.avi, use the following command:

interop2 %windir%\clock.avi
This will display the movie on your screen after you press ENTER.

COM Interop Part 2: C# Server Tutorial


Visual Studio .NET 2003 COM Interop allows COM developers to access managed code as easily as they access other COM objects. This tutorial demonstrates using a C# server with a C++ COM client. It also explains the following activities: y y How to create the C# server How to create the COM client

The tutorial also briefly demonstrates the marshaling that is automatically applied between managed and unmanaged components. COM Interop Part 1: C# Client Tutorial shows the fundamentals of using C# to interoperate with COM objects and is a prerequisite for this tutorial. For an overview of both tutorials, see COM Interop Tutorials.

Sample Files
See COM Interop Part 2 Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y y y y y C# Attributes 17. Attributes Advanced COM Interop System.Runtime.InteropServices Namespace Type Library to Assembly Conversion Summary Assembly Registration Tool (Regasm.exe) Type Library Importer (Tlbimp.exe) Interop Marshaling COM Interop Part 1: C# Client Tutorial

Tutorial
This tutorial demonstrates the following activities to create the C# server: y How to use the Guid attribute on interfaces and classes to expose them as COM objects and how to generate a globally unique identifier (GUID) for the Guid attribute.

How to use RegAsm to register a .NET Framework program for use by COM clients and create a type library (.tlb file) from a .NET Framework program.

The tutorial also demonstrates the following activities to create the COM client: y y How to export managed servers and how to use them to create COM objects. How to import the .tlb file, generated by RegAsm, into the COM client and how to use CoCreateInstance to create an instance of a .NET Framework coclass. Note To create a GUID for interfaces and coclasses that you export to COM clients, use the tool Guidgen.exe, shipped as part of Visual Studio. Guidgen allows you to choose the format in which the GUID is expressed so you don't have to retype it. For more information on Guidgen, see the Knowledge Base article Q168318 "XADM: Guidgen.exe Available Only for Intel Platforms." KB articles are available in the MSDN Library and on the Web at http://support.microsoft.com. Example This example consists of two files: y y A C# file, CSharpServer.cs, that creates the CSharpServer.dll file. The .dll is used to create the file CSharpServer.tlb. A C++ file, COMClient.cpp, that creates the executable client, COMClient.exe.

File 1: CSharpServer.cs

// CSharpServer.cs // compile with: /target:library // post-build command: regasm CSharpServer.dll /tlb:CSharpServer.tlb using System; using System.Runtime.InteropServices; namespace CSharpServer { // Since the .NET Framework interface and coclass have to behave as // COM objects, we have to give them guids. [Guid("DBE0E8C4-1C61-41f3-B6A4-4E2F353D3D05")] public interface IManagedInterface { int PrintHi(string name); } [Guid("C6659361-1625-4746-931C-36014B146679")] public class InterfaceImplementation : IManagedInterface { public int PrintHi(string name) { Console.WriteLine("Hello, {0}!", name); return 33; } } }

File 2: COMClient.cpp

// COMClient.cpp // Build with "cl COMClient.cpp" // arguments: friend #include <windows.h> #include <stdio.h> #pragma warning (disable: 4278) // To use managed-code servers like the C# server, // we have to import the common language runtime: #import <mscorlib.tlb> raw_interfaces_only // For simplicity, we ignore the server namespace and use named guids: #if defined (USINGPROJECTSYSTEM) #import "..\RegisterCSharpServerAndExportTLB\CSharpServer.tlb" no_namespace named_guids #else // Compiling from the command line, all files in the same directory #import "CSharpServer.tlb" no_namespace named_guids #endif int main(int argc, char* argv[]) { IManagedInterface *cpi = NULL; int retval = 1; // Initialize COM and create an instance of the InterfaceImplementation class: CoInitialize(NULL); HRESULT hr = CoCreateInstance(CLSID_InterfaceImplementation, NULL, CLSCTX_INPROC_SERVER, IID_IManagedInterface, reinterpret_cast<void**>(&cpi)); if (FAILED(hr)) { printf("Couldn't create the instance!... 0x%x\n", hr); } else { if (argc > 1) { printf("Calling function.\n"); fflush(stdout); // The variable cpi now holds an interface pointer // to the managed interface. // If you are on an OS that uses ASCII characters at the // command prompt, notice that the ASCII characters are // automatically marshaled to Unicode for the C# code. if (cpi->PrintHi(argv[1]) == 33) retval = 0; printf("Returned from function.\n"); } else printf ("Usage: COMClient <name>\n"); cpi->Release();

cpi = NULL; } // Be a good citizen and clean up COM: CoUninitialize(); return retval; }
Output The executable client can be invoked with the command line: COMClient <name>, where <name> is any string you want to use, for example, COMClient friend.

Calling function. Hello, friend! Returned from function.


In the sample IDE project, set the Command Line Arguments property in the project's Property Pages to the desired string (for example, "friend").

Attributes Tutorial
Visual Studio .NET 2003 This tutorial shows how to create custom attribute classes, use them in code, and query them through reflection.

Sample Files
See Attributes Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y C# Attributes 17. Attributes AttributeUsage Discovering Type Information at Runtime

Tutorial

Attributes provide a powerful method of associating declarative information with C# code (types, methods, properties, and so forth). Once associated with a program entity, the attribute can be queried at run time and used in any number of ways. Example usage of attributes includes: y y Associating help documentation with program entities (through a Help attribute). Associating value editors to a specific type in a GUI framework (through a ValueEditor attribute).

In addition to a complete example, this tutorial includes the following topics: y Declaring an Attribute Class The first thing you need to be able to do is declare an attribute. y Using an Attribute Class Once the attribute has been created, you then associate the attribute with a particular program element. y Accessing Attributes Through Reflection Once the attribute has been associated with a program element, you use reflection to query its existence and its value.

Declaring an Attribute Class


Declaring an attribute in C# is simple it takes the form of a class declaration that inherits from System.Attribute and has been marked with the AttributeUsage attribute as shown below:

using System; [AttributeUsage(AttributeTargets.All)] public class HelpAttribute : System.Attribute { public readonly string Url; public string Topic { get { return topic; } set { topic = value; } } // Topic is a named parameter

public HelpAttribute(string url) { this.Url = url; } private string topic; }


Code Discussion y y y

// url is a positional parameter

The attribute AttributeUsage specifies the language elements to which the attribute can be applied. Attributes classes are public classes derived from System.Attribute that have at least one public constructor. Attribute classes have two types of parameters: y Positional parameters must be specified every time the attribute is used. Positional parameters are specified as constructor arguments to the attribute class. In the example above, url is a positional parameter. y Named parameters are optional. If they are specified when the attribute is used, the name of the parameter must be used. Named parameters are defined by having a nonstatic field or property. In the example above, Topic is a named parameter. Attribute parameters are restricted to constant values of the following types: y Simple types (bool, byte, char, short, int, long, float, and double) y string y System.Type y enums y object (The argument to an attribute parameter of type object must be a constant value of one of the above types.) y One-dimensional arrays of any of the above types

Parameters for the AttributeUsage Attribute The attribute AttributeUsage provides the underlying mechanism by which attributes are declared. AttributeUsage has one positional parameter: y AllowOn, which specifies the program elements that the attribute can be assigned to (class, method, property, parameter, and so on). Valid values for this parameter can be found in theSystem.Attributes.AttributeTargets enumeration in the .NET Framework. The default value for this parameter is all program elements (AttributeElements.All).

AttributeUsage has one named parameter: y AllowMultiple, a Boolean value that indicates whether multiple attributes can be specified for one program element. The default value for this parameter is False.

Using an Attribute Class


Here's a simple example of using the attribute declared in the previous section:

[HelpAttribute("http://localhost/MyClassInfo")] class MyClass { }


In this example, the HelpAttribute attribute is associated with MyClass. Note By convention, all attribute names end with the word "Attribute" to distinguish them from other items in the .NET Framework. However, you do not need to specify the attribute suffix when using attributes in code. For example, you can specify HelpAttribute as follows: [Help("http://localhost/MyClassInfo")] // [Help] == [HelpAttribute] class MyClass { }

Accessing Attributes Through Reflection


Once attributes have been associated with program elements, reflection can be used to query their existence and values. The main reflection methods to query attributes are contained in theSystem.Reflection.MemberInfo class (GetCustomAttributes family of methods). The following example demonstrates the basic way of using reflection to get access to attributes:

class MainClass { public static void Main() { System.Reflection.MemberInfo info = typeof(MyClass); object[] attributes = info.GetCustomAttributes(true); for (int i = 0; i < attributes.Length; i ++) { System.Console.WriteLine(attributes[i]); } } }
Example The following is a complete example where all pieces are brought together.

// AttributesTutorial.cs // This example shows the use of class and method attributes.

using System; using System.Reflection; using System.Collections; // The IsTested class is a user-defined custom attribute class. // It can be applied to any declaration including // - types (struct, class, enum, delegate) // - members (methods, fields, events, properties, indexers) // It is used with no arguments. public class IsTestedAttribute : Attribute { public override string ToString() { return "Is Tested"; } } // The AuthorAttribute class is a user-defined attribute class. // It can be applied to classes and struct declarations only. // It takes one unnamed string argument (the author's name). // It has one optional named argument Version, which is of type int. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class AuthorAttribute : Attribute { // This constructor specifies the unnamed arguments to the attribute class. public AuthorAttribute(string name) { this.name = name; this.version = 0; } // This property is readonly (it has no set accessor) // so it cannot be used as a named argument to this attribute. public string Name { get { return name; } } // This property is read-write (it has a set accessor) // so it can be used as a named argument when using this // class as an attribute class. public int Version { get { return version; } set { version = value; } }

public override string ToString() { string value = "Author : " + Name; if (version != 0) { value += " Version : " + Version.ToString(); } return value; } private string name; private int version; } // Here you attach the AuthorAttribute user-defined custom attribute to // the Account class. The unnamed string argument is passed to the // AuthorAttribute class's constructor when creating the attributes. [Author("Joe Programmer")] class Account { // Attach the IsTestedAttribute custom attribute to this method. [IsTested] public void AddOrder(Order orderToAdd) { orders.Add(orderToAdd); } private ArrayList orders = new ArrayList(); } // Attach the AuthorAttribute and IsTestedAttribute custom attributes // to this class. // Note the use of the 'Version' named argument to the AuthorAttribute. [Author("Jane Programmer", Version = 2), IsTested()] class Order { // add stuff here ... } class MainClass { private static bool IsMemberTested(MemberInfo member) { foreach (object attribute in member.GetCustomAttributes(true)) { if (attribute is IsTestedAttribute) { return true; } } return false; } private static void DumpAttributes(MemberInfo member)

{ Console.WriteLine("Attributes for : " + member.Name); foreach (object attribute in member.GetCustomAttributes(true)) { Console.WriteLine(attribute); } } public static void Main() { // display attributes for Account class DumpAttributes(typeof(Account)); // display list of tested members foreach (MethodInfo method in (typeof(Account)).GetMethods()) { if (IsMemberTested(method)) { Console.WriteLine("Member {0} is tested!", method.Name); } else { Console.WriteLine("Member {0} is NOT tested!", method.Name); } } Console.WriteLine(); // display attributes for Order class DumpAttributes(typeof(Order)); // display attributes for methods on the Order class foreach (MethodInfo method in (typeof(Order)).GetMethods()) { if (IsMemberTested(method)) { Console.WriteLine("Member {0} is tested!", method.Name); } else { Console.WriteLine("Member {0} is NOT tested!", method.Name); } } Console.WriteLine(); } }
Output

Attributes for : Account Author : Joe Programmer Member GetHashCode is NOT tested! Member Equals is NOT tested! Member ToString is NOT tested! Member AddOrder is tested!

Member GetType is NOT tested! Attributes for : Order Author : Jane Programmer Version : 2 Is Tested Member GetHashCode is NOT tested! Member Equals is NOT tested! Member ToString is NOT tested! Member GetType is NOT tested!

Security Tutorial
Visual Studio .NET 2003 This tutorial discusses .NET Framework security and shows two ways to modify security permissions in C#: imperative and declarative security.

Sample Files
See Security Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y y Securing Applications Permissions Security Policy Role-Based Security Checks Security Demands Demands

Tutorial
Most application and component developers should not need to do anything special in order to work with the .NET Framework security system and benefit from the safety protection it provides. One exception that requires more in-depth knowledge and special consideration of the security system is secure libraries. This code represents the boundary between secure managed code and unrestricted code, such as native code (that is outside the ability of the .NET Framework security infrastructure to enforce). These libraries typically must be highly trusted to work, and are the one place in managed code where a programming error can potentially expose a security vulnerability. Code access security can't eliminate the potential for human error, but compared to the much larger volume of application code that uses a few secure libraries, the amount of code that requires close scrutiny is dramatically reduced.

Examples
The tutorial includes the following examples: y y y Example 1: Imperative Security Example 2: Declarative Security Example 3: Suppressing Security

Security
The .NET Framework security protects your code and data from being misused or damaged by other code by enforcing security restrictions on managed code. When a .NET Framework application requests permission, the security policy established by the administrator grants the permission or refuses to run the code. Trust is based on evidence about the code such as a digital signature, where the code comes from, and so forth. Once granted, security enforces permissions that control what code is (and by not being granted, what code is not) allowed to do.

Permissions
The .NET Framework security allows code to use protected resources only if it has "permission" to do so. To express this, the .NET Framework uses the concept of permissions, which represent the right for code to access protected resources. Code requests the permissions it needs, and the security policy applied by the .NET Framework determines which permissions the code is actually granted. The .NET Framework provides code access permission classes, each of which encapsulates the ability to access a particular resource. You use these permissions to indicate to the .NET Framework what your code needs to be allowed to do and to indicate what your code's callers must be authorized to do. Policy also uses these objects to determine what permissions to grant to code.

Policy
Enforcement of security policy is what makes .NET Framework managed code safe. Every assembly that loads is subject to security policy that grants code permissions based on trust, with trust based on evidence about the code. See the .NET Framework documentation in the Reading List for information on administering security policy.

Demanding Security Permissions in C#


There are two ways to demand security permissions in C#: y y Imperatively: Using calls to permission classes in the .NET Framework Declaratively: Using security permission attributes

The following two examples demonstrate these two approaches. For more information on demanding security permissions, see Demands.

Example 1: Imperative Security The following is an example of using .NET Framework calls to deny the UnmanagedCode permission.

// ImperativeSecurity.cs using System; using System.Security; using System.Security.Permissions; using System.Runtime.InteropServices; class NativeMethods { // This is a call to unmanaged code. Executing this method requires // the UnmanagedCode security permission. Without this permission // an attempt to call this method will throw a SecurityException: [DllImport("msvcrt.dll")] public static extern int puts(string str); [DllImport("msvcrt.dll")] internal static extern int _flushall(); } class MainClass { private static void CallUnmanagedCodeWithoutPermission() { // Create a security permission object to describe the // UnmanagedCode permission: SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); // Deny the UnmanagedCode from our current set of permissions. // Any method that is called on this thread until this method // returns will be denied access to unmanaged code. // Even though the CallUnmanagedCodeWithPermission method // is called from a stack frame that already // calls Assert for unmanaged code, you still cannot call native // code. Because you use Deny here, the permission gets // overwritten. perm.Deny(); try { Console.WriteLine("Attempting to call unmanaged code without permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code without permission. Whoops!"); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code."); } }

private static void CallUnmanagedCodeWithPermission() { // Create a security permission object to describe the // UnmanagedCode permission: SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); // Check that you have permission to access unmanaged code. // If you don't have permission to access unmanaged code, then // this call will throw a SecurityException. // Even though the CallUnmanagedCodeWithPermission method // is called from a stack frame that already // calls Assert for unmanaged code, you still cannot call native // code. Because you use Deny here, the permission gets // overwritten. perm.Assert(); try { Console.WriteLine("Attempting to call unmanaged code with permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code with permission."); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code. Whoops!"); } } public static void Main() { // The method itself will call the security permission Deny // for unmanaged code, which will override the Assert permission // in this stack frame. SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); perm.Assert(); CallUnmanagedCodeWithoutPermission(); // The method itself will call the security permission Assert // for unmanaged code, which will override the Deny permission in // this stack frame. perm.Deny(); CallUnmanagedCodeWithPermission(); } }
Output

Attempting to call unmanaged code without permission. Caught Security Exception attempting to call unmanaged code.

Attempting to call unmanaged code with permission. Hello World! Called unmanaged code with permission.
Example 2: Declarative Security This is the same example using attributes for the security permissions.

// DeclarativeSecurity.cs using System; using System.Security; using System.Security.Permissions; using System.Runtime.InteropServices; class NativeMethods { // This is a call to unmanaged code. Executing this method requires // the UnmanagedCode security permission. Without this permission, // an attempt to call this method will throw a SecurityException: [DllImport("msvcrt.dll")] public static extern int puts(string str); [DllImport("msvcrt.dll")] internal static extern int _flushall(); } class MainClass { // The security permission attached to this method will deny the // UnmanagedCode permission from the current set of permissions for // the duration of the call to this method: // Even though the CallUnmanagedCodeWithoutPermission method is // called from a stack frame that already calls // Assert for unmanaged code, you still cannot call native code. // Because this function is attached with the Deny permission for // unmanaged code, the permission gets overwritten. [SecurityPermission(SecurityAction.Deny, Flags = SecurityPermissionFlag.UnmanagedCode)] private static void CallUnmanagedCodeWithoutPermission() { try { Console.WriteLine("Attempting to call unmanaged code without permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code without permission. Whoops!"); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code."); } }

// The security permission attached to this method will force a // runtime check for the unmanaged code permission whenever // this method is called. If the caller does not have unmanaged code // permission, then the call will generate a Security Exception. // Even though the CallUnmanagedCodeWithPermission method is called // from a stack frame that already calls // Deny for unmanaged code, it will not prevent you from calling // native code. Because this method is attached with the Assert // permission for unmanaged code, the permission gets overwritten. [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)] private static void CallUnmanagedCodeWithPermission() { try { Console.WriteLine("Attempting to call unmanaged code with permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code with permission."); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code. Whoops!"); } } public static void Main() { SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); // The method itself is attached with the security permission // Deny for unmanaged code, which will override // the Assert permission in this stack frame. perm.Assert(); CallUnmanagedCodeWithoutPermission(); // The method itself is attached with the security permission // Assert for unmanaged code, which will override the Deny // permission in this stack frame. perm.Deny(); CallUnmanagedCodeWithPermission(); } }
Output

Attempting to call unmanaged code without permission. Caught Security Exception attempting to call unmanaged code. Attempting to call unmanaged code with permission. Hello World! Called unmanaged code with permission.

Security and Performance


The .NET Framework security system prevents malicious code downloaded from the network from damaging your computer system. However, these security checks are not without cost, even if your code never throws aSecurityException. Normally the common language runtime verifies that the caller of an unmanaged method has unmanaged code access permission at run time for every call to the unmanaged method. This can be very expensive for applications that make many calls to unmanaged code. The SuppressUnmanagedCodeSecurityAttribute changes this default behavior. When an unmanaged method is declared with this attribute, the security demand is checked when code that calls the method is loaded into the common language runtime. Security Note When using the SuppressUnmanagedCodeSecurityAttribute, you should take extra care to ensure that you are not introducing a security hole. For example, the developer needs to verify that he/she is using the unmanaged API safely and that callers cannot influence or abuse the call. Alternatively, the developer can add an appropriate demand to ensure that all callers have the appropriate permissions. For example, if a call was made into native code to access a file (to take advantage of structured storage such as extended file properties, and so forth) and the unmanaged code demand was suppressed, then a file IO demand should be made explicitly to ensure that the code cannot be misused. Example 3: Optimizing Unmanaged Calls In this example, the check for the unmanaged code permission is executed once at load time, rather than upon every call to the unmanaged method. If the unmanaged method is called multiple times, this could yield a significant performance gain.

// SuppressSecurity.cs using System; using System.Security; using System.Security.Permissions; using System.Runtime.InteropServices; class NativeMethods { // This is a call to unmanaged code. Executing this method requires // the UnmanagedCode security permission. Without this permission, // an attempt to call this method will throw a SecurityException: /* NOTE: The SuppressUnmanagedCodeSecurityAttribute disables the check for the UnmanagedCode permission at runtime. Be Careful! */ [SuppressUnmanagedCodeSecurityAttribute()] [DllImport("msvcrt.dll")] internal static extern int puts(string str); [SuppressUnmanagedCodeSecurityAttribute()] [DllImport("msvcrt.dll")] internal static extern int _flushall(); } class MainClass

{ // The security permission attached to this method will remove the // UnmanagedCode permission from the current set of permissions for // the duration of the call to this method. // Even though the CallUnmanagedCodeWithoutPermission method is // called from a stack frame that already calls // Assert for unmanaged code, you still cannot call native code. // Because this method is attached with the Deny permission for // unmanaged code, the permission gets overwritten. However, because // you are using SuppressUnmanagedCodeSecurityAttribute here, you can // still call the unmanaged methods successfully. // The code should use other security checks to ensure that you don't // incur a security hole. [SecurityPermission(SecurityAction.Deny, Flags = SecurityPermissionFlag.UnmanagedCode)] private static void CallUnmanagedCodeWithoutPermission() { try { // The UnmanagedCode security check is disbled on the call // below. However, the unmanaged call only displays UI. The // security will be ensured by only allowing the unmanaged // call if there is a UI permission. UIPermission uiPermission = new UIPermission(PermissionState.Unrestricted); uiPermission.Demand(); Console.WriteLine("Attempting to call unmanaged code without UnmanagedCode permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code without UnmanagedCode permission."); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code."); } } // The security permission attached to this method will add the // UnmanagedCode permission to the current set of permissions for the // duration of the call to this method. // Even though the CallUnmanagedCodeWithPermission method is called // from a stack frame that already calls // Deny for unmanaged code, it will not prevent you from calling // native code. Because this method is attached with the Assert // permission for unmanaged code, the permission gets overwritten. // Because you are using SuppressUnmanagedCodeSecurityAttribute here, // you can call the unmanaged methods successfully. // The SuppressUnmanagedCodeSecurityAttribute will let you succeed, // even if you don't have a permission. [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.UnmanagedCode)]

private static void CallUnmanagedCodeWithPermission() { try { Console.WriteLine("Attempting to call unmanaged code with permission."); NativeMethods.puts("Hello World!"); NativeMethods._flushall(); Console.WriteLine("Called unmanaged code with permission."); } catch (SecurityException) { Console.WriteLine("Caught Security Exception attempting to call unmanaged code. Whoops!"); } } public static void Main() { SecurityPermission perm = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); // The method itself is attached with the security permission Deny // for unmanaged code, which will override the Assert permission in // this stack frame. However, because you are using // SuppressUnmanagedCodeSecurityAttribute, you can still call the // unmanaged methods successfully. // The code should use other security checks to ensure that you // don't incur a security hole. perm.Assert(); CallUnmanagedCodeWithoutPermission(); // The method itself is attached with the security permission // Assert for unmanaged code, which will override the Deny // permission in this stack frame. Because you are using // SuppressUnmanagedCodeSecurityAttribute, you can call the // unmanaged methods successfully. // The SuppressUnmanagedCodeSecurityAttribute will let you succeed, // even if you don't have a permission. perm.Deny(); CallUnmanagedCodeWithPermission(); } }
Output

Attempting to call unmanaged code without UnmanagedCode permission. Hello World! Called unmanaged code without UnmanagedCode permission. Attempting to call unmanaged code with permission. Hello World! Called unmanaged code with permission.
Code Discussion

Note that the above example allows both unmanaged calls to succeed even though the first call doesn't have UnmanagedCode permission. When using the SuppressUnmanagedCodeSecurityAttribute, you should use other security checks to ensure that you don't incur a security hole. In the above example, this is done by adding the Demand for the UIPermission:

uiPermission.Demand();
before the unmanaged call, which ensures that the caller has permission to display UI.

Threading Tutorial
Visual Studio .NET 2003 The advantage of threading is the ability to create applications that use more than one thread of execution. For example, a process can have a user interface thread that manages interactions with the user and worker threads that perform other tasks while the user interface thread waits for user input. This tutorial demonstrates various thread activities: y y y y y Creating and executing a thread Synchronization of threads Interaction between threads Using a thread pool Using a mutex object to protect a shared resource

Sample Files
See Threading Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y Threads and Threading Threading Objects and Features System.Threading Namespace lock Statement Wait Synchronization Technology Sample

Tutorial
This tutorial contains the following examples: y Example 1: Creating, starting, and interacting between threads

y y y

Example 2: Synchronizing two threads: a producer and a consumer Example 3: Using a thread pool Example 4: Using the Mutex object

Example 1: Creating, starting, and interacting between threads This example demonstrates how to create and start a thread, and shows the interaction between two threads running simultaneously within the same process. Note that you don't have to stop or free the thread. This is done automatically by the .NET Framework common language runtime. The program begins by creating an object of type Alpha (oAlpha) and a thread (oThread) that references the Beta method of the Alpha class. The thread is then started. The IsAlive property of the thread allows the program to wait until the thread is initialized (created, allocated, and so on). The main thread is accessed through Thread, and the Sleep method tells the thread to give up its time slice and stop executing for a certain amount of milliseconds. The oThread is then stopped and joined. Joining a thread makes the main thread wait for it to die or for a specified time to expire (for more details, see Thread.Join Method). Finally, the program attempts to restart oThread, but fails because a thread cannot be restarted after it is stopped (aborted). For information on temporary cessation of execution, see Suspending Thread Execution.

// StopJoin.cs using System; using System.Threading; public class Alpha { // This method that will be called when the thread is started public void Beta() { while (true) { Console.WriteLine("Alpha.Beta is running in its own thread."); } } }; public class Simple { public static int Main() { Console.WriteLine("Thread Start/Stop/Join Sample"); Alpha oAlpha = new Alpha(); // Create the thread object, passing in the Alpha.Beta method // via a ThreadStart delegate. This does not start the thread. Thread oThread = new Thread(new ThreadStart(oAlpha.Beta)); // Start the thread oThread.Start();

// Spin for a while waiting for the started thread to become // alive: while (!oThread.IsAlive); // Put the Main thread to sleep for 1 millisecond to allow oThread // to do some work: Thread.Sleep(1); // Request that oThread be stopped oThread.Abort(); // Wait until oThread finishes. Join also has overloads // that take a millisecond interval or a TimeSpan object. oThread.Join(); Console.WriteLine(); Console.WriteLine("Alpha.Beta has finished"); try { Console.WriteLine("Try to restart the Alpha.Beta thread"); oThread.Start(); } catch (ThreadStateException) { Console.Write("ThreadStateException trying to restart Alpha.Beta. "); Console.WriteLine("Expected since aborted threads cannot be restarted."); } return 0; } }
Example Output

Thread Start/Stop/Join Sample Alpha.Beta is running in its own thread. Alpha.Beta is running in its own thread. Alpha.Beta is running in its own thread. ... ... Alpha.Beta has finished Try to restart the Alpha.Beta thread ThreadStateException trying to restart Alpha.Beta. Expected since aborted threads cannot be restarted.
Example 2: Synchronizing two threads: a producer and a consumer The following example shows how synchronization can be accomplished using the C# lock keyword and the Pulse method of the Monitor object. The Pulse method notifies a thread in the waiting queue of a change in the object's state (for more details on pulses, see the Monitor.Pulse Method). The example creates a Cell object that has two methods: ReadFromCell and WriteToCell. Two other objects are created from classes CellProd and CellCons; these objects both have a

method ThreadRun whose job is to call ReadFromCell and WriteToCell. Synchronization is accomplished by waiting for "pulses" from the Monitor object, which come in order. That is, first an item is produced (the consumer at this point is waiting for a pulse), then a pulse occurs, then the consumer consumes the production (while the producer is waiting for a pulse), and so on.

// MonitorSample.cs // This example shows use of the following methods of the C# lock keyword // and the Monitor class // in threads: // Monitor.Pulse(Object) // Monitor.Wait(Object) using System; using System.Threading; public class MonitorSample { public static void Main(String[] args) { int result = 0; // Result initialized to say there is no error Cell cell = new Cell( ); CellProd prod = new CellProd(cell, 20); CellCons cons = new CellCons(cell, 20); // // // // Use cell for storage, produce 20 items Use cell for storage, consume 20 items

Thread producer = new Thread(new ThreadStart(prod.ThreadRun)); Thread consumer = new Thread(new ThreadStart(cons.ThreadRun)); // Threads producer and consumer have been created, // but not started at this point. try { producer.Start( ); consumer.Start( ); producer.Join( ); // Join both threads with no timeout // Run both until done.

consumer.Join( ); // threads producer and consumer have finished at this point. } catch (ThreadStateException e) { Console.WriteLine(e); // Display text of exception result = 1; // Result says there was an error } catch (ThreadInterruptedException e) { Console.WriteLine(e); // This exception means that the thread // was interrupted during a Wait result = 1; // Result says there was an error } // Even though Main returns void, this provides a return code to // the parent process.

Environment.ExitCode = result; } } public class CellProd { Cell cell; // Field to hold cell object to be used int quantity = 1; // Field for how many items to produce in cell public CellProd(Cell box, int request) { cell = box; // Pass in what cell object to be used quantity = request; // Pass in how many items to produce in cell } public void ThreadRun( ) { for(int looper=1; looper<=quantity; looper++) cell.WriteToCell(looper); // "producing" } } public class CellCons { Cell cell; // Field to hold cell object to be used int quantity = 1; // Field for how many items to consume from cell public CellCons(Cell box, int request) { cell = box; // Pass in what cell object to be used quantity = request; // Pass in how many items to consume from cell } public void ThreadRun( ) { int valReturned; for(int looper=1; looper<=quantity; looper++) // Consume the result by placing it in valReturned. valReturned=cell.ReadFromCell( ); } } public class Cell { int cellContents; // Cell contents bool readerFlag = false; // State flag public int ReadFromCell( ) { lock(this) // Enter synchronization block { if (!readerFlag) { // Wait until Cell.WriteToCell is done producing try { // Waits for the Monitor.Pulse in WriteToCell Monitor.Wait(this); }

catch (SynchronizationLockException e) { Console.WriteLine(e); } catch (ThreadInterruptedException e) { Console.WriteLine(e); } } Console.WriteLine("Consume: {0}",cellContents); readerFlag = false; // Reset the state flag to say consuming // is done. Monitor.Pulse(this); // Pulse tells Cell.WriteToCell that // Cell.ReadFromCell is done. } // Exit synchronization block return cellContents; } public void WriteToCell(int n) { lock(this) // Enter synchronization block { if (readerFlag) { // Wait until Cell.ReadFromCell is done consuming. try { Monitor.Wait(this); // Wait for the Monitor.Pulse in // ReadFromCell } catch (SynchronizationLockException e) { Console.WriteLine(e); } catch (ThreadInterruptedException e) { Console.WriteLine(e); } } cellContents = n; Console.WriteLine("Produce: {0}",cellContents); readerFlag = true; // Reset the state flag to say producing // is done Monitor.Pulse(this); // Pulse tells Cell.ReadFromCell that // Cell.WriteToCell is done. } // Exit synchronization block } }
Example Output

Produce: Consume: Produce: Consume:

1 1 2 2

Produce: Consume: ... ... Produce: Consume:

3 3 20 20

Example 3: Using a thread pool The following example shows how to use a thread pool. It first creates a ManualResetEvent object, which enables the program to know when the thread pool has finished running all of the work items. Next, it attempts to add one thread to the thread pool. If this succeeds, it adds the rest (four in this example). The thread pool will then put work items into available threads. The WaitOne method on eventX is called, which causes the rest of the program to wait until the event is triggered (with the eventX.Set method). Finally, the program prints out the load (the thread that actually executes a particular work item) on the threads.

// SimplePool.cs // Simple thread pool example using System; using System.Collections; using System.Threading; // Useful way to store info that can be passed as a state on a work item public class SomeState { public int Cookie; public SomeState(int iCookie) { Cookie = iCookie; } } public class Alpha { public Hashtable HashCount; public ManualResetEvent eventX; public static int iCount = 0; public static int iMaxCount = 0; public Alpha(int MaxCount) { HashCount = new Hashtable(MaxCount); iMaxCount = MaxCount; } // Beta is the method that will be called when the work item is // serviced on the thread pool. // That means this method will be called when the thread pool has // an available thread for the work item. public void Beta(Object state) { // Write out the hashcode and cookie for the current thread Console.WriteLine(" {0} {1} :", Thread.CurrentThread.GetHashCode(),

((SomeState)state).Cookie); // The lock keyword allows thread-safe modification // of variables accessible across multiple threads. Console.WriteLine( "HashCount.Count=={0}, Thread.CurrentThread.GetHashCode()=={1}", HashCount.Count, Thread.CurrentThread.GetHashCode()); lock (HashCount) { if (!HashCount.ContainsKey(Thread.CurrentThread.GetHashCode())) HashCount.Add (Thread.CurrentThread.GetHashCode(), 0); HashCount[Thread.CurrentThread.GetHashCode()] = ((int)HashCount[Thread.CurrentThread.GetHashCode()])+1; } // Do some busy work. // Note: Depending on the speed of your machine, if you // increase this number, the dispersement of the thread // loads should be wider. int iX = 2000; Thread.Sleep(iX); // The Interlocked.Increment method allows thread-safe modification // of variables accessible across multiple threads. Interlocked.Increment(ref iCount); if (iCount == iMaxCount) { Console.WriteLine(); Console.WriteLine("Setting eventX "); eventX.Set(); } } } public class SimplePool { public static int Main(string[] args) { Console.WriteLine("Thread Pool Sample:"); bool W2K = false; int MaxCount = 10; // Allow a total of 10 threads in the pool // Mark the event as unsignaled. ManualResetEvent eventX = new ManualResetEvent(false); Console.WriteLine("Queuing {0} items to Thread Pool", MaxCount); Alpha oAlpha = new Alpha(MaxCount); // Create the work items. // Make sure the work items have a reference to the signaling event. oAlpha.eventX = eventX; Console.WriteLine("Queue to Thread Pool 0"); try { // Queue the work items, which has the added effect of checking // which OS is running. ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta), new SomeState(0)); W2K = true; }

catch (NotSupportedException) { Console.WriteLine("These API's may fail when called on a non-Windows 2000 system."); W2K = false; } if (W2K) // If running on an OS which supports the ThreadPool methods. { for (int iItem=1;iItem < MaxCount;iItem++) { // Queue the work items: Console.WriteLine("Queue to Thread Pool {0}", iItem); ThreadPool.QueueUserWorkItem(new WaitCallback(oAlpha.Beta),new SomeState(iItem)); } Console.WriteLine("Waiting for Thread Pool to drain"); // The call to exventX.WaitOne sets the event to wait until // eventX.Set() occurs. // (See oAlpha.Beta). // Wait until event is fired, meaning eventX.Set() was called: eventX.WaitOne(Timeout.Infinite,true); // The WaitOne won't return until the event has been signaled. Console.WriteLine("Thread Pool has been drained (Event fired)"); Console.WriteLine(); Console.WriteLine("Load across threads"); foreach(object o in oAlpha.HashCount.Keys) Console.WriteLine("{0} {1}", o, oAlpha.HashCount[o]); } return 0; } }
Example Output Note The following output will vary from one computer to another.

Thread Pool Sample: Queuing 10 items to Thread Pool Queue to Thread Pool 0 Queue to Thread Pool 1 ... ... Queue to Thread Pool 9 Waiting for Thread Pool to drain 98 0 : HashCount.Count==0, Thread.CurrentThread.GetHashCode()==98 100 1 : HashCount.Count==1, Thread.CurrentThread.GetHashCode()==100 98 2 : ... ... Setting eventX Thread Pool has been drained (Event fired) Load across threads

101 2 100 3 98 4 102 1


Example 4: Using the Mutex object You can use a mutex object to protect a shared resource from simultaneous access by multiple threads or processes. The state of a mutex object is either set to signaled, when it is not owned by any thread, or nonsignaled, when it is owned. Only one thread at a time can own a mutex object. For example, to prevent two threads from writing to shared memory at the same time, each thread waits for ownership of a mutex object before executing the code that accesses the memory. After writing to the shared memory, the thread releases the mutex object. This example demonstrates how to use the classes Mutex, AutoResetEvent, and WaitHandle in processing threads. It also demonstrates the methods used in processing the mutex object.

// Mutex.cs // Mutex object example using System; using System.Threading; public class MutexSample { static Mutex gM1; static Mutex gM2; const int ITERS = 100; static AutoResetEvent Event1 static AutoResetEvent Event2 static AutoResetEvent Event3 static AutoResetEvent Event4

= = = =

new new new new

AutoResetEvent(false); AutoResetEvent(false); AutoResetEvent(false); AutoResetEvent(false);

public static void Main(String[] args) { Console.WriteLine("Mutex Sample ..."); // Create Mutex initialOwned, with name of "MyMutex". gM1 = new Mutex(true,"MyMutex"); // Create Mutex initialOwned, with no name. gM2 = new Mutex(true); Console.WriteLine(" - Main Owns gM1 and gM2"); AutoResetEvent[] evs = evs[0] = Event1; // evs[1] = Event2; // evs[2] = Event3; // evs[3] = Event4; // new AutoResetEvent[4]; Event for t1 Event for t2 Event for t3 Event for t4

MutexSample tm = new MutexSample( ); Thread t1 = new Thread(new ThreadStart(tm.t1Start)); Thread t2 = new Thread(new ThreadStart(tm.t2Start)); Thread t3 = new Thread(new ThreadStart(tm.t3Start)); Thread t4 = new Thread(new ThreadStart(tm.t4Start)); t1.Start( ); // Does Mutex.WaitAll(Mutex[] of gM1 and gM2)

t2.Start( ); t3.Start( ); t4.Start( );

// Does Mutex.WaitOne(Mutex gM1) // Does Mutex.WaitAny(Mutex[] of gM1 and gM2) // Does Mutex.WaitOne(Mutex gM2)

Thread.Sleep(2000); Console.WriteLine(" - Main releases gM1"); gM1.ReleaseMutex( ); // t2 and t3 will end and signal Thread.Sleep(1000); Console.WriteLine(" - Main releases gM2"); gM2.ReleaseMutex( ); // t1 and t4 will end and signal // Waiting until all four threads signal that they are done. WaitHandle.WaitAll(evs); Console.WriteLine("... Mutex Sample"); } public void t1Start( ) { Console.WriteLine("t1Start started, Mutex.WaitAll(Mutex[])"); Mutex[] gMs = new Mutex[2]; gMs[0] = gM1; // Create and load an array of Mutex for WaitAll call gMs[1] = gM2; Mutex.WaitAll(gMs); // Waits until both gM1 and gM2 are released Thread.Sleep(2000); Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied"); Event1.Set( ); // AutoResetEvent.Set() flagging method is done } public void t2Start( ) { Console.WriteLine("t2Start started, gM1.WaitOne( )"); gM1.WaitOne( ); // Waits until Mutex gM1 is released Console.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied"); Event2.Set( ); // AutoResetEvent.Set() flagging method is done } public void t3Start( ) { Console.WriteLine("t3Start started, Mutex.WaitAny(Mutex[])"); Mutex[] gMs = new Mutex[2]; gMs[0] = gM1; // Create and load an array of Mutex for WaitAny call gMs[1] = gM2; Mutex.WaitAny(gMs); // Waits until either Mutex is released Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])"); Event3.Set( ); // AutoResetEvent.Set() flagging method is done } public void t4Start( ) { Console.WriteLine("t4Start started, gM2.WaitOne( )"); gM2.WaitOne( ); // Waits until Mutex gM2 is released Console.WriteLine("t4Start finished, gM2.WaitOne( )"); Event4.Set( ); // AutoResetEvent.Set() flagging method is done }

}
Sample Output

Mutex Sample ... - Main Owns gM1 and gM2 t1Start started, Mutex.WaitAll(Mutex[]) t2Start started, gM1.WaitOne( ) t3Start started, Mutex.WaitAny(Mutex[]) t4Start started, gM2.WaitOne( ) - Main releases gM1 t2Start finished, gM1.WaitOne( ) satisfied t3Start finished, Mutex.WaitAny(Mutex[]) - Main releases gM2 t1Start finished, Mutex.WaitAll(Mutex[]) satisfied t4Start finished, gM2.WaitOne( ) ... Mutex Sample
Note The output of the example may vary from one machine to another and from one run to another. The speed and operating system of the machine running the sample can affect the output order. In multithread environments, events may not occur in the order that you expect.

Unsafe Code Tutorial


Visual Studio .NET 2003 This tutorial demonstrates how to use unsafe code (code using pointers) in C#.

Sample Files
See Unsafe Code Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y A. Unsafe code unsafe fixed Statement

Tutorial
The use of pointers is rarely required in C#, but there are some situations that require them. As examples, using an unsafe context to allow pointers is warranted by the following cases: y Dealing with existing structures on disk

y y

Advanced COM or Platform Invoke scenarios that involve structures with pointers in them Performance-critical code

The use of unsafe context in other situations is discouraged. Specifically, an unsafe context should not be used to attempt to write C code in C#. Caution Code written using an unsafe context cannot be verified to be safe, so it will be executed only when the code is fully trusted. In other words, unsafe code cannot be executed in an untrusted environment. For example, you cannot run unsafe code directly from the Internet. This tutorial includes the following examples: y y y Example 1 Uses pointers to copy an array of bytes. Example 2 Shows how to call the Windows ReadFile function. Example 3 Shows how to print the Win32 version of the executable file.

Example 1 The following example uses pointers to copy an array of bytes from src to dst. Compile the example with the /unsafe option.

// fastcopy.cs // compile with: /unsafe using System; class Test { // The unsafe keyword allows pointers to be used within // the following method: static unsafe void Copy(byte[] src, int srcIndex, byte[] dst, int dstIndex, int count) { if (src == null || srcIndex < 0 || dst == null || dstIndex < 0 || count < 0) { throw new ArgumentException(); } int srcLen = src.Length; int dstLen = dst.Length; if (srcLen - srcIndex < count || dstLen - dstIndex < count) { throw new ArgumentException(); } // The following fixed statement pins the location of // the src and dst objects in memory so that they will // not be moved by garbage collection. fixed (byte* pSrc = src, pDst = dst) { byte* ps = pSrc;

byte* pd = pDst; // Loop over the count in blocks of 4 bytes, copying an // integer (4 bytes) at a time: for (int n =0 ; n < count/4 ; n++) { *((int*)pd) = *((int*)ps); pd += 4; ps += 4; } // Complete the copy by moving any bytes that weren't // moved in blocks of 4: for (int n =0; n < count%4; n++) { *pd = *ps; pd++; ps++; } } } static void Main(string[] args) { byte[] a = new byte[100]; byte[] b = new byte[100]; for(int i=0; i<100; ++i) a[i] = (byte)i; Copy(a, 0, b, 0, 100); Console.WriteLine("The first 10 elements are:"); for(int i=0; i<10; ++i) Console.Write(b[i] + " "); Console.WriteLine("\n"); } }
Example Output

The first 10 elements are: 0 1 2 3 4 5 6 7 8 9


Code Discussion y y Notice the use of the unsafe keyword, which allows pointers to be used within the Copy method. The fixed statement is used to declare pointers to the source and destination arrays. It pins the location of the src and dst objects in memory so that they will not be moved by garbage collection. The objects will be unpinned when the fixed block completes Unsafe code increases the performance by getting rid of array bounds checks.

Example 2

This example shows how to call the Windows ReadFile function from the Platform SDK, which requires the use of an unsafe context because the read buffer requires a pointer as a parameter.

// readfile.cs // compile with: /unsafe // arguments: readfile.cs // Use the program to read and display a text file. using System; using System.Runtime.InteropServices; using System.Text; class FileReader { const uint GENERIC_READ = 0x80000000; const uint OPEN_EXISTING = 3; IntPtr handle; [DllImport("kernel32", SetLastError=true)] static extern unsafe IntPtr CreateFile( string FileName, // uint DesiredAccess, // uint ShareMode, // uint SecurityAttributes, // uint CreationDisposition, // uint FlagsAndAttributes, // int hTemplateFile // ); [DllImport("kernel32", SetLastError=true)] static extern unsafe bool ReadFile( IntPtr hFile, // void* pBuffer, // int NumberOfBytesToRead, // int* pNumberOfBytesRead, // int Overlapped // ); [DllImport("kernel32", SetLastError=true)] static extern unsafe bool CloseHandle( IntPtr hObject // handle to object ); public bool Open(string FileName) { // open the existing file for reading handle = CreateFile( FileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);

file name access mode share mode Security Attributes how to create file attributes handle to template file

handle to file data buffer number of bytes to read number of bytes read overlapped buffer

if (handle != IntPtr.Zero) return true; else return false; } public unsafe int Read(byte[] buffer, int index, int count) { int n = 0; fixed (byte* p = buffer) { if (!ReadFile(handle, p + index, count, &n, 0)) return 0; } return n; } public bool Close() { // close file handle return CloseHandle(handle); } } class Test { public static int Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Usage : ReadFile <FileName>"); return 1; } if (! System.IO.File.Exists(args[0])) { Console.WriteLine("File " + args[0] + " not found."); return 1; } byte[] buffer = new byte[128]; FileReader fr = new FileReader(); if (fr.Open(args[0])) { // Assume that an ASCII file is being read ASCIIEncoding Encoding = new ASCIIEncoding(); int bytesRead; do { bytesRead = fr.Read(buffer, 0, buffer.Length); string content = Encoding.GetString(buffer,0,bytesRead);

Console.Write("{0}", content); } while ( bytesRead > 0); fr.Close(); return 0; } else { Console.WriteLine("Failed to open requested file"); return 1; } } }
Example Input The following input from readfile.txt produces the output shown in Example Output when you compile and run this sample.

line 1 line 2
Example Output

line 1 line 2
Code Discussion The byte array passed into the Read function is a managed type. This means that the common language runtime garbage collector could relocate the memory used by the array at will. The fixed statement allows you to both get a pointer to the memory used by the byte array and to mark the instance so that the garbage collector won't move it. At the end of the fixed block, the instance will be marked so that it can be moved. This capability is known as declarative pinning. The nice part about pinning is that there is very little overhead unless a garbage collection occurs in the fixed block, which is an unlikely occurrence. Example 3 This example reads and displays the Win32 version number of the executable file, which is the same as the assembly version number in this example. The executable file, in this example, is printversion.exe. The example uses the Platform SDK functions VerQueryValue, GetFileVersionInfoSize, and GetFileVersionInfo to retrieve specified version information from the specified version-information resource. This example uses pointers because it simplifies the use of methods whose signatures use pointers to pointers, which are common in the Win32 APIs.

// printversion.cs // compile with: /unsafe using System; using System.Reflection; using System.Runtime.InteropServices; // Give this assembly a version number: [assembly:AssemblyVersion("4.3.2.1")] public class Win32Imports { [DllImport("version.dll")] public static extern bool GetFileVersionInfo (string sFileName, int handle, int size, byte[] infoBuffer); [DllImport("version.dll")] public static extern int GetFileVersionInfoSize (string sFileName, out int handle); // The third parameter - "out string pValue" - is automatically // marshaled from ANSI to Unicode: [DllImport("version.dll")] unsafe public static extern bool VerQueryValue (byte[] pBlock, string pSubBlock, out string pValue, out uint len); // This VerQueryValue overload is marked with 'unsafe' because // it uses a short*: [DllImport("version.dll")] unsafe public static extern bool VerQueryValue (byte[] pBlock, string pSubBlock, out short *pValue, out uint len); } public class C { // Main is marked with 'unsafe' because it uses pointers: unsafe public static int Main () { try { int handle = 0; // Figure out how much version info there is: int size = Win32Imports.GetFileVersionInfoSize("printversion.exe", out handle); if (size == 0) return -1; byte[] buffer = new byte[size]; if (!Win32Imports.GetFileVersionInfo("printversion.exe", handle, size, buffer)) { Console.WriteLine("Failed to query file version information."); return 1; }

short *subBlock = null; uint len = 0; // Get the locale info from the version info: if (!Win32Imports.VerQueryValue (buffer, @"\VarFileInfo\Translation", out subBlock, out len)) { Console.WriteLine("Failed to query version information."); return 1; } string spv = @"\StringFileInfo\" + subBlock[0].ToString("X4") + subBlock[1].ToString("X4") + @"\ProductVersion"; byte *pVersion = null; // Get the ProductVersion value for this program: string versionInfo; if (!Win32Imports.VerQueryValue (buffer, spv, out versionInfo, out len)) { Console.WriteLine("Failed to query version information."); return 1; } Console.WriteLine ("ProductVersion == {0}", versionInfo); } catch (Exception e) { Console.WriteLine ("Caught unexpected exception " + e.Message); } return 0; } }
Example Output

ProductVersion == 4.3.2.1

OLE DB Tutorial
Visual Studio .NET 2003 OLE DB is a COM-based application programming interface (API) for accessing data. OLE DB supports accessing data stored in any format (databases, spreadsheets, text files, and so on) for which an OLE DB provider is available. Each OLE DB provider exposes data from a particular type of data source (for example SQL Server databases, Microsoft Access databases, or Microsoft Excel spreadsheets).

This tutorial demonstrates using a Microsoft Access database from a C# application.

Sample Files
See OLE DB Sample to download and build the sample files discussed in this tutorial.

Further Reading
y y y y y y System.Data.OleDb Namespace OLE DB Programming Overview (using OLE DB in Visual C++) OLE DB Templates (using the OLE DB Templates) OLE DB Programmer's Reference (the technology underlying the System.Data.OleDb namespace and the OLE DB Templates) Choosing Data Access Technologies Choosing Tools and Technologies

Tutorial
This tutorial demonstrates how to use a Microsoft Access database from C#. It shows how you can create a dataset and add tables to it from a database. The BugTypes.mdb database used in the sample program is a Microsoft Access 2000 .MDB file. Example This program accesses the BugTypes.mdb database, creates a dataset, adds the tables to it, and displays the number of tables, columns, and rows. It also displays the titles of each row.

// OleDbSample.cs using System; using System.Data; using System.Data.OleDb; using System.Xml.Serialization; public class MainClass { public static void Main () { // Set Access connection and select strings. // The path to BugTypes.MDB must be changed if you build // the sample from the command line: #if USINGPROJECTSYSTEM string strAccessConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=..\\..\\BugTypes.MDB"; #else string strAccessConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=BugTypes.MDB"; #endif string strAccessSelect = "SELECT * FROM Categories"; // Create the dataset and add the Categories table to it:

DataSet myDataSet = new DataSet(); OleDbConnection myAccessConn = null; try { myAccessConn = new OleDbConnection(strAccessConn); } catch(Exception ex) { Console.WriteLine("Error: Failed to create a database connection. \n{0}", ex.Message); return; } try { OleDbCommand myAccessCommand = new OleDbCommand(strAccessSelect,myAccessConn); OleDbDataAdapter myDataAdapter = new OleDbDataAdapter(myAccessCommand); myAccessConn.Open(); myDataAdapter.Fill(myDataSet,"Categories"); } catch (Exception ex) { Console.WriteLine("Error: Failed to retrieve the required data from the DataBase.\n{0}", ex.Message); return; } finally { myAccessConn.Close(); } // A dataset can contain multiple tables, so let's get them // all into an array: DataTableCollection dta = myDataSet.Tables; foreach (DataTable dt in dta) { Console.WriteLine("Found data table {0}", dt.TableName); } // The next two lines show two different ways you can get the // count of tables in a dataset: Console.WriteLine("{0} tables in data set", myDataSet.Tables.Count); Console.WriteLine("{0} tables in data set", dta.Count); // The next several lines show how to get information on // a specific table by name from the dataset: Console.WriteLine("{0} rows in Categories table", myDataSet.Tables["Categories"].Rows.Count); // The column info is automatically fetched from the database, // so we can read it here:

Console.WriteLine("{0} columns in Categories table", myDataSet.Tables["Categories"].Columns.Count); DataColumnCollection drc = myDataSet.Tables["Categories"].Columns; int i = 0; foreach (DataColumn dc in drc) { // Print the column subscript, then the column's name // and its data type: Console.WriteLine("Column name[{0}] is {1}, of type {2}",i++ , dc.ColumnName, dc.DataType); } DataRowCollection dra = myDataSet.Tables["Categories"].Rows; foreach (DataRow dr in dra) { // Print the CategoryID as a subscript, then the CategoryName: Console.WriteLine("CategoryName[{0}] is {1}", dr[0], dr[1]); } } }
Output The Categories table of the BugTypes.mdb database contains the following information.

Category ID
1 2 3 4

Category Name
Bugbash Stuff Appweek Bugs .NET Framework Reports Internal Support

When you run the sample, the following output will be displayed on the screen:

Found data table Categories 1 tables in data set 1 tables in data set 4 rows in Categories table 2 columns in Categories table Column name[0] is CategoryID, of type System.Int32 Column name[1] is CategoryName, of type System.String CategoryName[1] is Bugbash Stuff CategoryName[2] is Appweek Bugs CategoryName[3] is .NET Framework Reports CategoryName[4] is Internal Support

Potrebbero piacerti anche