Sei sulla pagina 1di 586

MSDN Library:

Visual C# Programming Guide

Content

Inside a C# Program

Main() and Command-Line Arguments (C# Programming Guide)

Types (C# Programming Guide)

Arrays (C# Programming Guide)

Strings (C# Programming Guide)

Statements, Expressions, and Operators (C# Programming Guide)

Classes and Structs (C# Programming Guide)

Properties (C# Programming Guide)

Interfaces (C# Programming Guide)

Indexers (C# Programming Guide)

Enumeration Types (C# Programming Guide)

Delegates (C# Programming Guide)

Events (C# Programming Guide)

Generics (C# Programming Guide)

LINQ Query Expressions (C# Programming Guide)

Lambda Expressions (C# Programming Guide)

Iterators (C# Programming Guide)

Namespaces (C# Programming Guide)

Nullable Types (C# Programming Guide)

Unsafe Code and Pointers (C# Programming Guide)

XML Documentation Comments (C# Programming Guide)

Inside a C# Program

Hello World -- Your First Program

The following procedure creates a C# version of the traditional "Hello World!" program. The program displays the string Hello World!

Note

Your computer might show different names or locations for some of the Visual Studio user interface elements in the following instructions. The Visual Studio edition that you have and the settings that you use determine these elements. For more information, see Visual Studio Settings.

To create and run a console application

1. Start Visual Studio.

2. On the menu bar, choose File, New, Project.

3. In the Installed Templates pane, expand Visual C#, and then choose Windows.

4. In the center pane, choose Console Application.

5. In the Name box, specify a name for your project, and then choose the OK button. The new project appears in Solution Explorer.

6. If Program.cs isn't open in the Code Editor, open the shortcut menu for Program.cs in Solution Explorer, and then choose View Code.

7. Replace the contents of Program.cs with the following code.

C#

// A Hello World! program in C#. using System; namespace HelloWorld

{

 

class Hello

{

static void Main()

{

Console.WriteLine("Hello World!");

// Keep the console window open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey();

}

}

}

8. Choose the F5 key to run the project. A Command Prompt window appears that contains the line Hello World! Next, the important parts of this program are examined.

Comments

The first line contains a comment. The characters // convert the rest of the line to a comment.

C#

// A Hello World! program in C#.

You can also comment out a block of text by enclosing it between the /* and */ characters. This is shown in the following example.

C#

/* A "Hello World!" program in C#. This program displays the string "Hello World!" on the screen. */

Main Method

A C# console application must contain a Main method, in which control starts and ends. The Main method is where you create objects and execute other methods. The Main method is a static (C# Reference) method that resides inside a class or a struct. In the previous "Hello World!" example, it resides in a class named Hello. You can declare the Main method in one of the following ways:

It can return void.

C#

static void Main()

{

}

//

It can also return an integer.

C#

static int Main()

{

 

//

return 0;

}

With either of the return types, it can take arguments.

C#

static void Main(string[] args)

{

}

-or-

//

C#

static int Main(string[] args)

{

 

//

return 0;

}

The parameter of the Main method, args, is a string array that contains the command-line arguments used to invoke the program. Unlike in C++, the array does not include the name of the executable (exe) file.

For more information about how to use command-line arguments, see the examples in Main() and Command-Line Arguments (C# Programming Guide) and How to: Create and Use Assemblies Using the Command Line (C# and Visual Basic). The call to ReadKey at the end of the Main method prevents the console window from closing before you have a chance to read the output when you run your program in debug mode, by pressing F5.

Input and Output

C# programs generally use the input/output services provided by the run-time library of the .NET Framework. The statement System.Console.WriteLine("Hello World!"); uses the WriteLine method. This is one of the output methods of the Console class in the run-time library. It displays its string parameter on the standard output stream followed by a new line. Other Console methods are available for different input and output operations. If you include the using System; directive at the beginning of the program, you can directly use the System classes and methods without fully qualifying them. For example, you can call Console.WriteLine instead of System.Console.WriteLine:

C#

using System;

C#

Console.WriteLine("Hello World!");

For more information about input/output methods, see System.IO.

Command-Line Compilation and Execution

You can compile the "Hello World!" program by using the command line instead of the Visual Studio Integrated Development Environment (IDE).

To compile and run from a command prompt

1. Paste the code from the preceding procedure into any text editor, and then save the file as a text file. Name the file Hello.cs. C# source code files use the extension .cs.

2. On the Start menu, expand Visual Studio Tools, and then choose the shortcut to open a Visual Studio Command Prompt window. As an alternative, you can follow the instructions in How to: Set Environment Variables to enable command-line builds from a standard Command Prompt window.

3. In the Visual Studio Command Prompt window, navigate to the folder that contains your Hello.cs file.

4. Enter the following command to compile Hello.cs. csc Hello.cs If your program has no compilation errors, an executable file that is named Hello.exe is created.

5. In the Visual Studio Command Prompt window, enter the following command to run the program:

Hello

General Structure of a C# Program

C# programs can consist of one or more files. Each file can contain zero or more namespaces. A namespace can contain types such as classes, structs, interfaces, enumerations, and delegates, in addition to other namespaces. The following is the skeleton of a C# program that contains all of these elements.

C#

// A skeleton of a C# program using System; namespace YourNamespace

{

 

class YourClass

{

}

struct YourStruct

{

}

interface IYourInterface

{

}

delegate int YourDelegate();

enum YourEnum

{

}

namespace YourNestedNamespace

{

struct YourStruct

{

}

}

class YourMainClass

{

static void Main(string[] args)

{

//Your program starts here

}

}

}

C# Coding Conventions

The C# Language Specification does not define a coding standard. However, the guidelines in this topic are used by Microsoft to develop samples and documentation. Coding conventions serve the following purposes:

They create a consistent look to the code, so that readers can focus on content, not layout.

They enable readers to understand the code more quickly by making assumptions based on previous experience.

They facilitate copying, changing, and maintaining the code.

They demonstrate C# best practices.

Naming Conventions

Naming guidelines are covered in Guidelines for Names.

You do not have to change the names of objects created by the Visual Studio designer tools to make them fit the guidelines.

In short examples that do not include using directives, use namespace qualifications. If you know that a namespace is imported by default in a project, you do not have to fully qualify the names from that namespace. Qualified names can be broken after a dot (.) if they are too long for a single line, as shown in the following example.

C#

var currentPerformanceCounterCategory = new System.Diagnostics. PerformanceCounterCategory();

Layout Conventions Good layout uses formatting to emphasize the structure of your code and to make the code easier to read. Microsoft examples and samples conform to the following conventions:

Use the default Code Editor settings (smart indenting, four-character indents, tabs saved as spaces). For more information, see Formatting, C#, Text Editor, Options Dialog Box.

Write only one statement per line.

Write only one declaration per line.

If continuation lines are not indented automatically, indent them one tab stop (four spaces).

Add at least one blank line between method definitions and property definitions.

Use parentheses to make clauses in an expression apparent, as shown in the following code.

C#

if ((val1 > val2) && (val1 > val3))

{

// Take appropriate action.

}

Commenting Conventions

Place the comment on a separate line, not at the end of a line of code.

Begin comment text with an uppercase letter.

End comment text with a period.

Insert one space between the comment delimiter (//) and the comment text, as shown in the following example.

C#

// The following declaration creates a query. It does not run // the query.

Do not create formatted blocks of asterisks around comments.

Language Guidelines

The following sections describe practices that the C# team follows to prepare code examples and samples.

String Data Type

Use the + operator to concatenate short strings, as shown in the following code.

C#

string displayName = nameList[n].LastName + ", " + nameList[n].FirstName;

To append strings in loops, especially when you are working with large amounts of text, use a StringBuilder object.

C#

var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala"; var manyPhrases = new StringBuilder(); for (var i = 0; i < 10000; i++)

{

manyPhrases.Append(phrase);

}

//Console.WriteLine("tra" + manyPhrases);

Implicitly Typed Local Variables

Use implicit typing for local variables when the type of the variable is obvious from the right side of the assignment, or when the precise type is not important.

C#

// When the type of a variable is clear from the context, use var // in the declaration. var var1 = "This is clearly a string."; var var2 = 27; var var3 = Convert.ToInt32(Console.ReadLine());

Do not use var when the type is not apparent from the right side of the assignment.

C#

// When the type of a variable is not clear from the context, use an // explicit type. int var4 = ExampleClass.ResultSoFar();

Do not rely on the variable name to specify the type of the variable. It might not be correct.

C#

// Naming the following variable inputInt is misleading. // It is a string. var inputInt = Console.ReadLine(); Console.WriteLine(inputInt);

Avoid the use of var in place of dynamic.

Use implicit typing to determine the type of the loop variable in for and foreach loops. The following example uses implicit typing in a for statement.

C#

var syllable = "ha"; var laugh = "";

for (var i = 0; i < 10; i++)

{

laugh += syllable; Console.WriteLine(laugh);

}

The following example uses implicit typing in a foreach statement.

C#

foreach (var ch in laugh)

{

if (ch == 'h') Console.Write("H");

else

Console.Write(ch);

}

Console.WriteLine();

Unsigned Data Type

In general, use int rather than unsigned types. The use of int is common throughout C#, and it is easier to interact with other libraries when you use int.

Arrays

Use the concise syntax when you initialize arrays on the declaration line.

C#

// Preferred syntax. Note that you cannot use var here instead of string[]. string[] vowels1 = { "a", "e", "i", "o", "u" };

// If you use explicit instantiation, you can use var. var vowels2 = new string[] { "a", "e", "i", "o", "u" };

// If you specify an array size, you must initialize the elements one at a time. var vowels3 = new string[5];

vowels3[0] = "a"; vowels3[1] = "e"; // And so on.

Delegates

Use the concise syntax to create instances of a delegate type.

C#

// First, in class Program, define the delegate type and a method that // has a matching signature.

// Define the type. public delegate void Del(string message);

// Define a method that has a matching signature. public static void DelMethod(string str)

{

Console.WriteLine("DelMethod argument: {0}", str);

}

C#

// In the Main method, create an instance of Del.

// Preferred: Create an instance of Del by using condensed syntax. Del exampleDel2 = DelMethod;

// The following declaration uses the full syntax. Del exampleDel1 = new Del(DelMethod);

try-catch and using Statements in Exception Handling

Use a try-catch statement for most exception handling.

C#

static string GetValueFromArray(string[] array, int index)

{

 

try

{

return array[index];

}

catch (System.IndexOutOfRangeException ex)

{

Console.WriteLine("Index is out of range: {0}", index); throw;

}

}

Simplify your code by using the C# using statement. If you have a try-finally statement in which the only code in the finally block is a call to the Dispose method, use a using statement instead.

C#

// This try-finally statement only calls Dispose in the finally block. Font font1 = new Font("Arial", 10.0f);

try

{

byte charset = font1.GdiCharSet;

}

finally

{

if (font1 != null)

{

((IDisposable)font1).Dispose();

}

}

// You can do the same thing with a using statement. using (Font font2 = new Font("Arial", 10.0f))

{

byte charset = font2.GdiCharSet;

}

&& and || Operators

To avoid exceptions and increase performance by skipping unnecessary comparisons, use && instead of & and || instead of | when you perform comparisons, as shown in the following example.

C#

Console.Write("Enter a dividend: "); var dividend = Convert.ToInt32(Console.ReadLine());

Console.Write("Enter a divisor: "); var divisor = Convert.ToInt32(Console.ReadLine());

// If the divisor is 0, the second clause in the following condition // causes a run-time error. The && operator short circuits when the // first expression is false. That is, it does not evaluate the // second expression. The & operator evaluates both, and causes // a run-time error when divisor is 0. if ((divisor != 0) && (dividend / divisor > 0))

{

 

Console.WriteLine("Quotient: {0}", dividend / divisor);

}

else

{

 

Console.WriteLine("Attempted division by 0 ends up here.");

}

New Operator

Use the concise form of object instantiation, with implicit typing, as shown in the following declaration.

C#

var instance1 = new ExampleClass();

The previous line is equivalent to the following declaration.

C#

ExampleClass instance2 = new ExampleClass();

Use object initializers to simplify object creation.

C#

// Object initializer. var instance3 = new ExampleClass { Name = "Desktop", ID = 37414, Location = "Redmond", Age = 2.3 };

// Default constructor and assignment statements. var instance4 = new ExampleClass(); instance4.Name = "Desktop"; instance4.ID = 37414; instance4.Location = "Redmond"; instance4.Age = 2.3;

Event Handling

If you are defining an event handler that you do not need to remove later, use a lambda expression.

C#

public Form2()

{

// You can use a lambda expression to define an event handler. this.Click += (s, e) =>

{

MessageBox.Show(

((MouseEventArgs)e).Location.ToString());

}

};

C#

// Using a lambda expression shortens the following traditional definition. public Form1()

{

this.Click += new EventHandler(Form1_Click);

}

void Form1_Click(object sender, EventArgs e)

{

MessageBox.Show(((MouseEventArgs)e).Location.ToString());

}

Static Members

Call static members by using the class name: ClassName.StaticMember. Do not access a static member that is defined in a base class from a derived class.

LINQ Queries

Use meaningful names for query variables. The following example uses seattleCustomers for customers who are located in Seattle.

C#

var seattleCustomers = from cust in customers where cust.City == "Seattle" select cust.Name;

Use aliases to make sure that property names of anonymous types are correctly capitalized, using Pascal casing.

C#

var localDistributors = from customer in customers join distributor in distributors on customer.City equals distributor.City select new { Customer = customer, Distributor = distributor };

Rename properties when the property names in the result would be ambiguous. For example, if your query returns a customer name and a distributor ID, instead of leaving them as Name and ID in the result, rename them to clarify that Name is the name of a customer, and ID is the ID of a distributor.

C#

var localDistributors2 = from cust in customers join dist in distributors on cust.City equals dist.City select new { CustomerName = cust.Name, DistributorID = dist.ID };

Use implicit typing in the declaration of query variables and range variables.

C#

var seattleCustomers = from cust in customers where cust.City == "Seattle" select cust.Name;

Align query clauses under the from clause, as shown in the previous examples.

Use where clauses before other query clauses to ensure that later query clauses operate on the reduced, filtered set of data.

C#

var seattleCustomers2 = from cust in customers where cust.City == "Seattle" orderby cust.Name select cust;

Use multiple from clauses instead of a join clause to access inner collections. For example, a collection of Student objects might each contain a collection of test scores. When the following query is executed, it returns each score that is over 90, along with the last name of the student who received the score.

C#

// Use a compound from to access the inner sequence within each element. var scoreQuery = from student in students from score in student.Scores where score > 90 select new { Last = student.LastName, score };

Security

Follow the guidelines in Secure Coding Guidelines.

Guidelines for Names

Naming guidelines provide guidance on selecting appropriate identifiers for the elements that make up class libraries including assemblies, namespaces, types, members, and parameters. Choosing identifiers that conform to these guidelines improves the usability of your library and encourages users to trust that your library will not require learning a new set of conventions. To provide a consistent developer experience, these guidelines are required for publicly exposed elements such as public classes and protected methods. However, for consistency throughout your code and improved maintainability, you should consider using these conventions consistently throughout your code.

Capitalization Conventions

Many of the naming conventions pertain to the casing of identifiers. It is important to note that the common language runtime (CLR) supports case-sensitive and case-insensitive languages. The capitalization conventions described in this topic make it easy for developers to understand and work with a library.

Casing Styles

The following terms describe different ways to case identifiers.

Pascal Casing

The first letter in the identifier and the first letter of each subsequent concatenated word are capitalized. You can use Pascal case for identifiers of three or more characters. For example:

BackColor

Camel Casing

The first letter of an identifier is lowercase and the first letter of each subsequent concatenated word is capitalized. For example:

backColor

Uppercase

All letters in the identifier are capitalized. For example:

IO

Capitalization Rules for Identifiers

When an identifier consists of multiple words, do not use separators, such as underscores ("_") or hyphens ("-"), between words. Instead, use casing to indicate the beginning of each word. The following guidelines provide the general rules for identifiers. Do use Pascal casing for all public member, type, and namespace names consisting of multiple words. Note that this rule does not apply to instance fields. For reasons that are detailed in the Member Design Guidelines, you should not use public instance fields. Do use camel casing for parameter names. The following table summarizes the capitalization rules for identifiers and provides examples for the different types of identifiers.

Identifier

Case

Example

Class

Pascal

AppDomain

Enumeration type

Pascal

ErrorLevel

Enumeration values

Pascal

FatalError

Event

Pascal

ValueChanged

Exception class

Pascal

WebException

Read-only static field

Pascal

RedValue

Interface

Pascal

IDisposable

Method

Pascal

ToString

Namespace

Pascal

System.Drawing

Parameter

Camel

typeName

Property

Pascal

BackColor

Capitalization Rules for Acronyms

An acronym is a word that is formed from the letters of words in a term or phrase. For example, HTML is an acronym for Hypertext Markup Language. You should include acronyms in identifiers only when they are widely known and well understood. Acronyms differ from abbreviations in that an abbreviation shortens a single word. For example, ID is an abbreviation for identifier. In general, library names should not use abbreviations.

Note:

The two abbreviations that can be used in identifiers are ID and OK. In Pascal-cased identifiers they should appear as Id, and Ok. If used as the first word in a camel-cased identifier, they should appear as id and ok, respectively.

Casing of acronyms depends on the length of the acronym. All acronyms are at least two characters long. For the purposes of these guidelines, if an acronym is exactly two characters, it is considered a short acronym. An acronym of three or more characters is a long acronym. The following guidelines specify the proper casing for short and long acronyms. The identifier casing rules take precedence over acronym casing rules. Do capitalize both characters of two-character acronyms, except the first word of a camel-cased identifier.

A property named DBRate is an example of a short acronym (DB) used as the first word of a

Pascal-cased identifier. A parameter named ioChannel is an example of a short acronym (IO) used as the first word of a camel-cased identifier.

Do capitalize only the first character of acronyms with three or more characters, except the first word of a camel-cased identifier.

A class named XmlWriter is an example of a long acronym used as the first word of a Pascal-cased

identifier. A parameter named htmlReader is an example of a long acronym used as the first word of a camel-cased identifier.

Do not capitalize any of the characters of any acronyms, whatever their length, at the beginning of a camel-cased identifier.

A parameter named xmlStream is an example of a long acronym (xml) used as the first word of a

camel-cased identifier. A parameter named dbServerName is an example of a short acronym (db) used as the first word of a camel-cased identifier.

Capitalization Rules for Compound Words and Common Terms

Do not capitalize each word in so-called closed-form compound words. These are compound words written as a single word, such as "endpoint". For example, hashtable is a closed-form compound word that should be treated as a single word and cased accordingly. In Pascal case, it is Hashtable; in camel case, it is hashtable. To determine whether a word is a closed-form compound word, check a current dictionary. The following list identifies some common terms that are not closed-form compound words. The word is shown in Pascal casing followed by the camel-cased form in parentheses.

BitFlag (bitFlag)

FileName (fileName)

LogOff (logOff)

LogOn (logOn)

SignIn (signIn)

SignOut (signOut)

UserName (userName)

WhiteSpace (whiteSpace)

CaseSensitivity

The capitalization guidelines exist solely to make identifiers easier to read and recognize. Casing cannot be used as a means of avoiding name collisions between library elements.

General Naming Conventions

The general naming conventions discuss choosing the best names for the elements in your libraries. These guidelines apply to all identifiers. Later sections discuss naming specific elements such as namespaces or properties.

Word Choice

Do choose easily readable identifier names. For example, a property named HorizontalAlignment is more readable in English than AlignmentHorizontal. Do favor readability over brevity. The property name CanScrollHorizontally is better than ScrollableX (an obscure reference to the X-axis). Do not use underscores, hyphens, or any other nonalphanumeric characters. Do not use Hungarian notation. Hungarian notation is the practice of including a prefix in identifiers to encode some metadata about the parameter, such as the data type of the identifier. Avoid using identifiers that conflict with keywords of widely used programming languages. While CLS-compliant languages must provide a way to use keywords as regular words, best practices dictate that you do not force developers to know how to do this. For most programming languages, the language reference documentation contains a list of the keywords used by the languages. The following table provides links to the reference documentation for some widely used programming languages.

Language

Link

C#

C# Reference

C++

C++ Language Reference

Visual Basic

Visual Basic Language Reference

Abbreviations and Acronyms

In general, you should not use abbreviations or acronyms. These make your names less readable. Similarly, it is difficult to know when it is safe to assume that an acronym is widely recognized. For capitalization rules for abbreviations, see Capitalization Conventions. Do not use abbreviations or contractions as parts of identifier names. For example, use OnButtonClick rather than OnBtnClick. Do not use any acronyms that are not widely accepted, and then only when necessary.

Language Specific Names

Do use semantically interesting names rather than language-specific keywords for type names. For example, GetLength is a better name than GetInt. Do use a generic common language runtime (CLR) type name, rather than a language-specific name, in the rare cases when an identifier has no semantic meaning beyond its type. For example, a method that converts data to Int16 should be named ToInt16, not ToShort because Short is the language-specific type name for Int16. The following table shows the language-specific type names for common programming languages and the CLR counterpart.

C# type

Visual Basic type name

JScript type

Visual C++ type name

Ilasm.exe

CLR type

name

name

representation

name

sbyte

SByte

sByte

char

int8

SByte

byte

Byte

byte

unsigned char

unsigned int8

Byte

short

Short

short

short

int16

Int16

ushort

UInt16

ushort

unsigned short

unsigned int16

UInt16

int

Integer

int

int

int32

Int32

uint

UInt32

uint

unsigned int

unsigned int32

UInt32

long

Long

long

int64

int64

Int64

ulong

UInt64

ulong

unsigned

int64

unsigned int64

UInt64

float

Single

float

float

float32

Single

double

Double

double

double

float64

Double

bool

Boolean

boolean

bool

bool

Boolean

char

Char

char

wchar_t

char

Char

string

String

string

String

string

String

object

Object

object

Object

object

Object

Do use a common name, such as value or item, rather than repeating the type name, in the rare cases when an identifier has no semantic meaning and the type of the parameter is not important.

Names of Assemblies and DLLs

In most scenarios, an assembly contains all or part of a reusable library and is contained in a single dynamic-link library (DLL). An assembly can be split among multiple DLLs but this is very uncommon and not addressed in this guideline. Where assemblies and DLLs are the physical organization of a library, namespaces are a logical organization and should be factored independent of the assembly's organization. Namespaces can and often do span multiple assemblies. Do choose names for your assembly DLLs that suggest large chunks of functionality such as System.Data. Assembly and DLL names do not have to correspond to namespace names but it is reasonable to follow the namespace name when naming assemblies. Consider naming DLLs according to the following pattern:

<Company>.<Component>.dll Where <Component> contains one or more dot-separated clauses. For example, Contoso.WebControls.dll.

Names of Namespaces

The name chosen for a namespace should indicate the functionality made available by types in the namespace. For example, the System.Net.Sockets namespace contains types that enable developers to use sockets to communicate over networks. The general format for a namespace name is as follows:

<Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>] For example, Microsoft.WindowsMobile.DirectX. Do prefix namespace names with a company name to prevent namespaces from different companies from having the same name and prefix. Do use a stable, version-independent product name at the second level of a namespace name. Do not use organizational hierarchies as the basis for names in namespace hierarchies, because group names within corporations tend to be short-lived. The namespace name is a long-lived and unchanging identifier. As organizations evolve, changes should not make the namespace name obsolete. Do use Pascal casing, and separate namespace components with periods (for example, Microsoft.Office.PowerPoint). If your brand employs nontraditional casing, you should follow the casing defined by your brand, even if it deviates from normal namespace casing. Consider using plural namespace names where appropriate. For example, use System.Collections instead of System.Collection. Brand names and acronyms are exceptions to this rule, however. For example, use System.IO instead of System.IOs. Do not use the same name for a namespace and a type in that namespace. For example, do not use Debug for a namespace name and also provide a class named Debug in the same namespace. Several compilers require such types to be fully qualified.

Some of the guidelines presented in this section are relevant to the following categories of namespaces:

Application model namespaces

Infrastructure namespaces

Core namespaces

Technology namespace groups

The namespaces in an application model provide the set of functionality specific to a class of applications. For example, the types in the System.Windows.Forms namespaces provide the functionality required to write Windows forms client applications. The types in the System.Web namespaces support writing Web-based server applications. In general, namespaces from different application models are not used in the same application, so name collisions are less likely to affect developers using your library. Infrastructure applications provide specialized support and are seldom referenced in program code. For example, the types in the *.Designer namespaces are used by program development tools. The *.Permissions namespaces are another example of infrastructure namespaces. Name collisions with types in infrastructure namespaces are unlikely to affect developers using your library. The core namespaces are the System.* namespaces (excluding the application and infrastructure namespaces). System and System.Text are examples of core namespaces. You should make every effort to avoid name collisions with types in the core namespaces. The namespaces belonging to a particular technology will have the same first and second level identifiers (Company.technology.*). You should avoid name collisions within a technology. General Namespace Guidelines Do not introduce generic type names such as Element, Node, Log, and Message. There is a very high probability it would lead to type name conflicts in common scenarios. You should qualify the generic type names (FormElement, XmlNode EventLog, SoapMessage). Application Namespace Guidelines Do not give the same name to types in namespaces within a single application model. For example, if you were writing a library of special controls to be used by Windows forms application developers, you should not introduce a type named Checkbox because a type with this name already exists for the application model (CheckBox). Core Namespace Guidelines Do not give types names that would conflict with any type in the core namespaces. For example, do not use Directory as a type name because this would conflict with the Directory

type. Technology Namespace Guidelines Do not assign type names that would conflict with other types within a single technology. Do not introduce type name conflicts between types in technology namespaces and an application model namespace (unless the technology is not intended to be used with the application model).

Names of Classes, Structs, and Interfaces

In general, type names should be noun phrases, where the noun is the entity represented by the type. For example, Button, Stack, and File each have names that identify the entity represented by the type. Choose names that identify the entity from the developer's perspective; names should reflect usage scenarios. The following guidelines apply to selecting type names. Do name classes, interfaces, and value types with nouns, noun phrases, or occasionally adjective phrases, using Pascal casing. Do not give class names a prefix (such as the letter C). Interfaces, which should begin with the letter I, are the exception to this rule.

Consider ending the name of derived classes with the name of the base class. For example, Framework types that inherit from Stream end in Stream, and types that inherit from Exception end in Exception. Do prefix interface names with the letter I to indicate that the type is an interface. Do ensure that when defining a class/interface pair where the class is a standard implementation of the interface, the names differ only by the letter I prefix on the interface name. For example, the Framework provides the IAsyncResult interface and the AsyncResult class.

Names of Generic Type Parameters

Generics are a major new feature of the .NET Framework version 2.0. The following guidelines cover selecting the correct names for generic type parameters. Do name generic type parameters with descriptive names, unless a single-letter name is completely self explanatory and a descriptive name would not add value. IDictionary<TKey, TValue> is an example of an interface that follows this guideline. Consider using the letter T as the type parameter name for types with one single-letter type parameter. Do prefix descriptive type parameter names with the letter T. Consider indicating constraints placed on a type parameter in the name of parameter. For example, a parameter constrained to ISession may be called TSession.

Names of Common Types

The following guidelines provide naming conventions that help developers recognize the intended usage scenario for certain classes. Where the guideline refers to types that inherit from some other type, this applies to all inheritors, not just the types that directly inherit. For example, the guideline "Do add the suffix Exception to types that inherit from Exception." means that any type that has Exception in its inheritance hierarchy should have a name that ends in Exception. Each of these guidelines also serves to reserve the specified suffix; unless your type meets the criteria established by the guideline, it should not use the suffix. For example, if your type does not directly or indirectly inherit from Exception, its name must not end in Exception. Do add the suffix Attribute to custom attribute classes. ObsoleteAttribute and AttributeUsageAttribute are type names that follow this guideline. Do add the suffix EventHandler to names of types that are used in events (such as return types of a C# event). AssemblyLoadEventHandler is a delegate name that follows this guideline. Do add the suffix Callback to the name of a delegate that is not an event handler. Do not add the suffix Delegate to a delegate. Do add the suffix EventArgs to classes that extend System.EventArgs. Do not derive from the System.Enum class; use the keyword supported by your language instead. For example, in C#, use the enum keyword. Do add the suffix Exception to types that inherit from System.Exception. Do add the suffix Dictionary to types that implement System.Collections.IDictionary or System.Collections.Generic.IDictionary<TKey, TValue>. Note that System.Collections.IDictionary is a specific type of collection, but this guideline takes precedence over the more general collections guideline below. Do add the suffix Collection to types that implement System.Collections.IEnumerable, System.Collections.ICollection, System.Collections.IList, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.ICollection<T>, or System.Collections.Generic.IList<T>.

Do add the suffix Stream to types that inherit from System.IO.Stream. Do add the suffix Permission to types that inherit from System.Security.CodeAccessPermission or implement System.Security.IPermission.

Names of Enumerations

Do not use a prefix on enumeration value names. For example, do not use a prefix such as ad for ADO enumerations, or rtf for rich text enumerations, and so on. This also implies that you should not include the enumeration type name in the enumeration value names. The following code example demonstrates incorrectly naming an enumeration's values.

C#

public enum Teams

{

 

TeamsAlpha,

TeamsBeta,

TeamsDelta

}

Do not use Enum as a suffix on enumeration types. Do not add Flags as a suffix on the names of flags enumerations. Do use a singular name for an enumeration, unless its values are bit fields. Do use a plural name for enumerations with bit fields as values, also called flags enumerations.

Names of Type Members

Types contain the following kinds of members:

Methods

Properties

Fields

Events

The guidelines in this section help class library designers select names for members that are consistent with the .NET Framework.

Names of Methods

Do give methods names that are verbs or verb phrases. Typically methods act on data, so using a verb to describe the action of the method makes it easier for developers to understand what the method does. When defining the action performed by the method, be careful to select a name that provides clarity from the developer's perspective. Do not select a verb that describes how the method does what it does; in other words, do not use implementation details for your method name.

Do not use properties that match the names of Get methods. For example do not name a property EmployeeRecord and also name a method GetEmployeeRecord. Developers will not know which member to use to accomplish their programming task.

Do name Boolean properties with an affirmative phrase (CanSeek instead of CantSeek). Optionally, you can also prefix Boolean properties with Is, Can, or Has, but only where it adds value. Consider giving a property the same name as its type. When you have a property that is strongly typed to an enumeration, the name of the property can be the same as the name of the enumeration. For example, if you have an enumeration named CacheLevel, a property that returns one of its values can also be named CacheLevel.

Names of Events

Do name events with a verb or a verb phrase. Do give event names a concept of before and after, using the present and past tense. For example, a close event that is raised before a window is closed would be called Closing and one that is raised after the window is closed would be called Closed. Do not use Before or After prefixes or suffixes to indicate pre and post events. Do name event handlers (delegates used as types of events) with the EventHandler suffix. Do use two parameters named sender and e in event handler signatures. The sender parameter should be of type Object, and the e parameter should be an instance of or inherit from EventArgs. Do name event argument classes with the EventArgs suffix.

Names of Fields

The naming guidelines for fields apply to static public and protected fields. You should not define public or protected instance fields. For more information, see Field Design. Do use Pascal casing in field names. Do name fields with nouns or noun phrases. Do not use a prefix for field names. For example, do not use g_ or s_ to distinguish static versus non-static fields.

Names of Parameters

Choosing good parameter names can significantly improve the usability of your library. A good parameter name should indicate what data or functionality is affected by the parameter. Do use camel casing in parameter names. Do use descriptive parameter names. In most scenarios, the name of the parameter and its type should be sufficient to determine the parameter's usage. Consider using names based on a parameter's meaning rather than names based on the parameter's

type.

In developer tools and documentation, the parameter's type is usually visible. By selecting a name that describes the usage or meaning of the parameter, you give developers valuable information that helps them identify the right member for their task and the right data to pass into the member.

Names of Resources

The guidelines in this topic apply to localizable resources such as error messages and menu text. Do use Pascal casing in resource keys.

Do provide descriptive rather than short identifiers. Keep them concise where possible, but do not sacrifice readability. Do not use language-specific keywords from the common language runtime (CLR) programming languages. Do use only alphanumeric characters and underscores in naming resources. Do use the dot separator (".") to nest identifiers with a clear hierarchy.

Menus.FileMenu.Close.Color

conform to this guideline. Do use the following naming convention for exception message resources. The resource identifier should be the exception type name plus a short identifier of the exception separated by a dot.

For

example,

names

such

as

Menus.FileMenu.Close.Text

and

For example, ArgumentException.BadEnumValue conforms to this guideline.

Main() and Command-Line Arguments

Main() and Command-Line Arguments

The Main method is the entry point of a C# console application or windows application. (Libraries and services do not require a Main method as an entry point.). When the application is started, the Main method is the first method that is invoked. There can only be one entry point in a C# program. If you have more than one class that has a Main method, you must compile your program with the /main compiler option to specify which Main method to use as the entry point. For more information, see /main (C# Compiler Options).

C#

class TestClass

{

 

static void Main(string[] args)

{

// Display the number of command line arguments:

System.Console.WriteLine(args.Length);

}

}

Overview

The Main method is the entry point of an .exe program; it is where the program control starts and ends.

Main is declared inside a class or struct. Main must be static and it should not be public. (In the earlier example, it receives the default access of private.) The enclosing class or struct is not required to be static.

Main can either have a void or int return type.

The Main method can be declared with or without a string[] parameter that contains command- line arguments. When using Visual Studio to create Windows Forms applications, you can add the parameter manually or else use the Environment class to obtain the command-line arguments. Parameters are read as zero-indexed command-line arguments. Unlike C and C++, the name of the program is not treated as the first command-line argument.

Command-Line Arguments

The Main method can use arguments, in which case, it takes one of the following forms:

C#

static int Main(string[] args)

C#

static void Main(string[] args)

Note:

To enable command-line arguments in the Main method in a Windows Forms application, you must manually modify the signature of Main in program.cs. The code generated by the Windows Forms designer creates a Main without an input parameter. You can also use Environment.CommandLine or Environment.GetCommandLineArgs to access the command-line arguments from any point in a console or Windows application.

The parameter of the Main method is a String array that represents the command-line arguments. Usually you determine whether arguments exist by testing the Length property, for example:

C#

if (args.Length == 0)

{

System.Console.WriteLine("Please enter a numeric argument."); return 1;

}

You can also convert the string arguments to numeric types by using the Convert class or the Parse method. For example, the following statement converts the string to a long number by using the Parse method:

long num = Int64.Parse(args[0]);

It is also possible to use the C# type long, which aliases Int64:

long num = long.Parse(args[0]);

You can also use the Convert class method ToInt64 to do the same thing:

long num = Convert.ToInt64(s);

For more information, see Parse and Convert.

Example

The following example shows how to use command-line arguments in a console application. The program takes one argument at run time, converts the argument to an integer, and calculates the factorial of the number. If no arguments are supplied, the program issues a message that explains the correct usage of the program.

Note:

When running an application in Visual Studio, you can specify command-line arguments in the Debug Page, Project Designer.

For more examples about how to use command-line arguments, see How to: Create and Use Assemblies Using the Command Line (C# and Visual Basic).

C#

public class Functions

{

public static long Factorial(int n)

{

 

// Test for invalid input if ((n < 0) || (n > 20))

{

return -1;

}

// Calculate the factorial iteratively rather than recursively:

long tempResult = 1; for (int i = 1; i <= n; i++)

{

tempResult *= i;

}

return tempResult;

}

}

class MainClass

{

 

static int Main(string[] args)

{

// Test if input arguments were supplied:

if (args.Length == 0)

{

System.Console.WriteLine("Please enter a numeric argument."); System.Console.WriteLine("Usage: Factorial <num>"); return 1;

}

// Try to convert the input arguments to numbers. This will throw // an exception if the argument is not a number. // num = int.Parse(args[0]); int num; bool test = int.TryParse(args[0], out num); if (test == false)

{

System.Console.WriteLine("Please enter a numeric argument."); System.Console.WriteLine("Usage: Factorial <num>"); return 1;

}

// Calculate factorial. long result = Functions.Factorial(num);

// Print result. if (result == -1) System.Console.WriteLine("Input must be >= 0 and <= 20.");

else

System.Console.WriteLine("The Factorial of {0} is {1}.", num, result);

return 0;

}

}

// If 3 is entered on command line, the // output reads: The factorial of 3 is 6.

How to: Display Command Line Arguments

Arguments provided to an executable on the command-line are accessible through an optional parameter to Main. The arguments are provided in the form of an array of strings. Each element of the array contains one argument. White-space between arguments is removed. For example, consider these command-line invocations of a fictitious executable:

Input on Command-line

Array of strings passed to Main

 

"a"

executable.exe a b c

"b"

 

"c"

 

"one"

executable.exe one two

"two"

 

"one two"

executable.exe "one two" three

"three"

Note

When you are running an application in Visual Studio, you can specify command-line arguments in the Debug Page, Project Designer.

Example

This example displays the command line arguments passed to a command-line application. The output shown is for the first entry in the table above.

C#

class CommandLine

{

 

static void Main(string[] args)

{

// The Length property provides the number of array elements System.Console.WriteLine("parameter count = {0}", args.Length);

for (int i = 0; i < args.Length; i++)

{

System.Console.WriteLine("Arg[{0}] = [{1}]", i, args[i]);

}

}

}

/* Output (assumes 3 cmd line args):

parameter count = 3 Arg[0] = [a] Arg[1] = [b] Arg[2] = [c]

*/

How to: Access Command-Line Arguments Using foreach

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, a .NET Framework collection class, or any class or struct that implements the IEnumerable interface.

Note:

When running an application in Visual Studio, you can specify command-line arguments in the Debug Page, Project Designer.

Example This example demonstrates how to print out the command line arguments using foreach.

C#

// arguments: John Paul Mary

C#

class CommandLine2

{

static void Main(string[] args)

{

System.Console.WriteLine("Number

args.Length);

of

foreach (string s in args)

{

System.Console.WriteLine(s);

}

}

}

/* Output:

Number of command line parameters = 3 John Paul

Mary

*/

Main() Return Values

The Main method can return void:

C#

static void Main()

{

}

//

It can also return an int:

C#

static int Main()

{

 

//

return 0;

}

command

line

parameters

=

{0}",

If the return value from Main is not used, returning void allows for slightly simpler code. However, returning an integer enables the program to communicate status information to other programs or scripts that invoke the executable file. The following example shows how the return value from Main can be accessed.

Example In this example, a batch file is used to run a program and test the return value of the Main function. When a program is executed in Windows, any value returned from the Main function is stored in an environment variable called ERRORLEVEL. A batch file can determine the outcome of execution by inspecting the ERRORLEVEL variable. Traditionally, a return value of zero indicates successful execution. The following example is a simple program that returns zero from the Main function. The zero indicates that the program ran successfully. Save the program as MainReturnValTest.cs.

C#

// Save this program as MainReturnValTest.cs. class MainReturnValTest

{

 

static int Main()

{

//

return 0;

}

}

Because this example uses a batch file, it is best to compile the code from a command prompt. Follow the instructions in How to: Set Environment Variables to enable command-line builds, or use the Visual Studio Command Prompt, available from the Start menu under Visual Studio Tools. From the command prompt, navigate to the folder in which you saved the program. The following command compiles MainReturnValTest.cs and produces the executable file MainReturnValTest.exe. csc MainReturnValTest.cs Next, create a batch file to run MainReturnValTest.exe and to display the result. Paste the following code into a text file and save it as test.bat in the folder that contains MainReturnValTest.cs and MainReturnValTest.exe. Run the batch file by typing test at the command prompt. Because the code returns zero, the batch file will report success. However, if you change MainReturnValTest.cs to return a non-zero value and then re-compile the program, subsequent execution of the batch file will report failure.

rem test.bat @echo off MainReturnValueTest @if "%ERRORLEVEL%" == "0" goto good

:fail echo Execution Failed echo return value = %ERRORLEVEL% goto end

:good echo Execution succeeded echo Return value = %ERRORLEVEL% goto end

:end

Types

Types

Types, Variables, and Values

C# is a strongly-typed language. Every variable and constant has a type, as does every expression that evaluates to a value. Every method signature specifies a type for each input parameter and for the return value. The .NET Framework class library defines a set of built-in numeric types as well as more complex types that represent a wide variety of logical constructs, such as the file system, network connections, collections and arrays of objects, and dates. A typical C# program uses types from the class library as well as user-defined types that model the concepts that are specific to the program's problem domain. The information stored in a type can include the following:

The storage space that a variable of the type requires.

The maximum and minimum values that it can represent.

The members (methods, fields, events, and so on) that it contains.

The base type it inherits from.

The location where the memory for variables will be allocated at run time.

The kinds of operations that are permitted. The compiler uses type information to make sure that all operations that are performed in your code are type safe. For example, if you declare a variable of type int, the compiler allows you to use the variable in addition and subtraction operations. If you try to perform those same operations on a variable of type bool, the compiler generates an error, as shown in the following example:

C#

int a = 5; int b = a + 2; //OK

bool test = true;

// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'. int c = a + test;

Note:

C and C++ developers, notice that in C#, bool is not convertible to int.

The compiler embeds the type information into the executable file as metadata. The common language runtime (CLR) uses that metadata at run time to further guarantee type safety when it allocates and reclaims memory.

Specifying Types in Variable Declarations

When you declare a variable or constant in a program, you must either specify its type or use the var keyword to let the compiler infer the type. The following example shows some variable declarations that use both built-in numeric types and complex user-defined types:

C#

// Declaration only:

float temperature; string name; MyClass myClass;

// Declaration with initializers (four examples):

char firstLetter = 'C'; var limit = 3; int[] source = { 0, 1, 2, 3, 4, 5 }; var query = from item in source where item <= limit select item;

The types of method parameters and return values are specified in the method signature. The following signature shows a method that requires an int as an input argument and returns a string:

C#

public string GetName(int ID)

{

if (ID < names.Length) return names[ID];

else

return String.Empty;

}

private string[] names = { "Spencer", "Sally", "Doug" };

After a variable is declared, it cannot be re-declared with a new type, and it cannot be assigned a value that is not compatible with its declared type. For example, you cannot declare an int and then assign it a Boolean value of true. However, values can be converted to other types, for example when they are assigned to new variables or passed as method arguments. A type conversion that does not cause data loss is performed automatically by the compiler. A conversion that might cause data loss requires a cast in the source code. For more information, see Casting and Type Conversions (C# Programming Guide).

Built in Types

C# provides a standard set of built-in numeric types to represent integers, floating point values, Boolean expressions, text characters, decimal values, and other types of data. There are also built-in string and object types. These are available for you to use in any C# program. For a more information about the built-in types, see Reference Tables for Types (C# Reference).

Custom Types

You use the struct, class, interface, and enum constructs to create your own custom types. The .NET Framework class library itself is a collection of custom types provided by Microsoft that you can use in your own applications. By default, the most frequently used types in the class library are available in any C# program. Others become available only when you explicitly add a project reference to the assembly in which they are defined. After the compiler has a reference to the assembly, you can declare variables (and constants) of the types declared in that assembly in source code. For more information, see .NET Framework Class Library.

The Common Type System

It is important to understand two fundamental points about the type system in the .NET Framework:

It supports the principle of inheritance. Types can derive from other types, called base types. The derived type inherits (with some restrictions) the methods, properties, and other members of the

base type. The base type can in turn derive from some other type, in which case the derived type inherits the members of both base types in its inheritance hierarchy. All types, including built-in numeric types such as System.Int32 (C# keyword: int), derive ultimately from a single base type, which is System.Object (C# keyword: object). This unified type hierarchy is called the Common Type System (CTS). For more information about inheritance in C#, see Inheritance (C# Programming Guide).

Each type in the CTS is defined as either a value type or a reference type. This includes all custom types in the .NET Framework class library and also your own user-defined types. Types that you define by using the struct keyword are value types; all the built-in numeric types are structs. Types that you define by using the class keyword are reference types. Reference types and value types have different compile-time rules, and different run-time behavior.

The following illustration shows the relationship between value types and reference types in the CTS.

between value types and reference types in the CTS. Note: You can see that the most

Note:

You can see that the most commonly used types are all organized in the System namespace. However, the namespace in which a type is contained has no relation to whether it is a value type or reference type.

Value Types

Value types derive from System.ValueType, which derives from System.Object. Types that derive from System.ValueType have special behavior in the CLR. Value type variables directly contain their values, which means that the memory is allocated inline in whatever context the variable is declared. There is no separate heap allocation or garbage collection overhead for value-type variables. There are two categories of value types: struct and enum. The built-in numeric types are structs, and they have properties and methods that you can access:

C#

// Static method on type Byte. byte b = Byte.MaxValue;

But you declare and assign values to them as if they were simple non-aggregate types:

C#

byte num = 0xA; int i = 5; char c = 'Z';

Value types are sealed, which means, for example, that you cannot derive a type from System.Int32, and you cannot define a struct to inherit from any user-defined class or struct because a struct can only inherit from System.ValueType. However, a struct can implement one or more interfaces. You can cast a struct type to an interface type; this causes a boxing operation to wrap the struct inside a reference type object on the managed heap. Boxing operations occur when you pass a value type to a method that takes a System.Object as an input parameter. For more information, see Boxing and Unboxing (C# Programming Guide). You use the struct keyword to create your own custom value types. Typically, a struct is used as a container for a small set of related variables, as shown in the following example:

C#

public struct CoOrds

{

 

public int x, y;

public CoOrds(int p1, int p2)

{

x = p1;

y = p2;

}

}

For more information about structs, see Structs (C# Programming Guide). For more information about value types in the .NET Framework, see Common Type System. The other category of value types is enum. An enum defines a set of named integral constants. For example, the System.IO.FileMode enumeration in the .NET Framework class library contains a set of named constant integers that specify how a file should be opened. It is defined as shown in the following example:

C#

public enum FileMode

{

CreateNew = 1, Create = 2, Open = 3, OpenOrCreate = 4, Truncate = 5, Append = 6,

}

The System.IO.FileMode.Create constant has a value of 2. However, the name is much more meaningful for humans reading the source code, and for that reason it is better to use enumerations instead of constant literal numbers. For more information, see System.IO.FileMode. All enums inherit from System.Enum, which inherits from System.ValueType. All the rules that apply to structs also apply to enums. For more information about enums, see Enumeration Types (C# Programming Guide).

Reference Types

A type that is defined as a class, delegate, array, or interface is a reference type. At run time, when

you declare a variable of a reference type, the variable contains the value null until you explicitly create an instance of the object by using the new operator, or assign it an object that has been created elsewhere by using new, as shown in the following example:

C#

MyClass mc = new MyClass(); MyClass mc2 = mc;

An interface must be initialized together with a class object that implements it. If MyClass implements IMyInterface, you create an instance of IMyInterface as shown in the following example:

C#

IMyInterface iface = new MyClass();

When the object is created, the memory is allocated on the managed heap, and the variable holds only a reference to the location of the object. Types on the managed heap require overhead both when they are allocated and when they are reclaimed by the automatic memory management functionality of the CLR, which is known as garbage collection. However, garbage collection is also highly optimized, and in most scenarios it does not create a performance issue. For more information about garbage collection, see Automatic Memory Management. All arrays are reference types, even if their elements are value types. Arrays implicitly derive from the System.Array class, but you declare and use them with the simplified syntax that is provided by C#, as shown in the following example:

C#

// Declare and initialize an array of integers. int[] nums = { 1, 2, 3, 4, 5 };

// Access an instance property of System.Array. int len = nums.Length;

Reference types fully support inheritance. When you create a class, you can inherit from any other interface or class that is not defined as sealed, and other classes can inherit from your class and override your virtual methods. For more information about how to create your own classes, see Classes and Structs (C# Programming Guide). For more information about inheritance and virtual methods, see Inheritance (C# Programming Guide).

Types of Literal Values

In C#, literal values receive a type from the compiler. You can specify how a numeric literal should

be typed by appending a letter to the end of the number. For example, to specify that the value 4.56 should be treated as a float, append an "f" or "F" after the number: 4.56f. If no letter is appended, the

compiler will infer a type for the literal. For more information about which types can be specified with letter suffixes, see the reference pages for individual types in Value Types (C# Reference). Because literals are typed, and all types derive ultimately from System.Object, you can write and compile code such as the following:

C#

string s = "The answer is " + 5.ToString(); // Outputs: "The answer is 5" Console.WriteLine(s);

Type type = 12345.GetType(); // Outputs: "System.Int32" Console.WriteLine(type);

Generic Types

A type can be declared with one or more type parameters that serve as a placeholder for the actual

type (the concrete type) that client code will provide when it creates an instance of the type. Such types are called generic types. For example, the .NET Framework type System.Collections.Generic.List<T> has

one type parameter that by convention is given the name T. When you create an instance of the type, you specify the type of the objects that the list will contain, for example, string:

C#

List<string> strings = new List<string>();

The use of the type parameter makes it possible to reuse the same class to hold any type of element, without having to convert each element to object. Generic collection classes are called strongly-typed collections because the compiler knows the specific type of the collection's elements and can raise an error at compile-time if, for example, you try to add an integer to the strings object in the previous example. For more information, see Generics (C# Programming Guide).

Implicit Types, Anonymous Types, and Nullable Types

As stated previously, you can implicitly type a local variable (but not class members) by using the var keyword. The variable still receives a type at compile time, but the type is provided by the compiler. For more information, see Implicitly Typed Local Variables (C# Programming Guide).

In some cases, it is inconvenient to create a named type for simple sets of related values that you do

not intend to store or pass outside method boundaries. You can create anonymous types for this purpose. For more information, see Anonymous Types (C# Programming Guide). Ordinary value types cannot have a value of null. However, you can create nullable value types by affixing a ? after the type. For example, int? is an int type that can also have the value null. In the CTS, nullable types are instances of the generic struct type System.Nullable<T>. Nullable types are especially useful when you are passing data to and from databases in which numeric values might be null. For more information, see Nullable Types (C# Programming Guide).

Casting and Type Conversions

Because C# is statically-typed at compile time, after a variable is declared, it cannot be declared again or used to store values of another type unless that type is convertible to the variable's type. For example, there is no conversion from an integer to any arbitrary string. Therefore, after you declare i as an integer, you cannot assign the string "Hello" to it, as is shown in the following code.

C#

int i; i = "Hello"; // Error: "Cannot implicitly convert type 'string' to 'int'"

However, you might sometimes need to copy a value into a variable or method parameter of another type. For example, you might have an integer variable that you need to pass to a method whose parameter is typed as double. Or you might need to assign a class variable to a variable of an interface type. These kinds of operations are called type conversions. In C#, you can perform the following kinds of conversions:

Implicit conversions: No special syntax is required because the conversion is type safe and no data will be lost. Examples include conversions from smaller to larger integral types, and conversions from derived classes to base classes.

Explicit conversions (casts): Explicit conversions require a cast operator. Casting is required when information might be lost in the conversion, or when the conversion might not succeed for other reasons. Typical examples include numeric conversion to a type that has less precision or a smaller range, and conversion of a base-class instance to a derived class.

User-defined conversions: User-defined conversions are performed by special methods that you can define to enable explicit and implicit conversions between custom types that do not have a base class–derived class relationship. For more information, see Conversion Operators (C# Programming Guide).

Conversions with helper classes: To convert between non-compatible types, such as integers and System.DateTime objects, or hexadecimal strings and byte arrays, you can use the System.BitConverter class, the System.Convert class, and the Parse methods of the built-in numeric types, such as Int32.Parse. For more information, see How to: Convert a byte Array to an int (C# Programming Guide), How to: Convert a string to an int (C# Programming Guide), and How to: Convert Between Hexadecimal Strings and Numeric Types (C# Programming Guide).

Implicit Conversions

For built-in numeric types, an implicit conversion can be made when the value to be stored can fit into the variable without being truncated or rounded off. For example, a variable of type long (8 byte integer) can store any value that an int (4 bytes on a 32-bit computer) can store. In the following example, the compiler implicitly converts the value on the right to a type long before assigning it to bigNum.

C#

// Implicit conversion. num long can // hold any value an int can hold, and more! int num = 2147483647; long bigNum = num;

For a complete list of all implicit numeric conversions, see Implicit Numeric Conversions Table (C# Reference).

For reference types, an implicit conversion always exists from a class to any one of its direct or indirect base classes or interfaces. No special syntax is necessary because a derived class always contains all the members of a base class.

Derived d = new Derived(); Base b = d; // Always OK.

Explicit Conversions

However, if a conversion cannot be made without a risk of losing information, the compiler requires that you perform an explicit conversion, which is called a cast. A cast is a way of explicitly informing the compiler that you intend to make the conversion and that you are aware that data loss might occur. To perform a cast, specify the type that you are casting to in parentheses in front of the value or variable to be converted. The following program casts a double to an int. The program will not compile without the cast.

C#

class Test

{

static void Main()

{

double x = 1234.7; int a; // Cast double to int. a = (int)x; System.Console.WriteLine(a);

}

}

// Output: 1234

For a list of the explicit numeric conversions that are allowed, see Explicit Numeric Conversions Table (C# Reference). For reference types, an explicit cast is required if you need to convert from a base type to a derived

type:

C#

// Create a new derived type. Giraffe g = new Giraffe();

// Implicit conversion to base type is safe. Animal a = g;

// Explicit conversion is required to cast back // to derived type. Note: This will compile but will // throw an exception at run time if the right-side // object is not in fact a Giraffe. Giraffe g2 = (Giraffe) a;

A cast operation between reference types does not change the run-time type of the underlying object; it only changes the type of the value that is being used as a reference to that object. For more information, see Polymorphism (C# Programming Guide).

Type Conversion Exceptions at Run Time

In some reference type conversions, the compiler cannot determine whether a cast will be valid. It is possible for a cast operation that compiles correctly to fail at run time. As shown in the following example, a type cast that fails at run time will cause an InvalidCastException to be thrown.

C#

class Animal

{

public void Eat() { Console.WriteLine("Eating."); } public override string ToString()

{

return "I am an animal.";

}

}

class Reptile : Animal { } class Mammal : Animal { }

class UnSafeCast

{

 

static void Main()

{

Test(new Mammal());

// Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey();

}

static void Test(Animal a)

{

// Cause InvalidCastException at run time // because Mammal is not convertible to Reptile. Reptile r = (Reptile)a;

}

}

C# provides the is and as operators to enable you to test for compatibility before actually performing a cast. For more information, see How to: Safely Cast by Using as and is Operators (C# Programming Guide).

Boxing and Unboxing

Boxing is the process of converting a value type to the type object or to any interface type implemented by this value type. When the CLR boxes a value type, it wraps the value inside a System.Object and stores it on the managed heap. Unboxing extracts the value type from the object. Boxing is implicit; unboxing is explicit. The concept of boxing and unboxing underlies the C# unified view of the type system, in which a value of any type can be treated as an object. In the following example, the integer variable i is boxed and assigned to object o.

C#

int i = 123; // The following line boxes i. object o = i;

The object o can then be unboxed and assigned to integer variable i:

C#

o = 123;

i = (int)o; // unboxing

The following examples illustrate how boxing is used in C#.

C#

// String.Concat example. // String.Concat has many versions. Rest the mouse pointer on // Concat in the following statement to verify that the version // that is used here takes three object arguments. Both 42 and // true must be boxed. Console.WriteLine(String.Concat("Answer", 42, true));

// List example. // Create a list of objects to hold a heterogeneous collection // of elements. List<object> mixedList = new List<object>();

// Add a string element to the list. mixedList.Add("First Group:");

// Add some integers to the list. for (int j = 1; j < 5; j++)

{

// Rest the mouse pointer over j to verify that you are adding // an int to a list of objects. Each element j is boxed when // you add j to mixedList. mixedList.Add(j);

}

// Add another string and more integers. mixedList.Add("Second Group:"); for (int j = 5; j < 10; j++)

{

mixedList.Add(j);

}

// Display the elements in the list. Declare the loop variable by // using var, so that the compiler assigns its type. foreach (var item in mixedList)

{

// Rest the mouse pointer over item to verify that the elements // of mixedList are objects. Console.WriteLine(item);

}

// The following loop sums the squares of the first group of boxed // integers in mixedList. The list elements are objects, and cannot // be multiplied or added to the sum until they are unboxed. The // unboxing must be done explicitly. var sum = 0; for (var j = 1; j < 5; j++)

{

 

// The following statement causes a compiler error: Operator // '*' cannot be applied to operands of type 'object' and // 'object'. //sum += mixedList[j] * mixedList[j]);

// After the list elements are unboxed, the computation does // not cause a compiler error. sum += (int)mixedList[j] * (int)mixedList[j];

}

// The sum displayed is 30, the sum of 1 + 4 + 9 + 16. Console.WriteLine("Sum: " + sum);

// Output:

// Answer42True // First Group:

// 1 // 2 // 3 // 4 // Second Group:

// 5 // 6 // 7 // 8 // 9 // Sum: 30

Performance In relation to simple assignments, boxing and unboxing are computationally expensive processes. When a value type is boxed, a new object must be allocated and constructed. To a lesser degree, the cast required for unboxing is also expensive computationally. For more information, see Performance.

Boxing

Boxing is used to store value types in the garbage-collected heap. Boxing is an implicit conversion of a value type to the type object or to any interface type implemented by this value type. Boxing a value type allocates an object instance on the heap and copies the value into the new object. Consider the following declaration of a value-type variable:

C#

int i = 123;

The following statement implicitly applies the boxing operation on the variable i:

C#

// Boxing copies the value of i into object o. object o = i;

The result of this statement is creating an object reference o, on the stack, that references a value of the type int, on the heap. This value is a copy of the value-type value assigned to the variable i. The difference between the two variables, i and o, is illustrated in the following figure.

variables, i and o, is illustrated in the following figure. It also possible to perform the

It also possible to perform the boxing explicitly as in the following example, but explicit boxing is never required:

C#

int i = 123; object o = (object)i; // explicit boxing

Description

This example converts an integer variable i to an object o by using boxing. Then, the value stored in the variable i is changed from 123 to 456. The example shows that the original value type and the boxed object use separate memory locations, and therefore can store different values.

Example

C#

class TestBoxing

{

static void Main()

{

int i = 123;

// Boxing copies the value of i into object o. object o = i;

// Change the value of i. i = 456;

// The change in i does not effect the value stored in o. System.Console.WriteLine("The value-type value = {0}", i); System.Console.WriteLine("The object-type value = {0}", o);

}

}

/* Output:

The value-type value = 456 The object-type value = 123

*/

The

following

example

demonstrates

a

case

of

invalid

unboxing

and

the

resulting

InvalidCastException. Using try and catch, an error message is displayed when the error occurs.

C#

class TestUnboxing

{

 

static void Main()

{

int i = 123; object o = i;

// implicit boxing

try

{

int j = (short)o;

// attempt to unbox

System.Console.WriteLine("Unboxing OK.");

}

catch (System.InvalidCastException e)

{

System.Console.WriteLine("{0} Error: Incorrect unboxing.", e.Message);

}

}

}

This program outputs:

Specified cast is not valid. Error: Incorrect unboxing. If you change the statement:

int j = (short) o;

to:

int j = (int) o;

the conversion will be performed, and you will get the output:

Unboxing OK.

Unboxing

Unboxing is an explicit conversion from the type object to a value type or from an interface type to a value type that implements the interface. An unboxing operation consists of:

Checking the object instance to make sure that it is a boxed value of the given value type.

Copying the value from the instance into the value-type variable.

The following statements demonstrate both boxing and unboxing operations:

C#

int i = 123; object o = i; int j = (int)o;

// a value type // boxing // unboxing

The following figure demonstrates the result of the previous statements.

figure demonstrates the result of the previous statements. For the unboxing of value types to succeed

For the unboxing of value types to succeed at run time, the item being unboxed must be a reference to an object that was previously created by boxing an instance of that value type. Attempting to unbox null causes a NullReferenceException. Attempting to unbox a reference to an incompatible value type causes an InvalidCastException.

Using Type dynamic

Visual C# 2010 introduces a new type, dynamic. The type is a static type, but an object of type dynamic bypasses static type checking. In most cases, it functions like it has type object. At compile time, an element that is typed as dynamic is assumed to support any operation. Therefore, you do not have to be concerned about whether the object gets its value from a COM API, from a dynamic language such as IronPython, from the HTML Document Object Model (DOM), from reflection, or from somewhere else in the program. However, if the code is not valid, errors are caught at run time. For example, if instance method exampleMethod1 in the following code has only one parameter, the compiler recognizes that the first call to the method, ec.exampleMethod1(10, 4), is not valid because it contains two arguments. The call causes a compiler error. The second call to the method, dynamic_ec.exampleMethod1(10, 4), is not checked by the compiler because the type of dynamic_ec is dynamic. Therefore, no compiler error is reported. However, the error does not escape notice indefinitely. It is caught at run time and causes a run-time exception.

C#

static void Main(string[] args)

{

 

ExampleClass ec = new ExampleClass(); // The following call to exampleMethod1 causes a compiler error // if exampleMethod1 has only one parameter. Uncomment the line // to see the error. //ec.exampleMethod1(10, 4);

dynamic dynamic_ec = new ExampleClass(); // The following line is not identified as an error by the // compiler, but it causes a run-time exception. dynamic_ec.exampleMethod1(10, 4);

// The following calls also do not cause compiler errors, whether // appropriate methods exist or not. dynamic_ec.someMethod("some argument", 7, null); dynamic_ec.nonexistentMethod();

}

C#

class ExampleClass

{

 

public ExampleClass() { } public ExampleClass(int v) { }

public void exampleMethod1(int i) { }

public void exampleMethod2(string str) { }

}

The role of the compiler in these examples is to package together information about what each statement is proposing to do to the object or expression that is typed as dynamic. At run time, the stored information is examined, and any statement that is not valid causes a run-time exception. The result of most dynamic operations is itself dynamic. For example, if you rest the mouse pointer over the use of testSum in the following example, IntelliSense displays the type (local variable) dynamic testSum.

C#

dynamic d = 1; var testSum = d + 3; // Rest the mouse pointer over testSum in the following statement. System.Console.WriteLine(testSum);

Operations in which the result is not dynamic include conversions from dynamic to another type, and constructor calls that include arguments of type dynamic. For example, the type of testInstance in the following declaration is ExampleClass, not dynamic.

C#

var testInstance = new ExampleClass(d);

Conversion examples are shown in the following section, "Conversions."

Conversions Conversions between dynamic objects and other types are easy. This enables the developer to switch between dynamic and non-dynamic behavior. Any object can be converted to dynamic type implicitly, as shown in the following examples.

C#

dynamic d1 = 7; dynamic d2 = "a string"; dynamic d3 = System.DateTime.Today; dynamic d4 = System.Diagnostics.Process.GetProcesses();

Conversely, an implicit conversion can be dynamically applied to any expression of type dynamic.

C#

int i = d1; string str = d2; DateTime dt = d3; System.Diagnostics.Process[] procs = d4;

Overload Resolution with Arguments of Type dynamic Overload resolution occurs at run time instead of at compile time if one or more of the arguments in

a method call have the type dynamic, or if the receiver of the method call is of type dynamic. In the

following example, if the only accessible exampleMethod2 method is defined to take a string argument, sending d1 as the argument does not cause a compiler error, but it does cause a run-time exception.

Overload resolution fails at run time because the run-time type of d1 is int, and exampleMethod2 requires

a string.

C#

// Valid. ec.exampleMethod2("a string");

// The following statement does not cause a compiler error, even though ec is not // dynamic. A run-time exception is raised because the run-time type of d1 is int.

ec.exampleMethod2(d1);

// The following statement does cause a compiler error.

//ec.exampleMethod2(7);

Dynamic Language Runtime

The dynamic language runtime (DLR) is a new API in .NET Framework 4. It provides the infrastructure that supports the dynamic type in C#, and also the implementation of dynamic programming languages such as IronPython and IronRuby. For more information about the DLR, see Dynamic Language Runtime Overview.

COM Interop

Visual C# 2010 includes several features that improve the experience of interoperating with COM APIs such as the Office Automation APIs. Among the improvements are the use of the dynamic type, and of named and optional arguments. Many COM methods allow for variation in argument types and return type by designating the types as object. This has necessitated explicit casting of the values to coordinate with strongly typed variables in C#. If you compile by using the /link (C# Compiler Options) option, the introduction of the dynamic type enables you to treat the occurrences of object in COM signatures as if they were of type dynamic, and thereby to avoid much of the casting. For example, the following statements contrast how you access a cell in a Microsoft Office Excel spreadsheet with the dynamic type and without the dynamic type.

C#

// Before the introduction of dynamic. ((Excel.Range)excelApp.Cells[1, 1]).Value2 = "Name"; Excel.Range range2008 = (Excel.Range)excelApp.Cells[1, 1];

C#

// After the introduction of dynamic, the access to the Value property and // the conversion to Excel.Range are handled by the run-time COM binder. excelApp.Cells[1, 1].Value = "Name"; Excel.Range range2010 = excelApp.Cells[1, 1];

How to: Safely Cast by Using as and is Operators

Because objects are polymorphic, it is possible for a variable of a base class type to hold a derived type. To access the derived type's method, it is necessary to cast the value back to the derived type. However, to attempt a simple cast in these cases creates the risk of throwing an InvalidCastException. That is why C# provides the is and as operators. You can use these operators to test whether a cast will succeed without causing an exception to be thrown. In general, the as operator is more efficient because it actually returns the cast value if the cast can be made successfully. The is operator returns only a Boolean value. It can therefore be used when you just want to determine an object's type but do not have to actually cast it.

Example The following examples show how to use the is and as operators to cast from one reference type to another without the risk of throwing an exception. The example also shows how to use the as operator with nullable value types.

C#

class SafeCasting

{

class Animal

{

public void Eat() { Console.WriteLine("Eating."); } public override string ToString()

{

return "I am an animal.";

}

}

class Mammal : Animal { } class Giraffe : Mammal { }

class SuperNova { }

static void Main()

{

SafeCasting app = new SafeCasting();

// Use the is operator to verify the type. // before performing a cast. Giraffe g = new Giraffe(); app.UseIsOperator(g);

// Use the as operator and test for null // before referencing the variable. app.UseAsOperator(g);

// Use the as operator to test // an incompatible type. SuperNova sn = new SuperNova(); app.UseAsOperator(sn);

// Use the as operator with a value type. // Note the implicit conversion to int? in

// the method body. int i = 5; app.UseAsWithNullable(i);

double d = 9.78654; app.UseAsWithNullable(d);

// Keep the console window open in debug mode. System.Console.WriteLine("Press any key to exit."); System.Console.ReadKey();