Complete CSharp Information – What is CSharp Language in DOT.NET

Complete CSharp(C#) Information – What is CSharp(C#) Language in DOT.NET

C# is a simple, modern, object oriented, and type-safe programming language derived from C and C++. It will immediately be familiar to C and C++ programmers. C# aims to combine the high productivity of Visual Basic and the raw power of C++.

Visual C# .NET is Microsoft’s C# development tool. It includes an interactive development environment, visual designers for building Windows and Web applications, a compiler, and a debugger. Visual C# .NET is part of a suite of products, called Visual Studio .NET, that also includes Visual Basic .NET, Visual C++ .NET, and the JScript scripting language. All of these languages provide access to the Microsoft .NET Framework, which includes a common execution engine and a rich class library. The.NET Framework defines a “Common Language Specification” (CLS), a sort of lingua franca that ensures seamless interoperability between CLS-compliant languages and class libraries. For C# developers, this means that even though C# is a new language, it has complete access to the same rich class libraries that are used by seasoned tools such as Visual Basic .NET and Visual C++ .NET. C# itself does not include a class library.

Close examination of program is illuminating:

The using System; directive references a namespace called System that is provided by the Microsoft .NET Framework class library. This namespace contains the Console class referred to in the Main method. Namespaces provide a hierarchical means of organizing the elements of one or more programs. A “using” directive enables unqualified use of the types that are members of the namespace. The “hello, world” program uses Console.WriteLine as shorthand for System.Console.WriteLine. (For the sake of brevity, most examples in this specification omit the using System; directive.)

The Main method is a member of the class Hello. It has the static modifier, and so it is a method on the class Hello rather than on instances of this class.

The entry point for an application—the method that is called to begin execution—is always a static method named Main.

The “hello, world” output is produced using a class library. The language does not itself provide a class library. Instead, it uses a class library that is also used by Visual Basic .NET and Visual C++ .NET.

For C and C++ developers, it is interesting to note a few things that do not appear in the “hello, world” program.

The program does not use a global method for Main. Methods and variables are not supported at the global level; such elements are always contained within type declarations (e.g., class and struct declarations).

The program does not use either “::” or “->” operators. The “::” is not an operator at all, and the “->” operator is used in only a small fraction of programs – those that employ unsafe code (§Error! Reference source not found.). The separator “.” is used in compound names such as Console.WriteLine.

The program does not contain forward declarations. Forward declarations are never needed, as declaration order is not significant.

The program does not use #include to import program text. Dependencies among programs are handled symbolically rather than textually. This approach eliminates barriers between applications written using different languages. For example, the Console class need not be written in C#.

Types

C# supports two kinds of types: value types and reference types. Value types include simple types (e.g., char, int, and float), enum types, and struct types. Reference types include class types, interface types, delegate types, and array types.

Value types differ from reference types in that variables of the value types directly contain their data, whereas variables of the reference types store references to objects. With reference types, it is possible for two variables to reference the same object, and thus possible for operations on one variable to affect the object referenced by the other variable. With value types, the variables each have their own copy of the data, and it is not possible for operations on one to affect the other.

The example

class Class1
{
public int Value = 0;
}

class Test
{
static void Main() {
int val1 = 0;
int val2 = val1;
val2 = 123;

Class1 ref1 = new Class1();
Class1 ref2 = ref1;
ref2.Value = 123;

Console.WriteLine(“Values: {0}, {1}”, val1, val2);
Console.WriteLine(“Refs: {0}, {1}”, ref1.Value, ref2.Value);
}
}

shows this difference. The output produced is

Values: 0, 123
Refs: 123, 123

The assignment to the local variable val1 does not impact the local variable val2 because both local variables are of a value type (the type int) and each local variable of a value type has its own storage. In contrast, the assignment ref2.Value = 123; affects the object that both ref1 and ref2 reference.

The lines

Console.WriteLine(“Values: {0}, {1}”, val1, val2);
Console.WriteLine(“Refs: {0}, {1}”, ref1.Value, ref2.Value);

deserve further comment, as they demonstrate some of the string formatting behavior of Console.WriteLine, which takes a variable number of arguments. The first argument is a string, which may contain numbered placeholders like {0} and {1}. Each placeholder refers to a trailing argument with {0} referring to the second argument, {1} referring to the third argument, and so on. Before the output is sent to the console, each placeholder is replaced with the formatted value of its corresponding argument.

Developers can define new value types through enum and struct declarations, and can define new reference types via class, interface, and delegate declarations. The example

public enum Color
{
Red, Blue, Green
}

public struct Point
{
public int x, y;
}

public interface IBase
{
void F();
}

public interface IDerived: IBase
{
void G();
}

public class A
{
protected virtual void H() {
Console.WriteLine(“A.H”);
}
}

public class B: A, IDerived
{
public void F() {
Console.WriteLine(“B.F, implementation of IDerived.F”);
}

public void G() {
Console.WriteLine(“B.G, implementation of IDerived.G”);
}

override protected void H() {
Console.WriteLine(“B.H, override of A.H”);
}
}

public delegate void EmptyDelegate();

shows an example of each kind of type declaration. Later sections describe type declarations in detail.

Predefined types

C# provides a set of predefined types, most of which will be familiar to C and C++ developers.

The predefined reference types are object and string. The type object is the ultimate base type of all other types. The type string is used to represent Unicode string values. Values of type string are immutable.

The predefined value types include signed and unsigned integral types, floating point types, and the types bool, char, and decimal. The signed integral types are sbyte, short, int, and long; the unsigned integral types are byte, ushort, uint, and ulong; and the floating point types are float and double.

The bool type is used to represent boolean values: values that are either true or false. The inclusion of bool makes it easier to write self-documenting code, and also helps eliminate the all-too-common C++ coding error in which a developer mistakenly uses “=” when “==” should have been used. In C#, the example

int i = …;
F(i);
if (i = 0) // Bug: the test should be (i == 0)
G();

results in a compile-time error because the expression i = 0 is of type int, and if statements require an expression of type bool.

The char type is used to represent Unicode characters. A variable of type char represents a single 16-bit Unicode character.

The decimal type is appropriate for calculations in which rounding errors caused by floating point representations are unacceptable. Common examples include financial calculations such as tax computations and currency conversions. The decimal type provides 28 significant digits.

The table below lists the predefined types, and shows how to write literal values for each of them.

Type Description Example
object The ultimate base type of all other types object o = null;
string String type; a string is a sequence of Unicode characters string s = “hello”;
sbyte 8-bit signed integral type sbyte val = 12;
short 16-bit signed integral type short val = 12;
int 32-bit signed integral type int val = 12;
long 64-bit signed integral type long val1 = 12;
long val2 = 34L;
byte 8-bit unsigned integral type byte val1 = 12;
ushort 16-bit unsigned integral type ushort val1 = 12;
uint 32-bit unsigned integral type uint val1 = 12;
uint val2 = 34U;
ulong 64-bit unsigned integral type ulong val1 = 12;
ulong val2 = 34U;
ulong val3 = 56L;
ulong val4 = 78UL;
float Single-precision floating point type float val = 1.23F;
double Double-precision floating point type double val1 = 1.23;
double val2 = 4.56D;
bool Boolean type; a bool value is either true or false bool val1 = true;
bool val2 = false;
char Character type; a char value is a Unicode character char val = ‘h’;
decimal Precise decimal type with 28 significant digits decimal val = 1.23M;

Each of the predefined types is shorthand for a system-provided type. For example, the keyword int refers to the struct System.Int32. As a matter of style, use of the keyword is favored over use of the complete system type name.

Predefined value types such as int are treated specially in a few ways but are for the most part treated exactly like other structs. Operator overloading enables developers to define new struct types that behave much like the predefined value types. For instance, a Digit struct can support the same mathematical operations as the predefined integral types, and can define conversions between Digit and predefined types.

The predefined types employ operator overloading themselves. For example, the comparison operators == and != have different semantics for different predefined types:

Two expressions of type int are considered equal if they represent the same integer value.

Two expressions of type object are considered equal if both refer to the same object, or if both are null.

Two expressions of type string are considered equal if the string instances have identical lengths and identical characters in each character position, or if both are null.

The example

class Test
{
static void Main() {
string s = “Test”;
string t = string.Copy(s);
Console.WriteLine(s == t);
Console.WriteLine((object)s == (object)t);
}
}

produces the output

True
False

because the first comparison compares two expressions of type string, and the second comparison compares two expressions of type object.

Conversions

The predefined types also have predefined conversions. For instance, conversions exist between the predefined types int and long. C# differentiates between two kinds of conversions: implicit conversions and explicit conversions. Implicit conversions are supplied for conversions that can safely be performed without careful scrutiny. For instance, the conversion from int to long is an implicit conversion. This conversion always succeeds, and never results in a loss of information. Implicit conversions can be performed implicitly, as shown in the example

class Test
{
static void Main() {
int intValue = 123;
long longValue = intValue;
Console.WriteLine(“{0}, {1}”, intValue, longValue);
}
}

which implicitly converts an int to a long.

In contrast, explicit conversions are performed with a cast expression. The example

class Test
{
static void Main() {
long longValue = Int64.MaxValue;
int intValue = (int) longValue;
Console.WriteLine(“(int) {0} = {1}”, longValue, intValue);
}
}

uses an explicit conversion to convert a long to an int. The output is:

(int) 9223372036854775807 = -1

because an overflow occurs. Cast expressions permit the use of both implicit and explicit conversions.

Array types

Arrays may be single-dimensional or multi-dimensional. Both “rectangular” and “jagged” arrays are supported.

Single-dimensional arrays are the most common type. The example

class Test
{
static void Main() {
int[] arr = new int[5];

for (int i = 0; i < arr.Length; i++)
arr[i] = i * i;

for (int i = 0; i < arr.Length; i++)
Console.WriteLine(“arr[{0}] = {1}”, i, arr[i]);
}
}

creates a single-dimensional array of int values, initializes the array elements, and then prints each of them out. The output produced is:

arr[0] = 0
arr[1] = 1
arr[2] = 4
arr[3] = 9
arr[4] = 16

The type int[] used in the previous example is an array type. Array types are written using a non-array-type followed by one or more rank specifiers. The example

class Test
{
static void Main() {
int[] a1; // single-dimensional array of int
int[,] a2; // 2-dimensional array of int
int[,,] a3; // 3-dimensional array of int

int[][] j2; // “jagged” array: array of (array of int)
int[][][] j3; // array of (array of (array of int))
}
}

shows a variety of local variable declarations that use array types with int as the element type.

Array types are reference types, and so the declaration of an array variable merely sets aside space for the reference to the array. Array instances are actually created via array initializers and array creation expressions. The example

class Test
{
static void Main() {
int[] a1 = new int[] {1, 2, 3};
int[,] a2 = new int[,] {{1, 2, 3}, {4, 5, 6}};
int[,,] a3 = new int[10, 20, 30];

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

shows a variety of array creation expressions. The variables a1, a2 and a3 denote rectangular arrays, and the variable j2 denotes a jagged array. It should be no surprise that these terms are based on the shapes of the arrays. Rectangular arrays always have a rectangular shape. Given the length of each dimension of the array, its rectangular shape is clear. For example, the lengths of a3’s three dimensions are 10, 20, and 30 respectively, and it is easy to see that this array contains 10*20*30 elements.

In contrast, the variable j2 denotes a “jagged” array, or an “array of arrays”. Specifically, j2 denotes an array of an array of int, or a single-dimensional array of type int[]. Each of these int[] variables can be initialized individually, and this allows the array to take on a jagged shape. The example gives each of the int[] arrays a different length. Specifically, the length of j2[0] is 3, the length of j2[1] is 6, and the length of j2[2] is 9.

The element type and shape of an array—including whether it is jagged or rectangular, and the number of dimensions it has—are part of its type. On the other hand, the size of the array—as represented by the length of each of its dimensions—is not part of an array’s type. This split is made clear in the language syntax, as the length of each dimension is specified in the array creation expression rather than in the array type. For instance the declaration

int[,,] a3 = new int[10, 20, 30];

has an array type of int[,,] and an array creation expression of new int[10, 20, 30].

For local variable and field declarations, a shorthand form is permitted so that it is not necessary to re-state the array type. For instance, the example

int[] a1 = new int[] {1, 2, 3};

can be shortened to

int[] a1 = {1, 2, 3};

without any change in program semantics.

The context in which an array initializer such as {1, 2, 3} is used determines the type of the array being initialized. The example

class Test
{
static void Main() {
short[] a = {1, 2, 3};
int[] b = {1, 2, 3};
long[] c = {1, 2, 3};

}
}

shows that the same array initializer syntax can be used for several different array types. Because context is required to determine the type of an array initializer, it is not possible to use an array initializer in an expression context without explicitly stating the type of the array.

Type system unification

C# provides a “unified type system”. All types—including value types—derive from the type object. It is possible to call object methods on any value, even values of “primitive” types such as int. The example

class Test
{
static void Main() {
Console.WriteLine(3.ToString());
}
}

calls the object-defined ToString method on an integer literal, resulting in the output “3”.

The example

class Test
{
static void Main() {
int i = 123;
object o = i; // boxing
int j = (int) o; // unboxing
}
}

is more interesting. An int value can be converted to object and back again to int. This example shows both boxing and unboxing. When a variable of a value type needs to be converted to a reference type, an object box is allocated to hold the value, and the value is copied into the box. Unboxing is just the opposite. When an object box is cast back to its original value type, the value is copied out of the box and into the appropriate storage location.

This type system unification provides value types with the benefits of object-ness without introducing unnecessary overhead. For programs that don’t need int values to act like objects, int values are simply 32-bit values. For programs that need int values to behave like objects, this capability is available on demand. This ability to treat value types as objects bridges the gap between value types and reference types that exists in most languages. For example, a Stack class can provide Push and Pop methods that take and return object values.

public class Stack
{
public object Pop() {…}

public void Push(object o) {…}
}

Because C# has a unified type system, the Stack class can be used with elements of any type, including value types like int.

Variables and parameters

Variables represent storage locations. Every variable has a type that determines what values can be stored in the variable. Local variables are variables that are declared in methods, properties, or indexers. A local variable is defined by specifying a type name and a declarator that specifies the variable name and an optional initial value, as in:

int a;
int b = 1;

but it is also possible for a local variable declaration to include multiple declarators. The declarations of a and b can be rewritten as:

int a, b = 1;

A variable must be assigned before its value can be obtained. The example

class Test
{
static void Main() {
int a;
int b = 1;
int c = a + b; // error, a not yet assigned

}
}

results in a compile-time error because it attempts to use the variable a before it is assigned a value. The rules governing definite assignment are defined in §Error! Reference source not found..

A field (§Error! Reference source not found.) is a variable that is associated with a class or struct, or an instance of a class or struct. A field declared with the static modifier defines a static variable, and a field declared without this modifier defines an instance variable. A static field is associated with a type, whereas an instance variable is associated with an instance. The example

using Personnel.Data;

class Employee
{
private static DataSet ds;

public string Name;
public decimal Salary;


}

shows an Employee class that has a private static variable and two public instance variables.

Formal parameter declarations also define variables. There are four kinds of parameters: value parameters, reference parameters, output parameters, and parameter arrays.

A value parameter is used for “in” parameter passing, in which the value of an argument is passed into a method, and modifications of the parameter do not impact the original argument. A value parameter refers to its own variable, one that is distinct from the corresponding argument. This variable is initialized by copying the value of the corresponding argument. The example

class Test {
static void F(int p) {
Console.WriteLine(“p = {0}”, p);
p++;
}

static void Main() {
int a = 1;
Console.WriteLine(“pre: a = {0}”, a);
F(a);
Console.WriteLine(“post: a = {0}”, a);
}
}

shows a method F that has a value parameter named p. The output produced is:

pre: a = 1
p = 1
post: a = 1

even though the value parameter p is modified.

A reference parameter is used for “by reference” parameter passing, in which the parameter acts as an alias for a caller-provided argument. A reference parameter does not itself define a variable, but rather refers to the variable of the corresponding argument. Modifications of a reference impact the corresponding argument. A reference parameter is declared with a ref modifier. The example

class Test {
static void Swap(ref int a, ref int b) {
int t = a;
a = b;
b = t;
}

static void Main() {
int x = 1;
int y = 2;

Console.WriteLine(“pre: x = {0}, y = {1}”, x, y);
Swap(ref x, ref y);
Console.WriteLine(“post: x = {0}, y = {1}”, x, y);
}
}

shows a Swap method that has two reference parameters. The output of the program is:

pre: x = 1, y = 2
post: x = 2, y = 1

The ref keyword must be used in both the declaration of the formal parameter and in uses of it. The use of ref at the call site calls special attention to the parameter so that a developer reading the code will understand that the value of the argument could change as a result of the call.

An output parameter is similar to a reference parameter, except that the initial value of the caller-provided argument is unimportant. An output parameter is declared with an out modifier. The example

class Test {
static void Divide(int a, int b, out int result, out int remainder) {
result = a / b;
remainder = a % b;
}

static void Main() {
for (int i = 1; i < 10; i++)
for (int j = 1; j < 10; j++) {
int ans, r;
Divide(i, j, out ans, out r);
Console.WriteLine(“{0} / {1} = {2}r{3}”, i, j, ans, r);
}
}
}

shows a Divide method that includes two output parameters—one for the result of the division and another for the remainder.

For value, reference, and output parameters, there is a one-to-one correspondence between caller-provided arguments and the parameters used to represent them. A parameter array enables a many-to-one relationship: many arguments can be represented by a single parameter array. In other words, parameter arrays enable variable length argument lists.

A parameter array is declared with a params modifier. There can be only one parameter array for a given method, and it must be the right-most parameter. The type of a parameter array is always a single dimensional array type. A caller can either pass a single argument of this array type, or any number of arguments of the element type of this array type. For instance, the example

class Test
{
static void F(params int[] args) {
Console.WriteLine(“# of arguments: {0}”, args.Length);
for (int i = 0; i < args.Length; i++)
Console.WriteLine(“\targs[{0}] = {1}”, i, args[i]);
}

static void Main() {
F();
F(1);
F(1, 2);
F(1, 2, 3);
F(new int[] {1, 2, 3, 4});
}
}

shows a method F that takes a variable number of int arguments, and several invocations of this method. The output is:

# of arguments: 0
# of arguments: 1
args[0] = 1
# of arguments: 2
args[0] = 1
args[1] = 2
# of arguments: 3
args[0] = 1
args[1] = 2
args[2] = 3
# of arguments: 4
args[0] = 1
args[1] = 2
args[2] = 3
args[3] = 4

Most of the examples presented in this introduction use the WriteLine method of the Console class. The argument substitution behavior of this method, as exhibited in the example

int a = 1, b = 2;
Console.WriteLine(“a = {0}, b = {1}”, a, b);

is accomplished using a parameter array. The WriteLine method provides several overloaded methods for the common cases in which a small number of arguments are passed, and one method that uses a parameter array.

namespace System
{
public class Console
{
public static void WriteLine(string s) {…}

public static void WriteLine(string s, object a) {…}

public static void WriteLine(string s, object a, object b) {…}

public static void WriteLine(string s, params object[] args) {…}
}
}

Automatic memory management

Manual memory management requires developers to manage the allocation and de-allocation of blocks of memory. Manual memory management is both time-consuming and difficult. In C#, automatic memory management is provided so that developers are freed from this burdensome task. In the vast majority of cases, automatic memory management increases code quality and enhances developer productivity without negatively impacting either expressiveness or performance.

The example

public class Stack
{
private Node first = null;

public bool Empty {
get {
return (first == null);
}
}

public object Pop() {
if (first == null)
throw new Exception(“Can’t Pop from an empty Stack.”);
else {
object temp = first.Value;
first = first.Next;
return temp;
}
}

public void Push(object o) {
first = new Node(o, first);
}

class Node
{
public Node Next;

public object Value;

public Node(object value): this(value, null) {}

public Node(object value, Node next) {
Next = next;
Value = value;
}
}
}

shows a Stack class implemented as a linked list of Node instances. Node instances are created in the Push method and are garbage collected when no longer needed. A Node instance becomes eligible for garbage collection when it is no longer possible for any code to access it. For instance, when an item is removed from the Stack, the associated Node instance becomes eligible for garbage collection.

The example

class Test
{
static void Main() {
Stack s = new Stack();

for (int i = 0; i < 10; i++)
s.Push(i);

s = null;
}
}

shows code that uses the Stack class. A Stack is created and initialized with 10 elements, and then assigned the value null. Once the variable s is assigned null, the Stack and the associated 10 Node instances become eligible for garbage collection. The garbage collector is permitted to clean up immediately, but is not required to do so.

The garbage collector underlying C# may work by moving objects around in memory, but this motion is invisible to most C# developers. For developers who are generally content with automatic memory management but sometimes need fine-grained control or that extra bit of performance, C# provides the ability to write “unsafe” code. Such code can deal directly with pointer types and object addresses. However, C# requires the programmer to fix objects to temporarily prevent the garbage collector from moving them.

This “unsafe” code feature is in fact a “safe” feature from the perspective of both developers and users. Unsafe code must be clearly marked in the code with the modifier unsafe, so developers can’t possibly use unsafe language features accidentally, and the compiler and the execution engine work together to ensure that unsafe code cannot masquerade as safe code. These restrictions limit the use of unsafe code to situations in which the code is trusted.

The example

class Test
{
unsafe static void WriteLocations(byte[] arr) {
fixed (byte *pArray = arr) {
byte *pElem = pArray;
for (int i = 0; i < arr.Length; i++) {
byte value = *pElem;
Console.WriteLine(“arr[{0}] at 0x{1:X} is {2}”,
i, (uint)pElem, value);
pElem++;
}
}
}

static void Main() {
byte[] arr = new byte[] {1, 2, 3, 4, 5};
WriteLocations(arr);
}
}

shows an unsafe method named WriteLocations that fixes an array instance and uses pointer manipulation to iterate over the elements. The index, value, and location of each array element are written to the console. One possible example of output:

arr[0] at 0x8E0360 is 1
arr[1] at 0x8E0361 is 2
arr[2] at 0x8E0362 is 3
arr[3] at 0x8E0363 is 4
arr[4] at 0x8E0364 is 5

but of course the exact memory locations may be different in different executions of the application.

Expressions

C# includes unary operators, binary operators, and one ternary operator. The following table summarizes the operators, listing them in order of precedence from highest to lowest:

Section Category Operators
Error! Reference source not found. Primary x.y f(x) a[x] x++ x– new

typeof checked unchecked

Error! Reference source not found. Unary + – ! ~ ++x –x (T)x
Error! Reference source not found. Multiplicative * / %
Error! Reference source not found. Additive + –
Error! Reference source not found. Shift << >>
Error! Reference source not found. Relational and type testing < > <= >= is as
Error! Reference source not found. Equality == !=
Error! Reference source not found. Logical AND &
Error! Reference source not found. Logical XOR ^
Error! Reference source not found. Logical OR |
Error! Reference source not found. Conditional AND &&
Error! Reference source not found. Conditional OR ||
Error! Reference source not found. Conditional ?:
Error! Reference source not found. Assignment = *= /= %= += -= <<= >>= &= ^= |=

When an expression contains multiple operators, the precedence of the operators controls the order in which the individual operators are evaluated. For example, the expression x + y * z is evaluated as x + (y * z) because the * operator has higher precedence than the + operator.

When an operand occurs between two operators with the same precedence, the associativity of the operators controls the order in which the operations are performed:

Except for the assignment operators, all binary operators are left-associative, meaning that operations are performed from left to right. For example, x + y + z is evaluated as (x + y) + z.

The assignment operators and the conditional operator (?:) are right-associative, meaning that operations are performed from right to left. For example, x = y = z is evaluated as x = (y = z).

Precedence and associativity can be controlled using parentheses. For example, x + y * z first multiplies y by z and then adds the result to x, but (x + y) * z first adds x and y and then multiplies the result by z.

Statements

C# borrows most of its statements directly from C and C++, though there are some noteworthy additions and modifications. The table below lists the kinds of statements that can be used, and provides an example for each.

Statement Example
Statement lists and block statements static void Main() {
F();
G();
{
H();
I();
}
}
Labeled statements and goto statements static void Main(string[] args) {
if (args.Length == 0)
goto done;
Console.WriteLine(args.Length); 

done:
Console.WriteLine(“Done”);
}

Local constant declarations static void Main() {
const float pi = 3.14f;
const int r = 123;
Console.WriteLine(pi * r * r);
}
Local variable declarations static void Main() {
int a;
int b = 2, c = 3;
a = 1;
Console.WriteLine(a + b + c);
}
Expression statements static int F(int a, int b) {
return a + b;

static void Main() {
F(1, 2); // Expression statement
}

if statements static void Main(string[] args) {
if (args.Length == 0)
Console.WriteLine(“No args”);
else
Console.WriteLine(“Args”);
}
switch statements static void Main(string[] args) {
switch (args.Length) {
case 0:
Console.WriteLine(“No args”);
break;
case 1:
Console.WriteLine(“One arg “);
break;
default:
int n = args.Length;
Console.WriteLine(“{0} args”, n);
break;
}
}
while statements static void Main(string[] args) {
int i = 0;
while (i < args.Length) {
Console.WriteLine(args[i]);
i++;
}
}
do statements static void Main() {
string s;
do { s = Console.ReadLine(); }
while (s != “Exit”);
}
for statements static void Main(string[] args) {
for (int i = 0; i < args.length; i++)
Console.WriteLine(args[i]);
}
foreach statements static void Main(string[] args) {
foreach (string s in args)
Console.WriteLine(s);
}
break statements static void Main(string[] args) {
int i = 0;
while (true) {
if (i == args.Length)
break;
Console.WriteLine(args[i++]);
}
}
continue statements static void Main(string[] args) {
int i = 0;
while (true) {
Console.WriteLine(args[i++]);
if (i < args.Length)
continue;
break;
}
}
return statements static int F(int a, int b) {
return a + b;

static void Main() {
Console.WriteLine(F(1, 2));
return;
}

throw statements and try statements static int F(int a, int b) {
if (b == 0)
throw new Exception(“Divide by zero”);
return a / b;

static void Main() {
try {
Console.WriteLine(F(5, 0));
}
catch(Exception e) {
Console.WriteLine(“Error”);
}
}

checked and unchecked statements static void Main() {
int x = Int32.MaxValue; 

Console.WriteLine(x + 1); // Overflow

checked {
Console.WriteLine(x + 1); // Exception
}

unchecked {
Console.WriteLine(x + 1); // Overflow
}
}

lock statements static void Main() {
A a = …;
lock(a) {
a.P = a.P + 1;
}
}
using statements static void Main() {
using (Resource r = new Resource()) {
r.F();
}
}

Classes

Class declarations define new reference types. A class can inherit from another class, and can implement interfaces.

Class members can include constants, fields, methods, properties, events, indexers, operators, instance constructors, destructors, static constructors, and nested type declarations. Each member has an associated accessibility, which controls the regions of program text that are able to access the member. There are five possible forms of accessibility. These are summarized in the table below.

Form Intuitive meaning
public Access not limited
protected Access limited to the containing class or types derived from the containing class
internal Access limited to this program
protected internal Access limited to this program or types derived from the containing class
private Access limited to the containing type

The example

class MyClass
{
public MyClass() {
Console.WriteLine(“Instance constructor”);
}

public MyClass(int value) {
MyField = value;
Console.WriteLine(“Instance constructor”);
}

~MyClass() {
Console.WriteLine(“Destructor”);
}

public const int MyConst = 12;

public int MyField = 34;

public void MyMethod(){
Console.WriteLine(“MyClass.MyMethod”);
}

public int MyProperty {
get {
return MyField;
}

set {
MyField = value;
}
}

public int this[int index] {
get {
return 0;
}

set {
Console.WriteLine(“this[{0}] = {1}”, index, value);
}
}

public event EventHandler MyEvent;

public static MyClass operator+(MyClass a, MyClass b) {
return new MyClass(a.MyField + b.MyField);
}

internal class MyNestedClass
{}
}

shows a class that contains each kind of member. The example

class Test
{
static void Main() {
// Instance constructor usage
MyClass a = new MyClass();
MyClass b = new MyClass(123);

// Constant usage
Console.WriteLine(“MyConst = {0}”, MyClass.MyConst);

// Field usage
a.MyField++;
Console.WriteLine(“a.MyField = {0}”, a.MyField);

// Method usage
a.MyMethod();

// Property usage
a.MyProperty++;
Console.WriteLine(“a.MyProperty = {0}”, a.MyProperty);

// Indexer usage
a[3] = a[1] = a[2];
Console.WriteLine(“a[3] = {0}”, a[3]);

// Event usage
a.MyEvent += new EventHandler(MyHandler);

// Overloaded operator usage
MyClass c = a + b;
}

static void MyHandler(object sender, EventArgs e) {
Console.WriteLine(“Test.MyHandler”);
}

internal class MyNestedClass
{}
}

shows uses of these members.

Constants

A constant is a class member that represents a constant value: a value that can be computed at compile-time. Constants are permitted to depend on other constants within the same program as long as there are no circular dependencies. The rules governing constant expressions are defined in §Error! Reference source not found.. The example

class Constants
{
public const int A = 1;
public const int B = A + 1;
}

shows a class named Constants that has two public constants.

Even though constants are considered static members, a constant declaration neither requires nor allows the static modifier. Constants can be accessed through the class, as in

class Test
{
static void Main() {
Console.WriteLine(“{0}, {1}”, Constants.A, Constants.B);
}
}

which prints out the values of Constants.A and Constants.B.

Fields

A field is a member that represents a variable associated with an object or class. The example

class Color
{
internal ushort redPart;
internal ushort bluePart;
internal ushort greenPart;

public Color(ushort red, ushort blue, ushort green) {
redPart = red;
bluePart = blue;
greenPart = green;
}


}

shows a Color class that has internal instance fields named redPart, bluePart, and greenPart. Fields can also be static, as shown in the example

class Color
{
public static Color Red = new Color(0xFF, 0, 0);
public static Color Blue = new Color(0, 0xFF, 0);
public static Color Green = new Color(0, 0, 0xFF);
public static Color White = new Color(0xFF, 0xFF, 0xFF);

}

which shows static fields for Red, Blue, Green, and White.

The use of static fields in this manner is not ideal. The fields are initialized at some point before they are used, but after this initialization there is nothing to stop a client from changing them. Such a modification could cause unpredictable errors in other programs that use Color and assume that the values do not change. Readonly fields can be used to prevent such problems. Assignments to a readonly field can only occur as part of the declaration, or in an instance constructor or static constructor in the same class. A static readonly field can be assigned in a static constructor, and a non-static readonly field can be assigned in an instance constructor. Thus, the Color class can be enhanced by adding the readonly modifier to the static fields:

class Color
{
internal ushort redPart;
internal ushort bluePart;
internal ushort greenPart;

public Color(ushort red, ushort blue, ushort green) {
redPart = red;
bluePart = blue;
greenPart = green;
}

public static readonly Color Red = new Color(0xFF, 0, 0);
public static readonly Color Blue = new Color(0, 0xFF, 0);
public static readonly Color Green = new Color(0, 0, 0xFF);
public static readonly Color White = new Color(0xFF, 0xFF, 0xFF);
}

Methods

A method is a member that implements a computation or action that can be performed by an object or class. Methods have a list of formal parameters (which may be empty), a return value (unless the method’s return-type is void), and are either static or non-static. Static methods are accessed through the class. Non-static methods, which are also called instance methods, are accessed through instances of the class. The example

public class Stack
{
public static Stack Clone(Stack s) {…}

public static Stack Flip(Stack s) {…}

public object Pop() {…}

public void Push(object o) {…}

public override string ToString() {…}


}

class Test
{
static void Main() {
Stack s = new Stack();
for (int i = 1; i < 10; i++)
s.Push(i);

Stack flipped = Stack.Flip(s);

Stack cloned = Stack.Clone(s);

Console.WriteLine(“Original stack: ” + s.ToString());
Console.WriteLine(“Flipped stack: ” + flipped.ToString());
Console.WriteLine(“Cloned stack: ” + cloned.ToString());
}
}

shows a Stack that has several static methods (Clone and Flip) and several instance methods (Push, Pop, and ToString).

Methods can be overloaded, which means that multiple methods may have the same name so long as they have unique signatures. The signature of a method consists of the name of the method and the number, modifiers, and types of its formal parameters. The signature of a method does not include the return type. The example

class Test
{
static void F() {
Console.WriteLine(“F()”);
}

static void F(object o) {
Console.WriteLine(“F(object)”);
}

static void F(int value) {
Console.WriteLine(“F(int)”);
}

static void F(ref int value) {
Console.WriteLine(“F(ref int)”);
}

static void F(int a, int b) {
Console.WriteLine(“F(int, int)”);
}

static void F(int[] values) {
Console.WriteLine(“F(int[])”);
}

static void Main() {
F();
F(1);
int i = 10;
F(ref i);
F(out i);
F((object)1);
F(1, 2);
F(new int[] {1, 2, 3});
}
}

shows a class with a number of methods named F. The output produced is

F()
F(int)
F(ref int)
F(object)
F(int, int)
F(int[])

Properties

A property is a member that provides access to a characteristic of an object or a class. Examples of properties include the length of a string, the size of a font, the caption of a window, the name of a customer, and so on. Properties are a natural extension of fields. Both are named members with associated types, and the syntax for accessing fields and properties is the same. However, unlike fields, properties do not denote storage locations. Instead, properties have accessors that specify the statements to be executed when their values are read or written.

Properties are defined with property declarations. The first part of a property declaration looks quite similar to a field declaration. The second part includes a get accessor and/or a set accessor. In the example below, the Button class defines a Caption property.

public class Button
{
private string caption;

public string Caption {
get {
return caption;
}

set {
caption = value;
Repaint();
}
}
}

Properties that can be both read and written, such as Caption, include both get and set accessors. The get accessor is called when the property’s value is read; the set accessor is called when the property’s value is written. In a set accessor, the new value for the property is made available via an implicit parameter named value.

The declaration of properties is real value of properties is seen when they are used. For example, the Caption property can be read and written in the same way that fields can be read and written:

Button b = new Button();

b.Caption = “ABC”; // set; causes repaint

string s = b.Caption; // get

b.Caption += “DEF”; // get & set; causes repaint

Events

An event is a member that enables an object or class to provide notifications. A class defines an event by providing an event declaration, which resembles a field declaration, though with an added event keyword, and an optional set of event accessors. The type of this declaration must be a delegate type.

An instance of a delegate type encapsulates one or more callable entities. For instance methods, a callable entity consists of an instance and a method on that instance. For static methods, a callable entity consists of just a method. Given a delegate instance and an appropriate set of arguments, one can invoke all of that delegate instance’s methods with that set of arguments.

In the example

public delegate void EventHandler(object sender, System.EventArgs e);

public class Button
{
public event EventHandler Click;

public void Reset() {
Click = null;
}
}

the Button class defines a Click event of type EventHandler. Inside the Button class, the Click member is exactly like a private field of type EventHandler. However, outside the Button class, the Click member can only be used on the left hand side of the += and -= operators. The += operator adds a handler for the event, and the -= operator removes a handler for the event. The example

public class Form1
{
public Form1() {
// Add Button1_Click as an event handler for Button1’s Click event
Button1.Click += new EventHandler(Button1_Click);
}

Button Button1 = new Button();

void Button1_Click(object sender, EventArgs e) {
Console.WriteLine(“Button1 was clicked!”);
}

public void Disconnect() {
Button1.Click -= new EventHandler(Button1_Click);
}
}

shows a Form1 class that adds Button1_Click as an event handler for Button1’s Click event. In the Disconnect method, the event handler is removed.

For a simple event declaration such as

public event EventHandler Click;

the compiler automatically provides the implementation underlying the += and -= operators.

An implementer who wants more control can get it by explicitly providing add and remove accessors. For example, the Button class could be rewritten as follows:

public class Button
{
private EventHandler handler;

public event EventHandler Click {

add { handler += value; }

remove { handler -= value; }
}
}

This change has no effect on client code, but allows the Button class more implementation flexibility. For example, the event handler for Click need not be represented by a field.

Operators

An operator is a member that defines the meaning of an expression operator that can be applied to instances of the class. There are three kinds of operators that can be defined: unary operators, binary operators, and conversion operators.

The following example defines a Digit type that represents decimal digits—integral values between 0 and 9.

public struct Digit
{
byte value;

public Digit(byte value) {
if (value < 0 || value > 9) throw new ArgumentException();
this.value = value;
}

public Digit(int value): this((byte) value) {}

public static implicit operator byte(Digit d) {
return d.value;
}

public static explicit operator Digit(byte b) {
return new Digit(b);
}

public static Digit operator+(Digit a, Digit b) {
return new Digit(a.value + b.value);
}

public static Digit operator-(Digit a, Digit b) {
return new Digit(a.value – b.value);
}

public static bool operator==(Digit a, Digit b) {
return a.value == b.value;
}

public static bool operator!=(Digit a, Digit b) {
return a.value != b.value;
}

public override bool Equals(object value) {
return this == (Digit) value;
}

public override int GetHashCode() {
return value.GetHashCode();
}

public override string ToString() {
return value.ToString();
}
}

class Test
{
static void Main() {
Digit a = (Digit) 5;
Digit b = (Digit) 3;
Digit plus = a + b;
Digit minus = a – b;
bool equals = (a == b);
Console.WriteLine(“{0} + {1} = {2}”, a, b, plus);
Console.WriteLine(“{0} – {1} = {2}”, a, b, minus);
Console.WriteLine(“{0} == {1} = {2}”, a, b, equals);
}
}

The Digit type defines the following operators:

An implicit conversion operator from Digit to byte.

An explicit conversion operator from byte to Digit.

An addition operator that adds two Digit values and returns a Digit value.

A subtraction operator that subtracts one Digit value from another, and returns a Digit value.

The equality (==) and inequality (!=) operators, which compare two Digit values.

Indexers

An indexer is a member that enables an object to be indexed in the same way as an array. Whereas properties enable field-like access, indexers enable array-like access.

As an example, consider the Stack class presented earlier. The designer of this class might want to expose array-like access so that it is possible to inspect or alter the items on the stack without performing unnecessary Push and Pop operations. That is, Stack is implemented as a linked list, but it also provides the convenience of array access.

Indexer declarations are similar to property declarations, with the main differences being that indexers are nameless (the “name” used in the declaration is this, since this is being indexed) and that indexers include indexing parameters. The indexing parameters are provided between square brackets. The example

public class Stack
{
private Node GetNode(int index) {
Node temp = first;
while (index > 0) {
temp = temp.Next;
index–;
}
return temp;
}

public object this[int index] {
get {
if (!ValidIndex(index))
throw new Exception(“Index out of range.”);
else
return GetNode(index).Value;
}

set {
if (!ValidIndex(index))
throw new Exception(“Index out of range.”);
else
GetNode(index).Value = value;
}
}


}

class Test
{
static void Main() {
Stack s = new Stack();

s.Push(1);
s.Push(2);
s.Push(3);

s[0] = 33; // Changes the top item from 3 to 33
s[1] = 22; // Changes the middle item from 2 to 22
s[2] = 11; // Changes the bottom item from 1 to 11
}
}

shows an indexer for the Stack class.

Instance constructors

An instance constructor is a member that implements the actions required to initialize an instance of a class.

The example

class Point
{
public double x, y;

public Point() {
this.x = 0;
this.y = 0;
}

public Point(double x, double y) {
this.x = x;
this.y = y;
}

public static double Distance(Point a, Point b) {
double xdiff = a.x – b.x;
double ydiff = a.y – b.y;
return Math.Sqrt(xdiff * xdiff + ydiff * ydiff);
}

public override string ToString() {
return string.Format(“({0}, {1})”, x, y);
}
}

class Test
{
static void Main() {
Point a = new Point();
Point b = new Point(3, 4);
double d = Point.Distance(a, b);
Console.WriteLine(“Distance from {0} to {1} is {2}”, a, b, d);
}
}

shows a Point class that provides two public instance constructors. One instance constructor takes no arguments, and the other takes two double arguments.

If no instance constructor is supplied for a class, then an empty instance constructor with no parameters is automatically provided.

Destructors

A destructor is a member that implements the actions required to destruct an instance of a class. Destructors cannot have parameters, cannot have accessibility modifiers, and cannot be called explicitly. The destructor for an instance is called automatically during garbage collection.

The example

class Point
{
public double x, y;

public Point(double x, double y) {
this.x = x;
this.y = y;
}

~Point() {
Console.WriteLine(“Destructed {0}”, this);
}

public override string ToString() {
return string.Format(“({0}, {1})”, x, y);
}
}

shows a Point class with a destructor.

Static constructors

A static constructor is a member that implements the actions required to initialize a class. Static constructors cannot have parameters, cannot have accessibility modifiers, and cannot be called explicitly. The static constructor for a class is called automatically.

The example

using Personnel.Data;

class Employee
{
private static DataSet ds;

static Employee() {
ds = new DataSet(…);
}

public string Name;
public decimal Salary;


}

shows an Employee class with a static constructor that initializes a static field.

Inheritance

Classes support single inheritance, and the type object is the ultimate base class for all classes.

The classes shown in earlier examples all implicitly derive from object. The example

class A
{
public void F() { Console.WriteLine(“A.F”); }
}

shows a class A that implicitly derives from object. The example

class B: A
{
public void G() { Console.WriteLine(“B.G”); }
}

class Test
{
static void Main() {
B b = new B();
b.F(); // Inherited from A
b.G(); // Introduced in B

A a = b; // Treat a B as an A
a.F();
}
}

shows a class B that derives from A. The class B inherits A’s F method, and introduces a G method of its own.

Methods, properties, and indexers can be virtual, which means that their implementation can be overridden in derived classes. The example

class A
{
public virtual void F() { Console.WriteLine(“A.F”); }
}

class B: A
{
public override void F() {
base.F();
Console.WriteLine(“B.F”);
}
}

class Test
{
static void Main() {
B b = new B();
b.F();

A a = b;
a.F();
}
}

shows a class A with a virtual method F, and a class B that overrides F. The overriding method in B contains a call, base.F(), which calls the overridden method in A.

A class can indicate that it is incomplete, and is intended only as a base class for other classes, by including the abstract modifier. Such a class is called an abstract class. An abstract class can specify abstract members—members that a non-abstract derived class must implement. The example

abstract class A
{
public abstract void F();
}

class B: A
{
public override void F() { Console.WriteLine(“B.F”); }
}

class Test
{
static void Main() {
B b = new B();
b.F();

A a = b;
a.F();
}
}

introduces an abstract method F in the abstract class A. The non-abstract class B provides an implementation for this method.

Structs

The list of similarities between classes and structs is long—structs can implement interfaces, and can have the same kinds of members as classes. Structs differ from classes in several important ways, however: structs are value types rather than reference types, and inheritance is not supported for structs. Struct values are stored “on the stack” or “in-line”. Careful programmers can sometimes enhance performance through judicious use of structs.

For example, the use of a struct rather than a class for a Point can make a large difference in the number of memory allocations performed at runtime. The program below creates and initializes an array of 100 points. With Point implemented as a class, 101 separate objects are instantiated—one for the array and one each for the 100 elements.

class Point
{
public int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

class Test
{
static void Main() {
Point[] points = new Point[100];
for (int i = 0; i < 100; i++)
points[i] = new Point(i, i*i);
}
}

If Point is instead implemented as a struct, as in

struct Point
{
public int x, y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}
}

then only one object is instantiated—the one for the array. The Point instances are allocated in-line within the array. This optimization can be misused. Using structs instead of classes can also make an application run slower, or take up more memory, as passing a struct instance as a value parameter causes a copy of the struct to be created. There is no substitute for careful data structure and algorithm design.

Interfaces

An interface defines a contract. A class or struct that implements an interface must adhere to its contract. Interfaces can contain methods, properties, events and indexers.

The example

interface IExample
{
string this[int index] { get; set; }

event EventHandler E;

void F(int value);

string P { get; set; }
}

public delegate void EventHandler(object sender, EventArgs e);

shows an interface that contains an indexer, an event E, a method F, and a property P.

Interfaces may employ multiple inheritance. In the example

interface IControl
{
void Paint();
}

interface ITextBox: IControl
{
void SetText(string text);
}

interface IListBox: IControl
{
void SetItems(string[] items);
}

interface IComboBox: ITextBox, IListBox {}

the interface IComboBox inherits from both ITextBox and IListBox.

Classes and structs can implement multiple interfaces. In the example

interface IDataBound
{
void Bind(Binder b);
}

public class EditBox: Control, IControl, IDataBound
{
public void Paint() {…}

public void Bind(Binder b) {…}
}

the class EditBox derives from the class Control and implements both IControl and IDataBound.

In previous example, the Paint method from the IControl interface and the Bind method from IDataBound interface are implemented using public members on the EditBox class. C# provides an alternative way of implementing these methods that allows the implementing class to avoid having these members be public. Interface members can be implemented using a qualified name. For example, the EditBox class could instead be implemented by providing IControl.Paint and IDataBound.Bind methods.

public class EditBox: IControl, IDataBound
{
void IControl.Paint() {…}

void IDataBound.Bind(Binder b) {…}
}

Interface members implemented in this way are called explicit interface members because each member explicitly designates the interface member being implemented. Explicit interface members can only be called via the interface. For example, the EditBox’s implementation of the Paint method can be called only by casting to the IControl interface.

class Test
{
static void Main() {
EditBox editbox = new EditBox();
editbox.Paint(); // error: no such method

IControl control = editbox;
control.Paint(); // calls EditBox’s Paint implementation
}
}

Delegates

Delegates enable scenarios that some other languages have addressed with function pointers. However, unlike function pointers, delegates are object-oriented, type-safe, and secure.

A delegate declaration defines a class that is derived from the class System.Delegate. A delegate instance encapsulates one or more methods, each of which is referred to as a callable entity. For instance methods, a callable entity consists of an instance and a method on that instance. For static methods, a callable entity consists of just a method. Given a delegate instance and an appropriate set of arguments, one can invoke all of that delegate instance’s methods with that set of arguments.

An interesting and useful property of a delegate instance is that it does not know or care about the classes of the methods it encapsulates; all that matters is that those methods be compatible (§Error! Reference source not found.) with the delegate’s type. This makes delegates perfectly suited for “anonymous” invocation. This is a powerful capability.

There are three steps in defining and using delegates: declaration, instantiation, and invocation. Delegates are declared using delegate declaration syntax. The example

delegate void SimpleDelegate();

declares a delegate named SimpleDelegate that takes no arguments and returns void.

The example

class Test
{
static void F() {
System.Console.WriteLine(“Test.F”);
}

static void Main() {
SimpleDelegate d = new SimpleDelegate(F);
d();
}
}

creates a SimpleDelegate instance and then immediately calls it.

There is not much point in instantiating a delegate for a method and then immediately calling it via the delegate, as it would be simpler to call the method directly. Delegates really show their usefulness when their anonymity is used. The example

void MultiCall(SimpleDelegate d, int count) {
for (int i = 0; i < count; i++)
d();
}
}

shows a MultiCall method that repeatedly calls a SimpleDelegate. The MultiCall method doesn’t know or care about the type of target method for the SimpleDelegate, what accessibility the method has, or whether or not the method is static. All that matters is that the target method is compatible (§Error! Reference source not found.) with SimpleDelegate.

Enums

An enum type declaration defines a type name for a related group of symbolic constants. Enums are used for “multiple choice” scenarios, in which a runtime decision is made from a fixed number of choices that are known at compile-time.

The example

enum Color
{
Red,
Blue,
Green
}

class Shape
{
public void Fill(Color color) {
switch(color) {
case Color.Red:

break;

case Color.Blue:

break;

case Color.Green:

break;

default:
break;
}
}
}

shows a Color enum and a method that uses this enum. The signature of the Fill method makes it clear that the shape can be filled with one of the given colors.

The use of enums is superior to the use of integer constants—as is common in languages without enums—because the use of enums makes the code more readable and self-documenting. The self-documenting nature of the code also makes it possible for the development tool to assist with code writing and other “designer” activities. For example, the use of Color rather than int for a parameter type enables smart code editors to suggest Color values.

Namespaces and assemblies

The programs presented so far have stood on their own except for dependence on a few system-provided classes such as System.Console. It is far more common, however, for real-world applications to consist of several different pieces, each compiled separately. For example, a corporate application might depend on several different components, including some developed internally and some purchased from independent software vendors.

Namespaces and assemblies enable this component-based system. Namespaces provide a logical organizational system. Namespaces are used both as an “internal” organization system for a program, and as an “external” organization system—a way of presenting program elements that are exposed to other programs.

Assemblies are used for physical packaging and deployment. An assembly may contain types, the executable code used to implement these types, and references to other assemblies.

There are two main kinds of assemblies: applications and libraries. Applications have a main entry point and usually have a file extension of .exe; libraries do not have a main entry point, and usually have a file extension of .dll.

To demonstrate the use of namespaces and assemblies, this section revisits the “hello, world” program presented earlier, and splits it into two pieces: a class library that provides messages and a console application that displays them.

The class library will contain a single class named HelloMessage. The example

// HelloLibrary.cs

namespace Microsoft.CSharp.Introduction
{
public class HelloMessage
{
public string Message {
get {
return “hello, world”;
}
}
}
}

shows the HelloMessage class in a namespace named Microsoft.CSharp.Introduction. The HelloMessage class provides a read-only property named Message. Namespaces can nest, and the declaration

namespace Microsoft.CSharp.Introduction
{…}

is shorthand for several levels of namespace nesting:

namespace Microsoft
{
namespace CSharp
{
namespace Introduction
{…}
}
}

The next step in the componentization of “hello, world” is to write a console application that uses the HelloMessage class. The fully qualified name for the class—Microsoft.CSharp.Introduction.HelloMessage—could be used, but this name is quite long and unwieldy. An easier way is to use a using namespace directive, which makes it possible to use all of the types in a namespace without qualification. The example

// HelloApp.cs

using Microsoft.CSharp.Introduction;

class HelloApp
{
static void Main() {
HelloMessage m = new HelloMessage();
System.Console.WriteLine(m.Message);
}
}

shows a using namespace directive that refers to the Microsoft.CSharp.Introduction namespace. The occurrences of HelloMessage are shorthand for Microsoft.CSharp.Introduction.HelloMessage.

C# also enables the definition and use of aliases. A using alias directive defines an alias for a type. Such aliases can be useful in situation in which name collisions occur between two class libraries, or when a small number of types from a much larger namespace are being used. The example

using MessageSource = Microsoft.CSharp.Introduction.HelloMessage;

shows a using alias directive that defines MessageSource as an alias for the HelloMessage class.

The code we have written can be compiled into a class library containing the class HelloMessage and an application containing the class HelloApp. The details of this compilation step might differ based on the compiler or tool being used. Using the command-line compiler provided in Visual Studio .NET, the correct invocations are

csc /target:library HelloLibrary.cs

which produces a class library HelloLibrary.dll and

csc /reference:HelloLibrary.dll HelloApp.cs

which produces the application HelloApp.exe.

Versioning

Versioning is the process of evolving a component over time in a compatible manner. A new version of a component is source compatible with a previous version if code that depends on the previous version can, when recompiled, work with the new version. In contrast, a new version of a component is binary compatible if an application that depended on the old version can, without recompilation, work with the new version.

Most languages do not support binary compatibility at all, and many do little to facilitate source compatibility. In fact, some languages contain flaws that make it impossible, in general, to evolve a class over time without breaking at least some client code.

As an example, consider the situation of a base class author who ships a class named Base. In the first version, Base contains no F method. A component named Derived derives from Base, and introduces an F. This Derived class, along with the class Base on which it depends, is released to customers, who deploy to numerous clients and servers.

// Author A
namespace A
{
public class Base // version 1
{
}
}

// Author B
namespace B
{
class Derived: A.Base
{
public virtual void F() {
System.Console.WriteLine(“Derived.F”);
}
}
}

So far, so good. But now the versioning trouble begins. The author of Base produces a new version, and adds its own F method.

// Author A
namespace A
{
public class Base // version 2
{
public virtual void F() { // added in version 2
System.Console.WriteLine(“Base.F”);
}
}
}

This new version of Base should be both source and binary compatible with the initial version. (If it weren’t possible to simply add a method then a base class could never evolve.) Unfortunately, the new F in Base makes the meaning of Derived’s F unclear. Did Derived mean to override Base’s F? This seems unlikely, since when Derived was compiled, Base did not even have an F! Further, if Derived’s F does override Base’s F, then it must adhere to the contract specified by Base—a contract that was unspecified when Derived was written? In some cases, this is impossible. For example, the contract of Base’s F might require that overrides of it always call the base. Derived’s F could not possibly adhere to such a contract.

C# addresses this versioning problem by requiring developers to clearly state their intent. In the original code example, the code was clear, since Base did not even have an F. Clearly, Derived’s F is intended as a new method rather than an override of a base method, since no base method named F exists.

If Base adds an F and ships a new version, then the intent of a binary version of Derived is still clear—Derived’s F is semantically unrelated, and should not be treated as an override.

However, when Derived is recompiled, the meaning is unclear—the author of Derived may intend its F to override Base’s F, or to hide it. Since the intent is unclear, the compiler produces a warning, and by default makes Derived’s F hide Base’s F. This course of action duplicates the semantics for the case in which Derived is not recompiled. The warning that is generated alerts Derived’s author to the presence of the F method in Base.

If Derived’s F is semantically unrelated to Base’s F, then Derived’s author can express this intent—and, in effect, turn off the warning—by using the new keyword in the declaration of F.

// Author A
namespace A
{
public class Base // version 2
{
public virtual void F() { // added in version 2
System.Console.WriteLine(“Base.F”);
}
}
}

// Author B
namespace B
{
class Derived: A.Base // version 2a: new
{
new public virtual void F() {
System.Console.WriteLine(“Derived.F”);
}
}
}

On the other hand, Derived’s author might investigate further, and decide that Derived’s F should override Base’s F. This intent can be specified by using the override keyword, as shown below.

// Author A
namespace A
{
public class Base // version 2
{
public virtual void F() { // added in version 2
System.Console.WriteLine(“Base.F”);
}
}
}

// Author B
namespace B
{
class Derived: A.Base // version 2b: override
{
public override void F() {
base.F();
System.Console.WriteLine(“Derived.F”);
}
}
}

The author of Derived has one other option, and that is to change the name of F, thus completely avoiding the name collision. Though this change would break source and binary compatibility for Derived, the importance of this compatibility varies depending on the scenario. If Derived is not exposed to other programs, then changing the name of F is likely a good idea, as it would improve the readability of the program—there would no longer be any confusion about the meaning of F.

Attributes

C# is an imperative language, but like all imperative languages it does have some declarative elements. For example, the accessibility of a method in a class is specified by declaring it public, protected, internal, protected internal, or private. Through its support for attributes, C# generalizes this capability, so that programmers can invent new kinds of declarative information, attach this declarative information to various program entities, and retrieve this declarative information at run-time. Programs specify this additional declarative information by defining and using attributes.

For instance, a framework might define a HelpAttribute attribute that can be placed on program elements such as classes and methods, enabling developers to provide a mapping from program elements to documentation for them. The example

[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute: Attribute
{
public HelpAttribute(string url) {
this.url = url;
}

public string Topic = null;

private string url;

public string Url {
get { return url; }
}
}

defines an attribute class named HelpAttribute, or Help for short, that has one positional parameter (string url) and one named argument (string Topic). Positional parameters are defined by the formal parameters for public instance constructors of the attribute class, and named parameters are defined by public non-static read-write fields and properties of the attribute class.

The example

[Help(“http://www.microsoft.com/…/Class1.htm&#8221;)]
public class Class1
{
[Help(“http://www.microsoft.com/…/Class1.htm&#8221;, Topic = “F”)]
public void F() {}
}

shows several uses of the attribute.

Attribute information for a given program element can be retrieved at run-time by using reflection support. The example

class Test
{
static void Main() {
Type type = typeof(Class1);
object[] arr = type.GetCustomAttributes(typeof(HelpAttribute), true);
if (arr.Length == 0)
Console.WriteLine(“Class1 has no Help attribute.”);
else {
HelpAttribute ha = (HelpAttribute) arr[0];
Console.WriteLine(“Url = {0}, Topic = {1}”, ha.Url, ha.Topic);
}
}
}

Checks to see if Class1 has a Help attribute, and writes out the associated Topic and Url values if the attribute is present.

Reference : Dilip Kumar Jena ( https://mstechexplore.wordpress.com )

Advertisements

4 thoughts on “Complete CSharp Information – What is CSharp Language in DOT.NET

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s