Building a C# Program

Information specific to building a C# project includes:

  • C# Compiler Options
  • Compiler Errors
  • Building from the Command Line

General information about building a C# project includes:

  • Building C++ Projects in Visual Studio

See Also

linktextlink

© 2000 Microsoft Corporation. All rights reserved.


Compiler Errors CS0001 Through CS9999

This section is a reference to the errors generated by the C# compiler. To get help on a particular error message, either click the mouse on an error number in the Output window and press F1, or type the error number in the Look for box in the Index.

See Also

Building a C# Program


Compiler Error CS0001

Internal compiler error

Try to determine if the compiler is failing because of its inability to parse unexpected syntax. Then, contact Microsoft Product Services Support.


Compiler Error CS0003

Out of memory

The compiler was unable to allocate enough virtual memory to complete compilation. Close all unnecessary applications and compile again.

You may also want to increase the pagefile size and make sure you have free disk space.


Compiler Error CS0004

Warning treated as error

You compiled with the /warnaserror compiler option, which causes the compiler to generate warnings as errors.


Compiler Error CS0005

Compiler option 'compiler_option' must be followed by an argument

Some compiler options require parameters. For example, if you specify /I, you must also specify a file name. If you do not specify a file name, the compiler generates CS0005.

For more information, see C# Compiler Options.


Compiler Error CS0006

Metadata file 'dll_name' could not be found

The program was compiled and explicitly passed the name of a file that contained metadata; however, the .dll was not found.


Compiler Error CS0007

Unexpected NGWS initialization error -- 'description'

The NGWS installation may be corrupt.

Reinstall the NGWS portion of your operating system.


Compiler Error CS0008

Unexpected error reading metadata from file 'file' -- 'description'

A DLL was successfully opened for retrieving metadata but is corrupted, such that data could not be read from it.

Get an uncorrupted DLL to import.


Compiler Error CS0009

Metadata file 'file' could not be opened -- 'description'

The file specified with the /reference compiler option does not contain valid metadata.


Compiler Error CS0010

Cannot declare a namespace and a type both named 'name'

A namespace and a user-defined type cannot have the same name.

For example, the following sample generates CS0010:

// compile without /stdlib to get mscorlib.dll and System namespace
class System {      // CS0010, use a different class name
   public static int Main()
   {
   return(0);
   }
}


Compiler Error CS0011

Referenced class 'class1' has unknown base class or interface 'class2'

A class that was imported from a file with /reference, is derived from a class or implements an interface that is not found. This could occur if a required .DLL is not also included in the compilation with /reference.

The following sequence of compilations will result in CS0011:

// B1.CS
class B1 {
   public static void Main() {
   }
}

Then:

// B2.CS; compile with /I:b1.exe
class B2: B1 {
   public static void Main() {
   }
}

Ignoring the CS0114 warning. Then:

// D.CS; compile with /I:b2.exe
namespace B1 {
   class D1
   {
      B2 b2;
      public static void Main() {
      }
   }
}

You could resolve this CS0011 by compiling with /reference:b2.exe;b1.exe and by changing the name of the namespace in d.sc so that it does not conflict with the class name in B1.CS.


Compiler Error CS0012

Cannot find a definition for referenced type 'type'

The definition for a referenced type was not found. This could occur if a required .DLL file is not included in the compilation. For example, the following sequence of compilations will result in a CS0012 error:

// B1.CS
class B1 {
   public static void Main() {
   }
}

Then:

// B2.CS; compile with /r:b1.exe
class B2: B1 {
   public static void Main() {
   }
}

Ignoring the CS0114 warning. Then:

// D.CS; compile with /r:b2.exe
class D: B2 {   // CS0012
   public static void Main() {
   }
}

You could resolve this CS0012 by compiling with /r:b2.exe;b1.exe.


Compiler Error CS0013

Unexpected error writing metadata to file 'file' -- 'description'

NGWS failed to emit metadata. This could be a number of problems:

  • Disk full.
  • Corrupt Visual C++; reinstall Visual C++.
  • Corrupt NGWS runtime; reinstall NGWS.


Compiler Error CS0014

Required file 'file' could not be found

A file is needed by the compiler but is not on the system.


Compiler Error CS0015

The name of type 'type' is too long

The name of a user-defined type must be less than or equal to 512 characters.

The following sample generates CS0015:

public class
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC   // CS0015, name greater than 512 characters
{
   public static int Main()
   {
      return 1;
   }
}


Compiler Error CS0016

Could not write to output file 'file' -- 'Access is denied'

The compiler could not write to an output file. The file may have read-only status.


Compiler Error CS0017

Program 'output file name' has more than one entry point defined: function

A program can only have one Main method.

The following sample generates CS0017:

public class clx {
   static public void Main() {
   }
}
public class cly {
   public static void Main() {   // CS0017, delete one Main method
   }
}


Compiler Error CS0019

Operator 'operator' cannot be applied to operands of type 'type' and 'type'

A binary operator is operating on data types for which it was not designed. For example, you cannot use the || operator on strings.

The following sample generates CS0019:

public class MyClass {
   [System.Diagnostics.ConditionalAttribute("DEBUG" || "TRACE")]   //CS0019
   // try the following line instead
   // [System.Diagnostics.ConditionalAttribute("DEBUG")]
   public void TestMethod() {
   }
   public static void Main() {
   }
}

In this example, conditional logic must be specified outside the ConditionalAttribute. You can only pass one predefined symbol to the ConditionalAttribute.


Compiler Error CS0020

Division by constant zero

An expression uses a literal (not variable) value of zero in the denominator of a division operation. Division by zero is not defined, and therefore illegal.

The following sample generates CS0020:

namespace x
{
   public class b
   {
      public static void Main()
      {
         1 / 0;   // CS0020
      }
   }
}


Compiler Error CS0021

Cannot apply indexing with [] to an expression of type 'type'

An attempt was made to access a value via index operation on a data type that does not support array-like operations.

The following sample generates CS0021:

public class MyClass {
   public static void Main() {
      int i = 0;
      i[2];   // CS0021
   }
}


Compiler Error CS0022

Wrong number of indices inside [], expected 'number'

An array-access operation specified multiple indices where only one is allowed.

The following sample generates CS0022:

public class MyClass {
   public static void Main() {
      int[] a;
      a[0] = 0;    // single-dimension array
      a[0,1] = 9;  // CS0022
   }
}


Compiler Error CS0023

Operator 'operator' cannot be applied to operand of type 'type'

An attempt was made to apply an operator to a variable whose type was not designed to work with the operator.

The following sample generates CS0023:

namespace x
{
   public class a
   {
      public static void Main()
      {
         string s = "hello";
         s = -s;   // CS0023, minus operator not allowed on strings
      }
   }
}


Compiler Error CS0024

Source file 'filename' could not be opened ('reason')

A file name was passed to the compiler but could not be located. Make sure that your path is set to find files in all desired directories. Also make sure that you spelled the file name correctly.


Compiler Error CS0025

Standard library file 'file' could not be found

A file that is needed by the compiler was not found. Check your path to ensure that the file is on the path. Also check that the file is on your computer.

You may need to reinstall Visual C++ to resolve this error.


Compiler Error CS0026

Keyword this is illegal in a static property, static method, or static field initializer

The this keyword is not allowed in static methods; you must have an instance before you can refer to an object with this.

The following sample generates CS0026:

public class a {
   public static int i = 0;
   public static void Main() {
      this.i = this.i + 1;   // CS0026
      // try the following line instead
      // i = i + 1;
   }
}


Compiler Error CS0027

Keyword this is not available in the current context

The this keyword was found outside of a property, method, or constructor.

The following sample generates CS0027:

using System;
class CMyClass {
   int err1 = this.fun() + 1;   // CS0027
   public int fun() {
      return 10;
   }
   public void x() {
      int err = this.fun() + 1;   // valid use of this
      Console.WriteLine(err);
   }
   public static void Main() {
      CMyClass c = new CMyClass();
      c.x();
   }
}


Compiler Warning (level 4) CS0028

'function declaration' has the wrong signature to be an entry point

The method declaration for Main was invalid.

The following sample generates CS0028:

namespace x
{
   public class a
   {
      public static void Main(int i)   // CS0028
      // try the following line to resolve CS0028
      // public static void Main()
      {
      }
   }
}


Compiler Error CS0029

Cannot implicitly convert type 'type' to 'type'

You must provide conversion routines to support certain operator overloads.

The following sample generates CS0029:

namespace x
{
   public class iii
   {
/*
      public static implicit operator iii(int aa)
      {
         return null;
      }
      public static implicit operator int(iii aa)
      {
         return 0;
      }
*/
      public static iii operator ++(iii aa)   // CS0029
      // uncomment the conversion routines to resolve CS0029
      {
         return 0;
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0030

Cannot convert type 'type' to 'type'

You must provide conversion routines to support certain operator overloads.

The following sample generates CS0030:

namespace x
{
   public class iii
   {
/*
      public static implicit operator iii(int aa)
      {
         return null;
      }
      public static implicit operator int(iii aa)
      {
         return 0;
      }
*/
      public static iii operator ++(iii aa)
      {
         return (iii)0;
         // uncomment the conversion routines to resolve CS0030
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0031

Constant value 'value' cannot be converted to a 'type'

An attempt was made to assign a value to a variable whose type cannot store the value.

The following sample generates CS0031:

namespace x
{
   public class a
   {
      public static void Main()
      {
         byte i = 512;   // CS0031, 512 cannot fit in byte
      }
   }
}


Compiler Error CS0032

Could not open incremental build file 'file' for writing

During an incremental build (/incremental), the compiler could not open the output file. The output file may have read-only status, which you should change to read/write status.


Compiler Error CS0033

Out of disk space or I/O error writing incremental build file 'file'

During an incremental build (/incremental), the compiler writes to a file containing dependency information used to make rebuild information faster. This write could fail. Perhaps the file had read-only status, which you should change to read/write status.


Compiler Error CS0034

Operator 'operator' is ambiguous on operands of type 'type1' and 'type2'

An operator was used on two objects and the compiler found more than one conversion. Conversions need to be unique, so you either need to make a cast or to make one of the conversions explicit.

The following sample generates CS0034:

public class A {
   // allows for the conversion of A object to int
   public static implicit operator int (A s) {
      return 0;
   }
   public static implicit operator string (A i) {
      return null;
   }
}

public class B {
   public static implicit operator int (B s) {
   // one way to resolve this CS0034 is to make one conversion explicit
//   public static explicit operator int (B s) {
      return 0;
   }
   public static implicit operator string (B i) {
      return null;
   }
   public static implicit operator B (string i) {
      return null;
   }
   public static implicit operator B (int i) {
      return null;
   }
}

public class C {
   public static void Main () {
      A a = new A();
      B b = new B();
      b = b + a;   // CS0034
      // another way to resolve this CS0034 is to make a cast
      // b = b + (int)a;
   }
}


Compiler Error CS0035

Operator 'operator' is ambiguous on an operand of type 'type'

The compiler has more than one available conversion and does not know which to choose before applying the operator.

The following sample generates CS0035:

class MyClass {
   private int i;

   public MyClass(int i) {
      this.i = i;
   }

   public static implicit operator double(MyClass x) {
      return (double) x.i;
   }

   public static implicit operator decimal(MyClass x) {
      return (decimal) x.i;
   }
}

class MyClass2 {
   static void Main() {
      MyClass x = new MyClass(7);
      object o = - x;   // CS0035
      // try a cast:
      // object o = - (double)x;
   }
}


Compiler Error CS0036

An out parameter cannot have the '[in]' attribute

Currently, the in attribute is not permitted on an out parameter.

The following sample generates CS0036:

using System;
public class MyClass {
   public static void TestOut([in] out char TestChar) {   // CS0036
   // try the following line instead
   // public static void TestOut(out char TestChar) {
      TestChar = 'b';
      Console.WriteLine(i);
   }

   public static void Main() {
      char i;   // variable need not be initialized
      TestOut(out i);   // the arg must be passed as out
      Console.WriteLine(i);
   }
}


Compiler Error CS0037

Cannot convert null to 'type' because it is a value type

The compiler cannot assign null to a value type; null can only be assigned to a reference type. struct is a value type.

The following sample generates CS0037:

public struct s {
}
class a {
   public static void Main() {
      int i = null;   // CS0037
      s ss = null;   // CS0037
   }
}


Compiler Error CS0038

Cannot access a non-static member of outer type 'type1' via nested type 'type2'

A field in a class is not automatically available to a nested class. To be available to a nested class, the field must be static. Otherwise, you must create an instance of the outer class.

The following sample generates CS0038:

class OuterClass {
   public int count;
   // try the following line instead
   // public static int count;
   class InnerClass {
      void func() {
         // or, create an instance
         // OuterClass class_inst = new OuterClass();
         // int count2 = class_inst.count;
         int count2 = count;   // CS0038
      }
   }
   public static void Main() {
   }
}


Compiler Error CS0040

Unexpected debug information initialization error -- 'reason'

This error can occur when using the /debug compiler option and indicates that the compiler was unable to write to the .pdb file. Possible resolutions to this error include reinstalling Visual C++, ensuring that the compiler has write access to a file or directory, or not compiling with /debug.


Compiler Error CS0041

Unexpected error writing debug information to file 'file' -- 'reason'

This error can occur when using the /debug compiler option. If you are unable to resolve the error based on the information in reason, do not compile with /debug.


Compiler Error CS0050

Inconsistent accessibility: return type 'type' is less accessible than method 'method'

A public construct must return a publicly accessible object.

The following sample generates CS0050:

class MyClass {
// try the following line instead
// public class MyClass {
}

public class MyClass2 {
   public static MyClass MyMethod() { // CS0050
      return new MyClass();
   }
   public static void Main() {
   }
}


Compiler Error CS0051

Inconsistent accessibility: parameter type 'type' is less accessible than method 'method'

Public constructs need to use public objects.

The following sample generates CS0051:

class A {
// try the following line instead
// public class A {
   public static implicit operator A(int i) {return null;}
}

class B {
// try the following line instead
// public class B {
   public static implicit operator B(int i) {return null;}
}

public class C {
   public void f (A a) {
   }

   public void f (B b) {
   }

   public static void Main () {
      C c = new C();
      c.f((A)5);     // CS0051, could convert either to A or B
   }
}


Compiler Error CS0052

Inconsistent accessibility: field type 'type' is less accessible than field 'field'

A public construct must return a publicly accessible object.

The following sample generates CS0052:

class MyClass {
// try the following line instead
// public class MyClass {
}

public class MyClass2 {
   public MyClass mf;   // CS0052
}

public class MyClass3 {
   public static void Main() {
   }
}


Compiler Error CS0053

Inconsistent accessibility: property type 'type' is less accessible than property 'property'

A public construct must return a publicly accessible object.

The following sample generates CS0053:

class MyClass {
// try the following line instead
// public class MyClass {
}

public class MyClass2 {
   public MyClass MyProperty {   // CS0053
      get {
         return new MyClass();
      }
      set {
      }
   }
}

public class MyClass3 {
   public static void Main() {
   }
}


Compiler Error CS0054

Inconsistent accessibility: indexer return type 'type' is less accessible than indexer 'indexer'

A public construct must return a publicly accessible object.

The following sample generates CS0054:

class MyClass {
// try the following line instead
// public class MyClass {
}

public class MyClass3 {
   public MyClass this[int i] {   // CS0054
      get {
         return new MyClass();
      }
   }
}

public class MyClass2 {
   public static void Main() {
   }
}


Compiler Error CS0055

Inconsistent accessibility: parameter type 'type' is less accessible than indexer 'indexer'

A public construct must return a publicly accessible object.

The following sample generates CS0055:

class MyClass {
// try the following line instead
// public class MyClass {
}

public class MyClass2 {
   public int this[MyClass i] {   // CS0055
      get {
         return 0;
      }
   }
}

public class MyClass3 {
   public static void Main() {
   }
}


Compiler Error CS0056

Inconsistent accessibility: return type 'type' is less accessible than operator 'operator'

A public construct must return a publicly accessible object.

The following sample generates CS0056:

class ii {
// try the following line instead
// public class ii {
}

public class a {
   public static implicit operator ii(a aa) { // CS0056
      return new ii();
   }
   public static void Main() {
   }
}


Compiler Error CS0057

Inconsistent accessibility: parameter type 'type' is less accessible than operator 'operator'

A public construct must return a publicly accessible object.

The following sample generates CS0057:

class MyClass {
// try the following line instead
// public class MyClass {
}

public class MyClass2 {
   public static implicit operator MyClass2(MyClass iii) {   // CS0057
      return new MyClass2();
   }
   public static void Main() {
   }
}


Compiler Error CS0058

Inconsistent accessibility: parameter type 'type' is less accessible than delegate 'delegate'

A public construct must return a publicly accessible object.

The following sample generates CS0058:

class MyClass {
// try the following line instead
// public class MyClass {
}

public delegate MyClass MyClassDel();   // CS0058

public class a {
   public static void Main() {
   }
}


Compiler Error CS0059

Inconsistent accessibility: parameter type 'type' is less accessible than delegate 'delegate'

A public construct must return a publicly accessible object.

The following sample generates CS0059:

class MyClass {
// try the following line instead
// public class MyClass {
}

public delegate void MyClassDel( MyClass iii);   // CS0059

public class a {
   public static void Main() {
   }
}


Compiler Error CS0060

Inconsistent accessibility: base class 'class1' is less accessible than class 'class2'

A public construct must return a publicly accessible object.

The following sample generates CS0060:

class MyClass {
// try the following line instead
// public class MyClass {
}

public class MyClass2 : MyClass {   // CS0060
   public static void Main() {
   }
}


Compiler Error CS0061

Inconsistent accessibility: base interface 'interface 1' is less accessible than interface 'interface 2'

A public construct must return a publicly accessible object.

The following sample generates CS0061:

// resolve this CS0061 by either making a public and b internal
// or by making both interfaces internal
internal interface a {
   void mf();
}

public interface b : a {
   new void mf();
}

class x {
   public static void Main() {
   }
}


Compiler Error CS0065

'event' : event property must have both get and set accessors

An event property must have both access methods.

The following sample generates CS0065:

using System;
public delegate void Eventhandler(object sender, int e);
public class MyClass {
   public event EventHandler Click {   // CS0065, no set
      get {
         return null;
      }
      // uncomment this set definition
/*
      set {
      }
*/
   }

   public static void Main() {
   }
}


Compiler Error CS0066

'event': event must be of a delegate type

An event must be a delegate.

The following sample generates CS0066:

using System;
public class EventHandler {
}

   // try the following declaration for EventHandler
   // public delegate void EventHandler(object sender, int e);

public class a {

   public event EventHandler Click {   // CS0066
      get {
         return null;
      }

      set {
      }

   }

   public static void Main() {
   }
}


Compiler Error CS0067

The event 'event' is never used

An event was declared but never used in the class in which it was declared.

The following sample generates CS0067:

using System;
delegate void MyDelegate();

class MyClass {
   public event MyDelegate evt;   // CS0067
   // uncomment TestMethod to resolve this CS0067
/*
   private void TestMethod() {
      if (evt != null)
         evt();
   }
*/
   public static void Main() {
   }
}


Compiler Error CS0068

'event': event in interface cannot have initializer

An event in an interface cannot have an initializer.

The following sample generates CS0068:

delegate void MyDelegate();
interface I {
   event MyDelegate d = new MyDelegate(M.f);   // CS0068
   // try the following line instead
   // event MyDelegate d2;
}

class M {
   event MyDelegate d = new MyDelegate(M.f);

   public static void f() {
   }

   public static void Main() {
   }
}


Compiler Error CS0069

'event': event in interface cannot have get or set accessors

You cannot define the get or set accessor functions of an event in an interface.

The following sample generates CS0069:

using System;
public delegate void EventHandler(object sender, int e);

public interface a {
   event EventHandler Click {   // CS0069
      get {
         return null;
      }

      set {
      }

   }
}
   // try the following line instead
   // event EventHandler Click;

public class b {
   public static void Main() {
   }
}


Compiler Error CS0070

The event 'event' can only appear on the left hand side of += or -= (except when used from within the class it is defined in)

Outside of the class it is defined in, an event can only add or subtract references.

The following sample generates CS0070:

using System;
public delegate void EventHandler();

public class A {

   public event EventHandler Click {
      get {
         return null;
      }
      set {
      }
   }

   public static void OnClick() {
      EventHandler eh;
      A a = new A();
      eh = a.Click;
   }

   public static void Main() {
   }
}

public class B {
   public int mf () {
      EventHandler eh = new EventHandler(A.OnClick);
      A a = new A();
      eh = a.Click;   // CS0070
      // try the following line instead
      // a.Click += eh;
      return 1;
   }
}


Compiler Error CS0071

An explicit interface implementation of an event must use property syntax

When implementing an event that was declared in an interface, you must use property syntax.

The following sample generates CS0071:

public delegate void MyEvent(object sender);

interface ITest {
   event MyEvent Clicked;
}

class Test : ITest {
   event MyEvent ITest.Clicked() {   // CS0071
   }

   // try the following code instead
   /*
   event MyEvent ITest.Clicked {
      get {
         return null;
      }
      set {
      }
   }
   */

   public static void Main() {
   }
}


Compiler Error CS0075

To cast a negative value, you must enclose the value in parentheses

Values that you want to cast must be in parentheses.

The following sample generates CS0075:

using System;
enum Myint : long {
   three = 3L,
}

class test {
public static void Main(String [] args ) {
   Myint i = (Myint) -1 ;   // CS0075
   // try the following line instead
   // Myint i = (Myint) (-1) ;
   i++;
   }
}

See Also

() Operator


Compiler Error CS0076

The enumerator name 'value__' is reserved and cannot be used

An item in an enumeration cannot have an identifier called value__.

The following sample generates CS0076:

using System;
enum Myint : long {
   value__ = 3L   // CS0076, cannot use the name value__
}

class MyClass{
   public static void Main() {
   }
}


Compiler Error CS0077

The as operator must be used with a reference type ('type' is a value type)

The as operator was passed a value type. Because as can return null, it can only be passed reference types.

The following sample generates CS0077:

using System;

class C {
}

struct S {
}

class M {
   public static void Main() {
      object o1, o2;
      C c;
      S s;

      o1 = new C();
      o2 = new S();

      s = o2 as S;  // CS0077, S is not a reference type.
      // try the following line instead
      // c = o1 as C;
   }
}


Compiler Error CS0100

The parameter name 'parameter name' is a duplicate

A method declaration used the same parameter name more than once. Parameter names must be unique in a method declaration.

The following sample generates CS0100:

namespace x
{
   public class a
   {
      public static void f(int i, char i)   // CS0100
      // try the following line instead
      // public static void f(int i, char j)
      {
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0101

The namespace 'namespace' already contains a definition for 'type'

A namespace has duplicate identifiers. Rename or delete one of the duplicate identifiers.

The following sample generates CS0101:

namespace x
{
   public class clx
   {
      static public void Main(){}
   }
   public class clx   // CS0101
   {
   }
}


Compiler Error CS0102

The class 'class' already contains a definition for 'identifier'

A class contains multiple declarations for identifiers with the same name; delete or rename one of the duplicate identifiers.

The following sample generates CS0102:

namespace x
{
   abstract public class clx
   {
      int i;
      enum splitch : int
      {
         x, x // CS0102
      };
      public static int Main()
      {
         return 0;
      }
   }
}


Compiler Error CS0103

The name 'name' does not exist in the class or namespace 'namespace'

An attempt was made to use a name that does not exist in the namespace. For example, attempting to derive a class from an unknown class name produces CS0103.

The following sample generates CS0103:

namespace x
{
   public class clx : abc   // CS0103, what is abc?
   {
      public static void Main() {
      }
   }
}


Compiler Error CS0104

'reference' is an ambiguous reference

Your program contains using directives for two namespaces and your code references a name that appears in both namespaces.

The following sample generates CS0104:

using x;
using y;

namespace x {
   public class Test {
   }
}

namespace y {
   public class Test {
   }
}

public class a {
   public static void Main() {
      Test test = new Test();   // CS0104, is Test in x or y namespace?
      // try the following line instead
      // y.Test test = new y.Test();
   }
}


Compiler Error CS0105

The using directive for 'namespace' appeared previously in this namespace

A namespace, which can only be declared once, was declared more than once; remove all duplicate namespace declarations.

The following sample generates CS0105:

using System;
using System;   // CS0105

public class a {
   public static void Main() {
   }
}


Compiler Error CS0106

The modifier 'modifier' is not valid for this member type

A class or interface member was marked with an invalid access modifier. For example:

  • The static and public modifiers are not permitted on interface methods.
  • The static modifier is not permitted on a class.

It is also possible to generate CS0106 by applying the public keyword to an explicit interface declaration. In this case, remove the public keyword from the explicit interface declaration.

The following sample generates CS0106:

namespace x {
   interface I {
      void m();
      static public void f();   // CS0106
   }
   public class clx {
      public void I.m() {   // CS0106
      }
      public static int Main() {
         return 0;
      }
   }
}


Compiler Error CS0107

More than one protection modifier

Only one access modifier (public, private, protected, or internal) is allowed on a class member.

The following sample generates CS0107:

public class C {
   private internal void f() {   // CS0107, delete private or internal
   }
   public static int Main() {
      return 1;
   }
}


Compiler Warning (level 1) CS0108

The keyword new is required on 'member1' because it hides inherited member 'member2'

A variable was declared with the same name as a variable in a base class. However, the new keyword was not used. This warning informs you that you should use new; the variable is declared as if new had been used in the declaration.

The following sample generates CS0108:

using System;
namespace x
{
   public class clx
   {
      public int i = 1;
   }
   public class cly : clx
   {
      public static int i = 2;   // CS0108, use the new keyword
      // the compiler parses the previous line as if you had specified:
      // public static new int i = 2;

      public static void Main()
      {
         Console.WriteLine(i);
      }
   }
}


Compiler Warning (level 4) CS0109

The member 'member' does not hide an inherited member. The new keyword is not required

A class declaration included the new keyword even though the declaration does not override an existing declaration in a base class. You can delete the new keyword.

The following sample generates CS0109:

namespace x
{
   public class a
   {
      public int i;
   }

   public class b : a
   {
      public new int i;
      public new int j;   // CS0109
      public static void Main()
      {
      }
   }
}


Compiler Error CS0110

The evaluation of the constant value for 'const declaration' involves a circular definition

The declaration of a const variable (a) cannot reference another const variable (b) that also references (a).

The following sample generates CS0110:

namespace x
{
   public class a
   {
      public static void Main()
      {
      }
   }
   public class b : a
   {
      public const int i = c + 1;   // CS0110, c already references i
      public const int c = i + 1;
      // the following line would be OK
      // public const int c = 10;
   }
}


Compiler Error CS0111

Class 'class' already defines a member called 'member' with the same parameter types

A class contains two identical member declarations.

The following sample generates CS0111:

namespace x
{
   abstract public class clx
   {
      public abstract void f();
   }

   public class cly : clx
   {
      public void f()
      {
      }
      override public void f() //CS0111, duplicate declaration
      {
      }
      public static int Main()
      {
         return 0;
      }
   }
}


Compiler Error CS0112

A static member 'function' cannot be marked as override, virtual or abstract

Any method declaration that uses the override, virtual, or abstract keyword cannot also use the static keyword.

The following sample generates CS0112:

namespace x
{
   abstract public class clx
   {
      public abstract void f();
   }

   public class cly : clx
   {
      override public static void f()   // CS0112, remove static keyword
      {
      }
      public static int Main()
      {
         return 0;
      }
   }
}

See Also

Modifiers


Compiler Error CS0113

A member 'function' marked as override cannot be marked as new, virtual, or abstract

It is mutually exclusive to mark a method with the new and override keywords.

The following sample generates CS0113:

namespace x
{
   abstract public class clx
   {
      public abstract void f();
   }

   public class cly : clx
   {
      override new public void f()   // CS0113, remove new keyword
      {
      }
      public static int Main()
      {
         return 0;
      }
   }
}


Compiler Warning (level 2) CS0114

'function1' hides inherited member 'function2'. To make the current method override that implementation, add the override keyword. Otherwise add the new keyword.

A declaration in a class conflicts with a declaration in a base class such that the base class member will be hidden.

The following sample generates CS0114:

namespace x {
   abstract public class clx
   {
      public abstract void f();
   }

   public class cly : clx
   {
      public void f(){} // CS0144, hides base class member, try:
      // override public void f(){}
      public static int Main()
      {
         return 0;
      }
   }
}

See Also

base


Compiler Error CS0115

'function' : no suitable method found to override

A method was marked as an override, but the compiler found no method to override.

The following sample generates CS0115:

namespace x
{
   abstract public class clu
   {
      public abstract int f();
   }

   abstract public class clx
   {
      public override int f()   // CS0115
      {
         return 0;
      }
      public static void Main()
      {
      }
   }
}

You can resolve CS0115 in one of two ways:

  • Remove the override keyword from the method in class clx.
  • Use clu as a base class for clx.


Compiler Error CS0116

A namespace does not directly contain members such as fields or methods

Inside a namespace, the compiler only accepts classes, structs, unions, enums, interfaces, and delegates.

The following sample generates CS0116:

namespace x {
   int xx;   // CS0116
}


Compiler Error CS0117

'type' does not contain a definition for 'function'

A call was made to a method that does not exist for the data type. This can also happen when a class name and its enclosing namespace name are the same and when a qualified method is called.

The following sample generates CS0117:

namespace x {
   public class a {
      public static void Main() {
         int i;
         i = i.get();   // CS0117, no get method on an int
      }
   }
}

When using indexed properties, it is not necessary to use the Item specifier:

using System;
using System.Collections;
class Test {
   public static void Main() {
      ArrayList al = new ArrayList();
      al.Add( new Test() );
      al.Add( new Test() );
      Console.WriteLine("First Element is {0}", al.Item[0]);   // CS0117
      // try the following line instead
      // Console.WriteLine("First Element is {0}", al.[0]);
   }
}


Compiler Error CS0118

'construct1_name' denotes a 'construct1' where a 'construct2' was expected

The compiler detected what was probably the instantiation of a class; however, the data type was a namespace.

The following sample generates CS0118:

namespace x {
   class a {
      x ix = new x();   // CS0118, x not a class!
      public static void Main() {
      }
   }
}


Compiler Error CS0119

'construct1_name' denotes a 'construct1' which is not valid in the given context

The compiler detected an unexpected construct. For example:

  • A class constructor is not valid as the test expression in a conditional statement.
  • A class name was used instead of an instance name when referring to an array element.

The following sample generates CS0119:

namespace a {
   public class z {
      public z() {
      }
      public static void Main() {
         (z()) ? 0 : 1;  // CS0119, cannot use constructor in a conditional
         // the following line is a more acceptable conditional statement
         // 1 < 2 ? 0 : 1;
      }
   }
}


Compiler Error CS0120

An object reference is required for the nonstatic field, method, or property 'member'

A variable declared outside of a method needs to have the same static declaration as the method in which it is used.

The following sample generates CS0120:

public class clx {
   public int i;   // not static
   public static void Main() {
   }
   public static void f() {
      i = 9;   // CS0120, in a static method
      // try the following lines instead
      // clx Myclx = new clx();
      // Myclx.i = 9;
   }
}

CS0120 will also be generated if there is a call to a nonstatic method from a static method:

using System;
public class MyClass {
   public static void Main() {
      TestCall();   // CS0120
      // To call a non-static method from Main,
      // first create an instance of the class
      // MyClass anInstanceofMyClass = new MyClass();
      // anInstanceofMyClass.TestCall();
   }
   public void TestCall() {
   }
}

Similarly, a static method cannot call an instance method unless you explicitly give it an instance of the class. For example, the following sample also generates CS0120:

using System;
public class x {
   public static void Main() {
      do_it("Hello There");   // CS0120
   }
   private void do_it(string sText) {
   // you could also add the keyword static to the method definition
   // private static void do_it(string sText) {
      Console.WriteLine(sText);
   }
}


Compiler Error CS0121

The call is ambiguous between 'function1' and 'function2'

Due to implicit conversion, the compiler was not able to call one form of an overloaded method. You can resolve this error either by specifying the method parameters in such a way that implicit conversion does not take place. Or, you could remove all overloads for the method.

The following sample generates CS0121:

public class C {
   void f(int i, double d) {
}
   void f(double d, int i) {
}
   public static void Main() {
      C c = new C();

      c.f(1, 1);   // CS0121
      // try the following line instead
      // c.f(1, 1.0);
      // or
      // c.f(1.0, 1);
   }
}


Compiler Error CS0122

'member' is inaccessible due to its protection level

The access modifier for a class member prevents that member’s use in a derived class.

The following sample generates CS0122:

public class clx {
   void f()   {   // make public to resolve CS0122
   }

public class clz {
   public static int Main() {
   clx a = new clx();
   a.f();   // CS0122, f is private
   return 0;
   }
}


Compiler Error CS0123

The signature of method 'function' does not match this delegate type

An attempt to use a method failed because the correct overloaded form was not present. You can resolve this error by adjusting your method call so that it finds a match.

The following sample generates CS0123:

namespace x
{
   public class clx
   {
      public clx(int i)
      {
      }
   }
   public class cly : clx   // CS0123
   {
      static cly(){}
      public static void Main()
      {
      }
   }
}


Compiler Error CS0126

An object of a type convertable to 'type' required

A return statement is present, but the statement does not return a value of the expected type.

The following sample generates CS0126:

// CS0126
namespace x
{
   public class a
   {
      public int i
      {
         set
         {
         }
         virtual get
         {
            return;   // CS0126, specify a value to return
         }
      }
   }
}


Compiler Error CS0127

Since 'function' returns void, a return keyword must not be followed by an object expression

A method with a void return type cannot return a value.

The following sample generates CS0127:

namespace x
{
   public class b
   {
      public int hiddenMember2
      {
         get
         {
            return 0;
         }
         set   // CS0127, set has an implicit void return type
         {
            return 0;   // remove return statement to resolve this CS0127
         }
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0128

A local variable named 'variable' is already defined in this scope

The compiler detected declarations of two local variables with the same name.

The following sample generates CS0128:

namespace x
{
   public class a
   {
      public static void Main()
      {
         char i;
         int i;   // CS0128
      }
   }
}


Compiler Error CS0131

The left-hand side of an assignment must be a variable, property or indexer

An assignment requires an l-value; check your syntax.

The following sample generates CS0131:

public class MyClass {
   public int i = 0;
   public void MyMethod()   {
      i++ = 1;   // CS0131
      // try the following line instead
      // i = 1;
   }
   public static void Main() {
   }
}


Compiler Error CS0132

'constructor' : a static constructor must be parameterless

A static constructor cannot be declared with one or more parameters.

The following sample generates CS0132:

namespace x
{
   public class clx
   {
      public clx(int i){}
   }

   public class cly : clx
   {
      static cly(int i)   // CS0132
      {
      }
   }
}

See Also

static


Compiler Error CS0133

The expression being assigned to 'variable' must be constant

A const variable cannot take as its value a variable that is not const.

The following sample generates CS0133:

namespace x
{
   public class a
   {
      public static void Main()
      {
      }
   }
   public class b : a
   {
      public const int i = c;   // CS0133, c is not const
      public static int c = i;
      // try the following line to resolve CS0133
      // public const int c = 6;
   }
}


Compiler Error CS0134

Cannot use qualified namespace names in nested namespace declarations

A qualified namespace is not permitted in a namespace declaration.

The following sample generates CS0134:

namespace MyNameSpace {
   namespace MyNameSpace.DottedNamespace {   // CS0134, qualified namespace
   // try the following line instead
   // namespace NotRoot {
      public class MyClass {
         public static void Main() {
         }
      }
      namespace DottedNamespace    {
         public class MyClass2 {
            public static void f1() {
               MyNameSpace.NotRoot.MyClass.Main();   // legal use of qualified namespace
            }
         }
      }
   }
}


Compiler Error CS0135

'declaration1' conflicts with the declaration 'declaration2'

The compiler does not allow name hiding, which commonly leads to logic errors in your code. Consider the following sample:

public class MyClass2 {
   public static int i = 0;
   public static void Main() {
      {
         int i = 4;
         i++;
      }
      i = 0;   // CS0135
   }
}


Compiler Error CS0136

A local variable named 'var' cannot be declared in this scope because it would give a different meaning to 'var', which is already used in a 'parent or current' scope to denote something else

A variable declaration hides another declaration that would otherwise be in scope. Rename the variable that is declared on the line that generated CS0136.

The following sample generates CS0136:

namespace x
{
   public class a
   {
      public static void Main()
      {
         int i = 0;
         {
            char i = 'a';   // CS0136, hides int i
         }
         i++;
      }
   }
}


Compiler Error CS0138

A using namespace directive can only be applied to namespaces; 'class' is a class not a namespace

A using directive can only take the name of a namespace as a parameter.

The following sample generates CS0138:

using System.Object;   // CS0138


Compiler Error CS0139

No enclosing loop out of which to break or continue

A break or continue statement was encountered outside of a loop.

The following sample generates CS0139 twice:

namespace x
{
   public class a
   {
      public static void Main()
      {
         continue;  // CS0139
         break;     // CS0139
      }
   }
}

See Also

Jump Statements


Compiler Error CS0140

The label 'label' is a duplicate

A label with the same name appeared twice.

The following sample generates CS0140:

namespace x
{
   public class a
   {
      public static void Main()
      {
         l1: int i = 0;
         l1: int j = 0;   // CS0140, comment this line to resolve
         goto l1;
      }
   }
}


Compiler Error CS0143

The type 'class' has no constructors defined

A class was not able to call the constructor of its base class because the constructor does not exist.

CS0143 will also occur if the base class has no constructor (is created in a language that allows a class without a constructor).

The following sample generates CS0143:

class nmc {
   static public void Main () {
      double d = new double(4.5);   // CS0143
   }
}


Compiler Error CS0144

Cannot create an instance of the abstract class or interface 'interface'

You cannot create an instance of an abstract class or interface.

The following sample generates CS0144:

interface ii {
}
public class a {
   public static void Main() {
      ii xx = new ii();   // CS0144
   }
}


Compiler Error CS0145

A const field requires a value to be provided

You must initialize a const variable.

The following sample generates CS0145:

class x {
   const int i;   // CS0145
   // try the following line instead
   // const int i = 0;
   public static void Main() {
   }
}


Compiler Error CS0146

Circular base class definition between 'class1' and 'class2'

The inheritance list for a class includes a direct or indirect reference to itself. A class cannot inherit from itself.

The following sample generates CS0146:

namespace x {
   public interface a {
   }
   public class b : a, c {
      public void Main() {
      }
   }
   public class c : b {   // CS0146
   }
}


Compiler Error CS0148

The delegate 'delegate' does not have a valid constructor

You imported and used a managed program (one that uses the NGWS runtime) that was created with another compiler. That compiler allowed an ill-formed delegate constructor.


Compiler Error CS0149

Method name expected

When creating a delegate, specify a method.

The following sample generates CS0149:

using System;
delegate string func(int i);

class a {
   static func dt;   // class member-field of the declared delegate type
   public static void Main() {
      dt = new func(17.45);   // CS0149
      // try the following line instead
      // dt = new func(func2);
      x(dt);
   }

   public static string func2(int j) {
      Console.WriteLine(j);
      return j.ToString();
   }

   public static void x(func myFunc) {
      myFunc(8);
   }
}


Compiler Error CS0150

A constant value is expected

A variable was found where a constant was expected.

The following sample generates CS0150:

namespace x
{
   public class a
   {
      public static void Main()
      {
         int i = 0;
         int j = 0;
         switch(i)
         {
            case j:   // CS0150 j is a variable int, not a constant int
            // try something similar to the following line
            // case 1:
         }
      }
   }
}


Compiler Error CS0151

A value of an integral type expected

A variable was used in a situation where an integral data type was required. This can happen when there is no conversion or if the available implicit conversions result in an ambiguous situation.

The following sample generates CS0151:

public class iii {
   public static implicit operator int (iii aa) {
      return 0;
   }
   public static implicit operator long (iii aa) {
      return 0;
   }
   public static void Main() {
      iii a = new iii();
      switch (a) {   // CS0151, compiler cannot choose between int and long
      // casting is one way to resolve this error
      // switch ((int)a) {
      case 1:
         break;
      }
   }
}


Compiler Error CS0152

The label 'label' already occurs in this switch statement

A label was repeated in a switch statement.

The following sample generates CS0152:

namespace x
{
   public class a
   {
      public static void Main()
      {
         int i = 0;
         switch (i)
         {
            case 1:
               i++;
               return;

            case 1:   // CS0152, two case 1 statements
               i++;
               return;
         }
      }
   }
}


Compiler Error CS0153

A goto case is only valid inside a switch statement

An attempt was made to use switch syntax outside of a switch statement.

The following sample generates CS0153:

public class a
{
   public static void Main()
   {
      goto case 5;   // CS0153
   }
}


Compiler Error CS0154

The property or indexer 'property' cannot be used in this context because it lacks the get accessor

An attempt to use a property failed because no get accessor method was defined in the property.

The following sample generates CS0154:

public class b {
   public int i {
      set {
      }
      // uncomment the get method to resolve this CS0154
      /*
      get {
         return 0;
      }
      */
   }
}

public class a {
   public static void Main() {
      b bb = new b();
      int j = bb.i;   // CS0154, no get method
   }
}


Compiler Error CS0155

The type caught or thrown must be derived from System.Exception

An attempt was made to pass a data type that does not derive from System.Exception into a catch block. Only data types that derive from System.Exception can be passed into a catch block.

The following sample generates CS0155:

using System;
namespace x
{
   public class b
   // try the following line instead
   // public class b : Exception
   {
   }
   public class a
   {
      public static void Main()
      {
         try
         {
         }

         catch(b)   // CS0155, resolves if you derive b from Exception
         {
         }
      }
   }
}

See Also

Exception Handling Statements


Compiler Error CS0156

An argumentless throw statement is not allowed outside of a catch clause

A throw statement with no parameters can only appear in a catch clause that takes no parameters.

The following sample generates CS0156:

using System;
namespace x
{
   public class b : Exception
   {
   }
   public class a
   {
      public static void Main()
      {
         try
         {
            throw;   // CS0156
         }
         catch(b)
         {
            throw;   // this throw is valid
         }
      }
   }
}

See Also

Exception Handling Statements


Compiler Error CS0157

Control cannot leave the body of a finally clause

All of the statements in a finally clause must execute.

The following sample generates CS0157:

using System;
namespace x
{
   public class b : Exception
   {
   }

   public class a
   {
      public static void Main()
      {
         try
         {
         }

         finally
         {
            return;   // CS0157, cannot leave finally clause
         }
      }
   }
}

See Also

Exception Handling Statements


Compiler Error CS0158

The label 'label' shadows another label by the same name in a contained scope

A label in an inner scope hides a label with the same name in an outer scope.

The following sample generates CS0158:

namespace x
{
   public class a
   {
      public static void Main()
      {
         goto lab1;
         lab1:
         {
            lab1:
            goto lab1;   // CS0158
         }
      }
   }
}


Compiler Error CS0159

No such label 'label' within the scope of the goto statement

The scope defined by a switch statement does not include the label that was referenced in a goto statement.

The following sample generates CS0159:

namespace x
{
   public class a
   {
      public static void Main()
      {
         int i = 0;
         switch (i)
         {
            case 1:
               goto case 3;   // CS0159, no case 3 label
            case 2:
         }
      }
   }
}


Compiler Error CS0160

A previous catch clause already catches all exceptions of this or a super type ('type')

A series of catch statements needs to be in decreasing order of derivation. For example, the most derived objects have to appear first.

The following sample generates CS0160:

using System;
public class b : Exception {
}

public class a {
   public static void Main() {
      try {
      }

      catch(Exception) {   // second most derived, should be second catch
      }

      catch(b) {   // CS0160 most derived, should be first catch
      }

      catch {   // must be last catch
      }
   }
}

See Also

Exception Handling Statements


Compiler Error CS0161

'method': not all code paths return a value

A method that returns a value must have a return statement.

The following sample generates CS0161:

public class ii {
   int i   {
      get {   // CS0161
      // uncomment the following line to resolve
      // return 0;
      }
   }
}
public class a {
   public static void Main() {
   }
}


Compiler Error CS0162

Unreachable code detected

The compiler detected code that will never be executed.

The following sample generates CS0162:

public class a {
   public static void Main() {
      lab1:
      goto lab1; {
         int i = 9;   // CS0162, never reached
         i++;
      }
   }
}


Compiler Error CS0163

Control cannot fall through from one case label ('label') to another

When a case statement contains one or more statements and is followed by another case statement, one of the following statements must explicitly terminate the case:

  • return
  • goto
  • break

If you want to implement "fall through" behavior, use goto case #.

The following sample generates CS0163:

public class MyClass {
   public static void Main() {
      int i = 0;
      switch (i) {
         case 1:
            i++;
            // uncomment one of the following lines to resolve
            // return;
            // break;
            // goto case 3;
         case 2:
            i++;
            return;
         case 3:
            i = 0;
            return;
      }
   }
}


Compiler Error CS0164

This label has not been referenced

A label was declared but never used.

The following sample generates CS0164:

namespace x
{
   public class a
   {
      public int i = 0;
      public static void Main()
      {
         l1: int i = 0;   // CS0164
         i++;
         // the following line resolves this CS0164
         // goto l1;
      }
   }
}


Compiler Error CS0165

Use of possibly unassigned local variable 'var'

An attempt was made to manipulate a reference that was not assigned to an object.

The following sample generates CS0165:

class MyClass {
   public static void Main() {
      string str;   // CS0165
      // try the following line instead
      // string str = new string("Testing");
      System.Console.WriteLine("Length is :" + str.Length);
   }
}


Compiler Error CS0167

The delegate 'delegate' is missing the Invoke method

You imported and used a managed program (one that uses the NGWS runtime) that was created with another compiler. That compiler allowed an ill-formed delegate, such that the Invoke method was not available.

See Also

delegate


Compiler Warning (level 3) CS0168

The variable 'var' is declared but never used

The compiler warns when a variable is declared but not used.

The following sample generates two CS0168 warnings:

public class clx {
   public int i;
}
public class clz {
   public static void Main() {
      int j = 0;   // CS0168, uncomment the following line
      // j++;
      clx a;   // CS0168, try the following line instead
      // clx a = new clx();
   }
}


Compiler Warning (level 3) CS0169

The private field 'class member' is never used

A private variable was declared but never referenced. A common way to generate this warning is when you declare a private member of a class and do not modify the value of the member inside the class. To resolve a warning of this nature, either remove the field or add the public keyword to the field's declaration.

The following sample generates CS0169:

public class clx {
   int i;   // CS0169
   // try the following line instead
   // public int i;
   public static void Main() {
   }
}


Compiler Error CS0170

Use of possibly unassigned field 'field'

A field in a structure was used without being initialized. The field must first be initialized.

The following sample generates CS0170:

public struct error {
   public int i;
}

public class CMyClass {
   public static void Main() {
      error e;
      // uncomment the next line to resolve this CS0170
      // e.i = 0;
      System.Console.WriteLine( e.i );   // CS0170
   }
}


Compiler Error CS0171

Field 'field' must be fully assigned before control leaves the constructor

A constructor in a struct must initialize all fields in the struct.

The following sample generates CS0171:

struct MyStruct {
   MyStruct(int initField) {   // CS0171
      // i = initField;   // uncomment to resolve this CS0171
   }
   public int i;
}
class MyClass {
   public static void Main() {
      MyStruct aStruct = new MyStruct();
   }
}


Compiler Error CS0172

Type of conditional expression can't be determined because 'type1' and 'type2' both implicitly convert to each other

In a conditional statement, the types on either side of the : operator have to be convertible. Plus, there cannot be mutual conversion routines; you only need one conversion.

public class Square {

   public class Circle {
      public static implicit operator Circle(Square aa) {
         return null;
      }

      public static implicit operator Square(Circle aa)   {
      // changing this declaration to use explicit resolves this error
      // public static explicit operator Square(Circle aa) {
         return null;
      }
   }
   public static void Main() {
      Circle aa = new Circle();
      Square ii = new Square();
      object o = (1 == 1) ? aa : ii;   // CS0172
      // the following cast would resolve this error
      // (1 == 1) ? aa : (Circle)ii;
   }
}


Compiler Error CS0173

Type of conditional expression can't be determined because there is no implicit conversion between 'class1' and 'class2'

Conversions between classes are useful when you want objects of different classes to work with the same code. However, two classes that will work together cannot have mutual and redundant conversions. To resolve CS0173, make sure that there is one and only one conversion, regardless of which direction the conversion is in and regardless of which class the conversion is in.

// to resolve, remove one conversion routine
public class iii {

   // a conversion routine
   public static implicit operator a(iii aa) {
      return new a();
   }
   // a conversion routine
   public static implicit operator iii(a aa) {
      return new iii();
   }
   public static void Main() {
       a aa = new a();
       iii ii = new iii();

       a aaa = (1 == 1) ? aa : ii; // CS0173
   }
}

public class a {
   // a conversion routine
   public static implicit operator a(iii aa) {
      return new a();
   }
   // a conversion routine
   public static implicit operator iii(a aa) {
      return new iii();
   }
}


Compiler Error CS0174

A base class is required for a base reference

CS0174 can occur only when the NGWS runtime compiles the source code for the System.Object class, which is the only class that has no base class.


Compiler Error CS0175

Use of keyword base is illegal in this context

The base keyword must be used to specify a particular member of the base class.

The following sample generates CS0175:

using System;
class BaseClass {
   public int TestInt = 0;
}

class MyClass : BaseClass {
   public static void Main() {
      MyClass aClass = new MyClass();
      aClass.BaseTest();
   }

   public void BaseTest() {
      Console.WriteLine(base);   // CS0175
      // try the following line instead
      // Console.WriteLine(base.TestInt);
      base = 9;   // CS0175
      // try the following line instead
      // base.TestInt = 9;
   }
}


Compiler Error CS0176

Static member 'member' cannot be accessed with an instance reference; use typename instead

Only a class name can be used to qualify a static variable; an instance name cannot be a qualifier.

The following sample generates CS0176:

public class b {
   public static int ii;
}

public class a {
   public static void Main() {
      b bb = new b();
      int i = bb.ii;   // CS0176
      // try the following line instead
      // int i = b.ii;
   }
}


Compiler Error CS0177

The out parameter 'parameter' must be assigned to before control leaves the current method

A parameter marked with the out keyword was not assigned a value in the method body.

The following sample generates CS0177:

public class iii {
   public void f(out int i) {   // CS0177
   // uncomment the following line for a resolution
   //   i = 0;
   }

   public static void Main() {
   }
}


Compiler Error CS0178

Incorrectly structured array initializer

An array initialization was ill formed. When specifying the array dimensions, you can specify:

  • A number in brackets.
  • Empty brackets.
  • A comma enclosed in brackets.

The following sample generates CS0178:

class x {
   public static void Main() {
      a = new int[5][,][][5;   // CS0178, no comma or right bracket
}
}


Compiler Error CS0179

'member' is external and has a body

When a class member is marked extern, it means that the member's definition is located in another file. Therefore, a class member marked as extern cannot be defined in the class. Either remove the extern keyword or delete the definition.

The following sample generates CS0179:

public class MyClass {
   public extern int ExternMethod(int aa) {   // CS0179
      return 0;
   }
   // try the following line instead
   // public extern int ExternMethod(int aa);

   public static void Main() {
   }
}


Compiler Error CS0180

'member' cannot be both external and abstract

abstract and extern are mutually exclusive. extern means that the member is defined outside the file, and abstract means that the implementation is provided in a derived class.

The following sample generates CS0180:

namespace x
{
   public class iii
   {
      public extern abstract int aaa(int aa);   // CS0180
      public static void Main()
      {
      }
   }

}


Compiler Error CS0181

Unknown attribute

An attribute was used that is neither a user-defined attribute nor a predefined attribute.

The following sample generates CS0181:

namespace x {
   [x]   // CS0181
   public class a {
      public static void Main() {
      }
   }
}


Compiler Error CS0182

An attribute parameter must be a constant, method, field, property, or class

An argument to an attribute was not specified correctly.

The following sample generates CS0182:

public class C {
   static string s;

   [System.Diagnostics.ConditionalAttribute(s)]   // CS0182
   // try the following line instead
   // [System.Diagnostics.ConditionalAttribute("s")]
   void nonConstantArgumentToConditional() {
   }
   public static void Main() {
   }
}


Compiler Warning (level 1) CS0183

The given expression is always of the provided ('type') type

If a conditional statement always evaluates to true, then you do not need a conditional statement.

The following sample generates CS0183:

using System;
public class Test {
   public static void Main() {
      Object obj3 = null;
      String str2 = "CS0183";
      obj3 = str2;

   if ( obj3 is String )
      Console.WriteLine( "obj3 is String" );

   if ( str2 is Object )
      Console.WriteLine( "str2 is Object" );   // CS0183
   }
}


Compiler Warning (level 1) CS0184

The given expression is never of the provided ('type') type

The expression can never be true because the variable you are testing is neither declared as type nor derived from type.

The following sample generates CS0184:

class MyClass {
   public static void Main(){
      int i = 0;
   if (i is string) // CS0184
         i++;
   }
}


Compiler Error CS0185

'type' is not a reference type as required by the lock statement

The lock statement can only evaluate reference types.

The following sample generates CS0185:

public class MainClass {
   public static void Main () {
      lock (1) {   // CS0185
      // try the following lines instead
      // MainClass x = new MainClass();
      // lock(x) {
      }
   }
}


Compiler Error CS0186

Use of null is not valid in this context

Casting null is not valid.

The following sample generates CS0186:

using System;
class MyClass {
   public static void Main(){
      ((Object) null) is string;   // CS0186, cannot cast null
   }
}


Compiler Error CS0187

No such operator 'operator' defined for type 'type'

An operator was used on a type, but the operator is not defined for use on the type.

The following sample generates CS0187:

class MyClass {
   public static void Main(){
      string someString = "Hello";
      someString++;   // CS0187
   }
}


Compiler Error CS0188

The this object cannot be used before all of its fields are assigned to

All fields in a struct have to be assigned by a constructor before the constructor can call a method in the struct.

The following sample generates CS0188:

struct s {
   public int a;

   void f() {
   }

   s (int i) {
      // a = i;
      f(); // CS0188
   }
}

class MyClass {
   public static void Main() {
   }
}


Compiler Error CS0190

The __arglist construct is valid only within a variable argument method

__arglist holds the handle to the argument iterator which can be used to obtain the individual arguments using class library methods.

The following sample generates CS0190:

using System;
public class MyClass {
   public static void Test (int a) {
       object o = __arglist;   // CS0190
   }
   public static void Main() {
      Test(1);
   }
}


Compiler Error CS0191

A readonly field cannot be assigned to (except in a constructor or a variable initializer)

A readonly field can only take an assignment in a constructor or at declaration.

The following sample generates CS0191:

class MyClass {
   public readonly int TestInt = 6;  // OK to assign to readonly field in declaration

   MyClass() {
      TestInt = 11;  // OK to assign to readonly field in constructor
   }

   public void TestReadOnly() {
      TestInt = 19;  // CS0191
   }
   public static void Main() {
   }
}

You can also get CS0191 if the readonly field is static and the constructor is not marked static.


Compiler Error CS0192

A readonly field cannot be passed ref or out (except in a constructor)

A field (variable) marked with the readonly keyword cannot be passed either to a ref or out parameter except inside a constructor.

The following sample generates CS0192:

class MyClass {
   public readonly int TestInt = 6;

   static void TestMethod(ref int testInt) {
      testInt = 0;
   }

   MyClass() {
      TestMethod(ref TestInt);   // OK
   }

   public void PassReadOnlyRef() {
      TestMethod(ref TestInt);   // CS0192
   }

   public static void Main() {
   }
}

You can also get CS0192 if the readonly field is static and the constructor is not marked static.


Compiler Error CS0193

The * or -> operator must be applied to a pointer

The * or -> operator was used with a nonpointer type.

The following sample generates CS0193:

using System;
struct Age {
   public int AgeYears;
   public int AgeMonths;
   public int AgeDays;
}

public class MyClass {
   public static void SetAge(ref Age anAge, int years, int months, int days) {
      anAge->Months = 3;   // CS0193, anAge is not a pointer
      // try the following line instead
      // anAge.AgeMonths = 3;
   }
   public static void Main() {
      Age MyAge = new Age();
      Console.WriteLine(MyAge.AgeMonths);
      SetAge(ref MyAge, 22, 4, 15);
      Console.WriteLine(MyAge.AgeMonths);
   }
}


Compiler Error CS0196

A pointer must be indexed by only one value

A pointer cannot have multiple indices.

The following sample generates CS0196:

public class MainClass {
   public static void Main () {
      int *i = null;
      int j = 0;
      j = i[1,2];   // CS0196
      // try the following line instead
      // j = i[1];
   }
}


Compiler Error CS0198

A static readonly field cannot be assigned to (except in a static constructor or a variable initializer)

A readonly variable must have the same static usage as the constructor in which you want to initialize it.

The following sample generates CS0198:

class MyClass {
   public static readonly int TestInt = 6;

   MyClass() {
      TestInt = 11;   // CS0198, constructor is not static and readonly field is.
   }

   public static void Main() {
   }
}


Compiler Error CS0199

A static readonly field cannot be passed ref or out (except in a static constructor)

A readonly variable must have the same static usage as the constructor in which you want to pass it as a ref or out parameter.

The following sample generates CS0199:

class MyClass {
   public static readonly int TestInt = 6;

   static void TestMethod(ref int testInt) {
      testInt = 0;
   }

   MyClass() {
      TestMethod(ref TestInt);   // CS0199, TestInt is static
   }

   public static void Main() {
   }
}


Compiler Error CS0200

Property or indexer 'property' cannot be assigned to -- it is read only

An attempt was made to assign a value to a property, but the property does not have a set accessor. Resolve the error by adding a set accessor.

The following sample generates CS0200:

public class MainClass {
   // private int _mi;
   int I {
      get {
         return 1;
      }
      // uncomment the following set accessor
      // also uncomment the declaration for _mi
      // set {
      //   _mi = value;
      // }
   }
   public static void Main () {
      MainClass II = new MainClass();
      II.I = 9;   // CS0200
   }
}


Compiler Error CS0201

Only assignment, call, increment, decrement, and new expressions can be used as a statement

The compiler generates an error when it encounters a meaningless statement.

The following sample generates CS0201:

using System;
public class MainClass {
   public static void Main () {
      2 * 3;   // CS0201
   }
}


Compiler Error CS0202

The call to GetEnumerator must return a class or a struct, not 'type'

A call to GetEnumerator cannot return a pointer; it must return an instance.

The following sample generates CS0202:

public class C1 {
   public int Current {
      get {
         return 0;
      }
   }

   public bool MoveNext () {
      return false;
   }

   public static implicit operator C1 (int c1) {
      return 0;
   }

}

public class C2 {
   public int Current {
      get {
         return 0;
      }
   }

   public bool MoveNext () {
      return false;
   }

   public C1* GetEnumerator () {   // CS0202
   // try the following line instead
   // public C1 GetEnumerator () {
      return null;
   }
}

public class MainClass {
   unsafe public static void Main () {
      C2 c2 = new C2();
      foreach (C1 x in c2) {x = null;}
   }
}


Compiler Error CS0203

Can't call a method on 'variable'

Method calls must be on an object and not on a pointer.

The following sample generates CS0203:

public class C2 {
   public C2 Current {
      get {
         return null;
      }
   }

   public bool MoveNext () {
      return false;
   }

   public C2 GetEnumerator () {
      return null;
   }
}

public class MainClass {
   unsafe public static void Main () {
      C2** c2 = null;
      foreach (C2 x in c2) {}   // CS0203
      // try the following line instead
      // foreach (C2 x in (*c2)) {}
      }
}


Compiler Error CS0204

Only 65535 locals are allowed

The limit of 65535 local variables has been exceeded in a method.


Compiler Error CS0205

Cannot call an abstract base method or property: 'method'

You cannot call an abstract method, even from within the implementation of the method.

The following sample generates CS0205:

abstract public class MyClass {
   abstract public void mf();
}

public class MyClass2 : MyClass {

   public override void mf() {
      base.mf();   // CS0205, delete this line
   }
   public static void Main() {
   }
}


Compiler Error CS0206

A property or indexer may not be passed as an out or ref parameter

A property is not available to be passed as a ref or out parameter.

The following sample generates CS0206:

public class MyClass {
   public static int iii {
      get {
         return 0;
      }

      set {
      }
   }
   public static void MyMeth(ref int i) {
   // public static void MyMeth(int i) {
   }
   public static void Main() {
      MyMeth(ref iii);   // CS0206
      // try the following line instead
      // MyMeth(iii);   // CS0206
   }
}


Compiler Error CS0207

The accessor ('accessor method') need not be declared unsafe if the property already is

If a property is defined as unsafe, then it is not valid syntax to also use the unsafe keyword on any of the property's accessors.

The following sample generates CS0207:

public class MyClass {
   unsafe public static int iii {
      unsafe get {   // CS0207, remove unsafe keyword
         return 0;
      }

      set {
      }
   }

   public static void Main() {
   }
}


Compiler Error CS0208

Cannot take the address or size of a variable of a managed type ('S')

Even when used with the unsafe keyword, taking the address of a managed object is not allowed.

The following sample generates CS0208:

class S {
      public int a = 98;
}

public class a {
   unsafe public static int Main() {
      S s = new S();  // S is managed
      S * s2 = &s;    // CS0208
      return 1;
   }
}


Compiler Error CS0209

The type of locals declared in a fixed statement must be a pointer type

The variable that you declare in a fixed statement must be a pointer.

The following sample generates CS0209:

public class Class {

   unsafe public static void Main() {

      fixed (int i) {   // CS0209
      // try the following line instead
      // fixed (int *i = null) {
      }
   }
}


Compiler Error CS0210

You must provide an initializer in a fixed statement declaration

You must declare and initialize the variable in a fixed statement.

The following sample generates CS0210:

public class Class {
   unsafe public static void Main() {
      fixed (int *i) {   // CS0210
      // try the following line instead
      // fixed (int *i = null) {
      }
   }
}


Compiler Error CS0211

Cannot take the address of the given expression

You can take the address of fields, local variables, and indirection of pointers, but you cannot take, for example, the address of the sum of two local variables.

The following sample generates CS0211:

public class a {
   unsafe public void mf() {
      int a = 0, b = 0;
      int *i = &(a + b);   // CS0211, the addition of two local variables
      // try the following line instead
      // int *i = &a;
   }
   public static void Main() {
   }
}


Compiler Error CS0212

You can only take the address of unfixed expression inside of a fixed statement initializer

The following sample generates CS0212 and shows how to take the address of an unfixed expression:

public class MyClass {
   public int TestInt = 0;
}

public class MyClass2 {
   unsafe public static void Main() {
      MyClass C = new MyClass();
      int *j = &C.TestInt;
      /* instead, try the following:
      fixed (int *j = &C.TestInt;) {
      }
      */
   }
}


Compiler Warning (level 3) CS0213

You do not need to use the fixed statement to take the address of an already fixed expression

A local variable in an unsafe method or a parameter to an unsafe method is already fixed (on the stack), so you do not need to take the address of either of these two variables in a fixed expression.

The following sample generates CS0213:

public class C {
   public int i = 0;
}
public class MyClass {
   unsafe public static void Main() {
      int i = 0;
      i++;
      C c = new C();

      fixed (int *j = &i) {   // CS0213
      // try the following line instead
      // fixed (int *j = &(c.i)) {
      }
   }
}


Compiler Error CS0214

Pointers may only be used in an unsafe context

Pointers can only be used with the unsafe keyword.

The following sample generates CS0214:

public struct S {
   public int a;
}

public class a {
   public static int Main() {
// try the following line instead
   // unsafe public static int Main() {
      S s = new S();
      S * s2 = &s;    // CS0214
      s2->a = 3;      // CS0214
      return 1;
   }
}


Compiler Error CS0215

The return type of operator True or False must be bool

User-defined true and false operators must have a return type of bool.

The following sample generates CS0215:

class MyClass {
   public static int operator true (MyClass MyInt) {   // CS0215
   // try the following line instead
// public static bool operator true (MyClass MyInt) {
      return true;
   }

   public static int operator false (MyClass MyInt) {   // CS0215
   // try the following line instead
   // public static bool operator false (MyClass MyInt) {
      return true;
   }

   public static void Main() {
   }
}


Compiler Error CS0216

The operator ('operator') requires a matching operator to also be defined

A user-defined true operator requires a user-defined false operator, and vice versa.

The following sample generates CS0216:

class MyClass {
      public static bool operator true (MyClass MyInt) {   // CS0216
      return true;
   }

   // to resolve, uncomment the following operator definition
   /*
   public static bool operator false (MyClass MyInt) {
      return true;
   }
   */

   public static void Main() {
   }
}


Compiler Error CS0217

In order to be applicable as a short circuit operator a user defined logical operator ('operator') must have the same return type as the type of its 2 parameters.

If you define an operator for a user-defined type, and then try to use the operator as a short-circuit operator, the user-defined operator must have parameters and return values of the same type. For more information on short-circuit operators, see && Operator and || Operator.

The following sample generates CS0217:

using System;

public class MyClass {
   public static bool operator true (MyClass f) {
      return false;
   }

   public static bool operator false (MyClass f) {
      return false;
   }

   public static implicit operator int(MyClass x) {
      return 0;
   }

   public static int operator & (MyClass f1, MyClass f2) {   // CS0217
   // try the following line instead
   // public static MyClass operator & (MyClass f1, MyClass f2) {
      return new MyClass();
   }

   public static void Main() {
      MyClass f = new MyClass();
      int i = f && f;
   }
}


Compiler Error CS0218

The type ('type') must contain declarations of operator true and operator false

If you define an operator for a user-defined type, and then try to use the operator as a short-circuit operator, the user-defined operator must have operator true and operator false defined. For more information on short-circuit operators, see && Operator and || Operator.

The following sample generates CS0218:

using System;
public class MyClass {
   // uncomment these operator declarations to resolve this CS0218
   /*
   public static bool operator true (MyClass f) {
      return false;
   }

   public static bool operator false (MyClass f) {
      return false;
   }
   */

   public static implicit operator int(MyClass x) {
      return 0;
   }

   public static MyClass operator & (MyClass f1, MyClass f2) {
      return new MyClass();
   }

   public static void Main() {
      MyClass f = new MyClass();
   int i = f && f;   // CS0218, requires operators true and false
   }
}


Compiler Warning (level 3) CS0219

The variable 'variable' is assigned but never used

The compiler warns, at level three, when you declare and assign a variable, but do not use it.

The following sample generates CS0219:

public class MyClass {
   public static void Main() {
      int a = 0;   // CS0219
   }
}


Compiler Warning (level 3) CS0220

The operation overflows at compile time in checked mode

An operation was detected by checked, which is the default, that resulted in a data loss. Either correct the inputs to the assignment or use unchecked to resolve this error.

The following sample generates CS0220:

using System;
class TestClass {

   const int x = 1000000;
   const int y = 1000000;

   public int MethodCh() {
      int z = (x * y);   // CS0220
      return z;
   }

   public int MethodUnCh() {
      unchecked {
         int z = (x * y);
         return z;
      }
   }

   public static void Main() {
      TestClass myObject = new TestClass();
      Console.WriteLine("Checked  : {0}", myObject.MethodCh());
      Console.WriteLine("Unchecked: {0}", myObject.MethodUnCh());
   }
}


Compiler Error CS0221

Constant value 'value' cannot be converted to a 'type' (use 'unchecked' syntax to override)

An assignment operation that would result in a data loss was detected by checked, which is on by default. Either correct the assignment or use unchecked to resolve this error.

The following sample generates CS0221:

public class MyClass {
   public static void Main() {
   //   unchecked {
         int a = (int)0xFFFFFFFF;   // CS0221
         a++;
   //   }
   }
}


Compiler Error CS0223

Cannot have a null string case label

A switch statement cannot test for a null string.

The following sample generates CS0223:

public class MyClass {
   public static void Main() {
      string s = "test";
      switch (s) {
         case null:   // CS0223
         // try the following line instead
         // case "test":
            System.Console.WriteLine("in a case");
            return;
         default:
            System.Console.WriteLine("in default");
      }
   }
}


Compiler Error CS0224

A method cannot both have a params parameter and varargs

A method declaration cannot have both the params keyword and __arglist. The recommended way to define a method to take multiple parameters is with the params keyword.

The following sample generates CS0224:

public class MyClass {
   public static void MyMethod(params int[] a, __arglist) {   // CS0224
   // try the following line instead
   // public static void MyMethod(params int[] a) {
}

   public static void Main () {
   }
}


Compiler Error CS0225

The params parameter must be a single dimensional array

When using the params keyword, you must specify a single-dimensional array of the data type.

The following sample generates CS0225:

public class MyClass {
   public static void TestParams(params int a) {   // CS0225
   // try the following line instead
   // public static void TestParams(params int[] a) {
   }
   public static void Main() {
      TestParams(1);
   }
}


Compiler Error CS0227

Unsafe code may only appear if compiling with /unsafe

If source code contains the unsafe keyword, then the /unsafe compiler option must also be used.

The following sample, when compiled without /unsafe, will generate CS0227:

public class a {
   unsafe public static void Main() {
   }
}


Compiler Error CS0228

'type' does not contain a definition for 'member', or it is not accessible

You may have replaced the system version of System.Hashtable, System.String, or System.Array, and the replacement does not contain member.

Otherwise, you may need to reinstall Visual Studio.


Compiler Error CS0229

Ambiguity between 'member1' and 'member2'

Members of different interfaces have the same name. If you want to keep the same names, you must qualify the names.

The following sample generates CS0229:

interface IList {
   int Count {
      get;
      set;
   }
}

interface ICounter {
   void Count(int i);
}

interface IListCounter : IList , ICounter{
}

class main {
   void Test(IListCounter x) {
      x.Count = 1;  // CS0229
      x.Count(1);   // CS0229
      // try the following lines instead
      // ((IList)x).Count = 1;
      // ((ICounter)x).Count(1);
   }

   public static void Main() {
   }
}


Compiler Error CS0230

Type and identifier are both required in a foreach statement

A foreach statement was poorly formed.

The following sample generates CS0230:

using System;
class MyClass {
   public static void Main() {
      int[] myarray = new int[3] {1,2,3};
      foreach (int in myarray) {   // CS0230
      // try the following line instead
      // foreach (int x in myarray) {
         Console.WriteLine(x);
      }
   }
}


Compiler Error CS0231

A params parameter list or the __arglist parameter must not have any following arguments

The params keyword must be the last parameter declared for a method.

The following sample generates CS0231:

using System;
public class MyClass {

   public static void UseParams(int AnInt, params int[] list, int i) {   // CS0231
   // try the following line instead
   // public static void UseParams(int AnInt, params int[] list) {
      for ( int i = 0 ; i < list.Length ; i++ )
         Console.WriteLine(list[i]);
      Console.WriteLine();
   }

   public static void UseArglist(int AnInt, char AChar, __arglist) {
      Console.WriteLine(AnInt);
      Console.WriteLine(AChar);

      ArgIterator ai = new ArgIterator(__arglist);

      while (ai.GetRemainingCount() > 0)
         Console.WriteLine(__refvalue(ai.GetNextArg(), int));
   }

   public static void Main() {
      UseParams(1, 2, 3);
      UseArglist(20, 'b', __arglist(1, 2, 3));
   }
}


Compiler Error CS0233

sizeof can only be used in an unsafe context (consider using System.Runtime.InteropServices.Marshal.SizeOf)

The sizeof operator can only be used in an unsafe block.

The following sample generates CS0233:

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public struct S {
   public int a;
}

public class MyClass {
   public static void Main() {
      S myS = new S();
      Console.WriteLine(sizeof(myS));   // CS0233
      // try the following line instead
      // Console.WriteLine(Marshal.SizeOf(myS));
   }
}


Compiler Error CS0234

The type or namespace name 'name' does not exist in the class or namespace 'scope'

A type was expected. One place where this could happen is if you pass a variable name to the typeof operator.

The following sample generates CS0234:

using System;
public class MyClass {
   public static void Main() {
      string i = "hello world";
      Type t = typeof(i);   // CS0234
      // try the following line instead
      // Type t = typeof(string);
      Console.WriteLine(i);
      Console.WriteLine(t);
   }
}


Compiler Error CS0500

'class member' cannot declare a body because it is marked abstract

An abstract class cannot contain a method with an implementation.

For example, the following sample generates CS0500:

namespace x
{
   abstract public class clx
   {
      abstract public void f(){}   // CS0500
      // try the following line instead
      // abstract public void f();
   }
   public class cly
   {
      public static int Main()
      {
         return 0;
      }
   }
}


Compiler Error CS0501

'member function' must declare a body because it is not marked abstract or extern

Nonabstract methods must have implementations. For example, the following sample generates CS0501:

namespace x
{
   public class clx
   {
      public void f();   // CS0501, needs implementation
   }
}


Compiler Error CS0502

The class 'class' is abstract and sealed

A class cannot be both abstract (meant to be a base class) and sealed (cannot be a base class).

The following sample generates CS0502:

namespace x
{
   abstract sealed public class clx // CS0502
   {
      abstract public void f();
   }
}


Compiler Error CS0503

The abstract method 'method' cannot be marked virtual

It is redundant to mark a member method as both abstract and virtual because abstract implies virtual.

The following sample generates CS0503:

namespace x
{
   abstract public class clx
   {
      abstract virtual public void f();   // CS0503
   }
}


Compiler Error CS0504

The constant 'variable' cannot be marked static

If a variable is const, it is also static. If you want a const and static variable, just declare that variable as const; if all you want is a static variable, just mark it static.

The following sample generates CS0504:

namespace x
{
   abstract public class clx
   {
      static const int i = 0;   // CS0504, cannot be both static and const
      abstract public void f();
   }
}


Compiler Error CS0505

'member1' : cannot override inherited nonfunction 'member2'

A class declaration attempted to override a nonmethod in a base class. Overrides must match the member type. If a method with the same name as a method in a base class is desired, use new (and not override) on the method declaration in the base class.

The following sample generates CS0505:

namespace x
{
   public class clx
   {
      public int i;
      int z()
      {
         return 0;
      }
   }
   public class cly : clx
   {
      public override int i() // CS0505, clx::i is a variable
      {
         return 0;
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0506

'function1' : cannot override inherited member 'function2' because it is not "virtual", "abstract", or "override"

A method was overridden that was not explicitly marked as virtual, abstract, or override.

The following sample generates CS0506:

namespace x
{
   abstract public class clx
   {
      public int i = 0;
      public int f()
      {
         return 0;
      }
      // try this definition for f:
      // public int f();
   }
   public class cly : clx
   {
      public override int f()   // CS0506
      {
         return 0;
      }
      public static int Main()
      {
         return 0;
      }
   }
}


Compiler Error CS0507

'function1' : cannot change access modifiers when overriding 'access' inherited member 'function2'

An attempt was made to change the access specification in a method override.

The following sample generates CS0507:

abstract public class clx {
   public int i = 0;
   virtual protected int f() {
      return 0;
   }
}
public class cly : clx {
   public override int f()   {   // CS0507
      return 0;
   }
   public static int Main() {
      return 0;
   }
}


Compiler Error CS0508

'function1' cannot change return type when overriding inherited member 'function2'

An attempt was made to change the return type in a method override.

The following sample generates CS0508:

namespace x
{
   abstract public class clx
   {
      public int i = 0;
      abstract public int f();   // return type is int
   }
   public class cly : clx
   {
      public override double f(){return 0.0;}   // CS0508
      public static int Main()
      {
         return 0;
      }
   }
}


Compiler Error CS0509

'class2' : cannot inherit from sealed class 'class2'

A sealed class cannot act as a base class. Structs are sealed by default.

The following sample generates CS0509:

namespace x
{
   sealed public class clx   // class is sealed
   {
      public int i = 0;
   }
   public class cly : clx   // CS0509
   {
      public static int Main()
      {
         return 0;
      }
   }
}


Compiler Error CS0513

'function' is abstract but it is contained in nonabstract class 'class'

A method cannot be an abstract member of a nonabstract class.

The following sample generates CS0513:

namespace x
{
   public class clx
   {
      abstract public void f();   // CS0513, abstract member of nonabstract class
   }
}


Compiler Error CS0514

'constructor' : static constructor cannot have an explicit this or base contructor call

There is no object for a static class. Therefore, the base class cannot be called. Objects must be known at compile time.

The following sample generates CS0514:

namespace x
{
   public class clx
   {
      public clx(int i)
      {
      }
   }
   public class cly : clx
   {
      static cly() : base(0) // CS0514, remove base or static
      {
      }
   }
}

See Also

base


Compiler Error CS0515

'function' : access modifiers are not allowed on static contructors

A constructor cannot have an access modifier.

The following sample generates CS0515:

namespace x
{
   public class clx
   {
      public static int Main()
      {
         return 0;
      }
   }
   public class clz
   {
      public static clz()   // CS0515, remove public keyword
      {
      }
   }
}


Compiler Error CS0516

Constructor 'constructor' can not call itself

A program cannot recursively call constructors.

The following sample generates CS0516:

namespace x
{
   public class clx
   {
      public clx() : this() // CS0516, delete "this()"
      {
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0517

'class' has no base class and cannot call a base constructor

CS0517 can occur only when the NGWS runtime compiles the source code for the object class, which is the only class that has no base class.


Compiler Error CS0518

The predefined type 'type' is not defined or imported

One or more files needed by the compiler were not found. Reinstall NGWS.


Compiler Error CS0519

'name' conflicts with a predefined namespace

One or more files needed by the compiler were not found. Reinstall NGWS.


Compiler Error CS0520

'name' conflicts with a predefined type

One or more files needed by the compiler were not found. Reinstall NGWS.


Compiler Error CS0522

'constructor' : structs cannot call base class constructors

A struct cannot call a base class constructor; remove the call to the base class constructor.

The following sample generates CS0522:

namespace x
{
   public class clx
   {
      public clx(int i)
      {
      }
      public static void Main()
      {
      }
   }
   public struct cly
   {
      public cly(int i):base(0)   // CS0522
      // try the following line instead
      // public cly(int i){}
      {
      }
   }
}


Compiler Error CS0523

Struct member 'struct2 field' of type 'struct1' causes a cycle in the structure layout

The definitions of two structs include recursive references. Change the struct definitions such that each does not define itself on the other.

The following sample generates CS0523:

struct RecursiveLayoutStruct1 {
   public RecursiveLayoutStruct2 field;
}

struct RecursiveLayoutStruct2 {
   public RecursiveLayoutStruct1 field;   // CS0523
}

class MyClass {
   public static void Main() {
   }
}


Compiler Error CS0524

'type' : interfaces cannot declare types

An interface cannot contain a user-defined type.

The following sample generates CS0524:

namespace x
{
   public interface clx
   {
      public class cly   // CS0524, delete user-defined type
      {
      }
   }
}


Compiler Error CS0525

Interfaces cannot contain fields

An interface can contain methods and properties but not fields.

The following sample generates CS0525:

namespace x
{
   public interface clx
   {
      public int i;   // CS0525
   }
}


Compiler Error CS0526

Interfaces cannot contain constructors

Constructors cannot be defined for interfaces. A method is considered a constructor if it has the same name as the class and no return type.

The following sample generates CS0526:

namespace x
{
   public interface clx
   {
      public clx()   // CS0526
      {
      }
   }
   public class cly
   {
      public static void Main()
      {
      }
   }
}


Compiler Error CS0527

'type' : type in interface list is not an interface

It is possible for a struct or interface to inherit from another interface but not from any other type.

The following sample generates CS0527:

namespace x
{
   public struct clx : int   // CS0527, int not an interface
   {
      public static void Main()
      {
      }
   }
}


Compiler Error CS0528

'interface' is already listed in interface list

An interface-inheritance list includes a duplicate. An interface can only be specified once in the inheritance list.

The following sample generates CS0528:

namespace x
{
   public interface a
   {
   }
   public class b : a, a   // CS0528
   {
      public void Main()
      {
      }
   }
}


Compiler Error CS0529

Inherited interface 'interface' causes a cycle in the interface hierarchy

The inheritance list for an interface includes a direct or indirect reference to itself. An interface cannot inherit from itself.

The following sample generates CS0529:

namespace x {
   public interface a {
   }
   public interface b : a, c {
   }
   public interface c : b {   // CS0529, b inherits from c
   }
}


Compiler Error CS0531

'member' : interface members cannot have a definition

Methods that are declared in an interface must be implemented in a class that inherits from it and not in the interface itself.

The following sample generates CS0531:

namespace x {
   public interface clx {
      int xclx() {   // CS0531, cannot define xclx
         return 0;
      }
      // try the following declaration instead
      //   int xclx();
   }
   public class cly {
      public static void Main() {
      }
   }
}


Compiler Error CS0533

'derived-class member' hides inherited abstract member 'base-class member'

A base class method is hidden. Check the syntax of your declaration to see if it is correct.

The following sample generates CS0533:

namespace x {
   abstract public class a {
      abstract public void f();
   }

   abstract public class b : a {
      new abstract public void f();   // CS0533
      // try the following line instead
      // override public void f(){}
      public static void Main(){
      }
   }
}

See Also

base


Compiler Error CS0534

'function1' does not implement inherited abstract member 'function2'

A class is required to implement all the abstract members in the base class.

The following sample generates CS0534:

namespace x
{
   abstract public class clx
   {
      public abstract void f();
   }

   public class cly : clx    // CS0534, no override for clx::f
   {
// the following line is a sample override to resolve CS0534
//      override public void f(){}
      public static int Main()
      {
         return 0;
      }
   }
}


Compiler Error CS0535

'class' does not implement interface member 'member'

A class derived from an interface, but the class did not implement one or more of the interface's members. A class must implement all members of interfaces from which it derives.

The following sample generates CS0535:

namespace x {
   public interface a {
      void f();
   }
   public class b : a {   // CS0535, a::f is not implemented
      public static void Main() {
      }
   }
}


Compiler Error CS0536

'class' does not implement interface member 'interface member'. 'class member' is static, not public, or has the wrong return type

The compiler did not detect an interface member implementation. A declaration may be present that almost implements the interface member. Check for the following syntax errors in the declaration for the interface member:

  • public keyword is omitted.
  • Return type does not match.
  • static keyword is present.

The following sample generates CS0536:

namespace x
{
   public interface a
   {
      void f();
   }

   public class b : a
   {
      public static int f()   // CS0536
      {
      }
      // try, public void f(){}
      public static void Main()
      {
      }
   }
}


Compiler Error CS0538

'name' in explicit interface declaration is not an interface

An attempt was made to explicitly declare an interface, but an interface was not specified.

The following sample generates CS0538:

namespace x {
   interface IMyFace {
      void F();
   }
   public class CMyClass {
      public void G();
   }
   class C: IMyFace {
      void IMyFace.F() {
      }
      void CMyClass.G() {   // CS0538, CMyClass not an interface
      }
   }
}


Compiler Error CS0539

'member' in explicit interface declaration is not a member of interface

An attempt was made to explicitly declare an interface member that does not exist. You should either delete the declaration or change it so that it refers to a valid interface member.

The following sample generates CS0539:

namespace x {
   interface I {
      void m();
   }
   public class clx : I {
      void I.x() {   // CS0539
      }
      public static int Main() {
         return 0;
      }
   }
}


Compiler Error CS0540

'interface member' : containing class does not implement interface 'interface'

You attempted to implement an interface member in a class that does not derive from the interface. You should either delete the implementation of the interface member or add the interface to the base-class list of the class.

The following sample generates CS0540:

namespace x
{
   interface I
   {
      void m();
   }
   public class clx
   {
      void I.m()   // CS0540
      {
      }
      public static int Main()
      {
         return 0;
      }
   }
}


Compiler Error CS0541

'declaration' : explicit interface declaration can only be declared in a class or struct

An explicit interface declaration was found outside a class or struct.

The following sample generates CS0541:

namespace x
{
   interface IFace
   {
      void F();
   }
   interface IFace2 : IFace
   {
      void IFace.F();   // CS0541
   }
}


Compiler Error CS0542

'user-defined type' : member names cannot be the same as their enclosing type

A name was used more than once in the same construct.

The following sample generates CS0542:

namespace x
{
   interface F
   {
      void F();   // CS0542, same name as the interface
   }
}


Compiler Error CS0543

'enumeration' : the enumerator value is too large to fit in its type

A value that was assigned to an element in an enumeration is outside the range of the data type.

The following sample generates CS0543:

// CS0543
namespace x
{
   enum I : byte
   {a = 255, b, c}   // CS0543
   public class clx
   {
      public static int Main()
      {
         return 0;
      }
   }
}


Compiler Error CS0544

'property override' : cannot override inherited nonproperty 'non-property'

An attempt was made to override a nonproperty data type as a property, which is not allowed.

The following sample generates CS0544:

namespace x
{
   public class a
   {
      public int i;
   }

   public class b : a
   {
      public override int i   // CS0544
      // the following line resolves CS0544
      // public new int i
      {
         get
         {
            return 0;
         }
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0545

'function' : cannot override because 'property' does not have an overridable get accessor

An attempt was made to define an override for a property accessor when the base class has no such definition to override. This error can be resolved by adding a virtual accessor in the base class.

The following sample generates CS0545:

namespace x
{
   public class a
   {
      public int i
      {
         set
         {
         }
         // uncomment this accessor to resolve CS0545
/*
         virtual get
         {
            return 0;
         }
*/
      }
   }

   public class b : a
   {
      public override int i   // CS0545
      {
         get
         {
            return 0;
         }
      }
      public static void Main()
      {
      }
   }
}

See Also

Property Declaration | Accessors


Compiler Error CS0546

'accessor' : cannot override because 'property' does not have an overridable set accessor

An attempt to override one of the accessor methods for a property failed because the accessor cannot be overridden. To resolve, add the virtual keyword in front of the accessor you want to override. Or, you can make the class that holds the property abstract, in which case the accessor methods would have no implementation.

The following sample generates CS0546:

namespace x
{
   public class a
   {
      public int i
      {
         get
         {
            return 0;
         }
         // uncomment the following accessor to resolve this CS0546
         /*
         virtual set
         {
         }
         */
      }
   }

   public class b : a
   {
      public override int i
      {
         set   // CS0546, no set
         {
         }
      }
      public static void Main()
      {
      }
   }
}

See Also

Property Declaration | Accessors


Compiler Error CS0547

'property' : property or indexer cannot have void type

void is invalid as a return value for a property.

The following sample generates CS0547:

public class a {
   public void i {   // CS0547
   // try the following declaration instead
   // public int i
      get {
         return 0;
      }
   }
}

public class b : a {
   public static void Main() {
   }
}

See Also

Properties


Compiler Error CS0548

'property' : property or indexer must have at least one accessor

A property must have at least one accessor (get or set) method.

The following sample generates CS0548:

namespace x
{
   public class b
   {
      public int MyProp
      {
// remove comments to resolve CS0548
/*
         get
         {
            return 0;
         }
         set
         {
            return 0;
         }
*/
      }
      public static void Main()
      {
      }
   }
}

See Also

Property Declaration | Accessors


Compiler Error CS0549

'function' is a new virtual member in sealed class 'class'

A sealed class cannot be used as a base class, and a virtual method has to be implemented in a derived class, which is a contradiction.

The following sample generates CS0549:

sealed public class MyClass {
   virtual public void TestMethod() {   // CS0549
   }
// to resolve, remove virtual from method or sealed from class
   public static void Main() {
   }
}


Compiler Error CS0550

'accessor' adds an accessor not found in interface member 'property'

The implementation of a property in a derived class contains an accessor that was not specified in the base interface.

The following sample generates CS0550:

namespace x
{
   interface ii
   {
      int i
      {
         get;
         // add the following accessor to resolve this CS0550
         // set;
      }
   }

   public class a : ii
   {
      int ii.i
      {
         get
         {
            return 0;
         }
         set // CS0550, no set in interface
         {
         }
      }
      public static void Main()
      {
      }
   }
}

See Also

Property Declaration | Accessors


Compiler Error CS0551

Explicit interface implementation 'implementation' is missing accessor 'accessor'

A class that explicitly implements an interface's property must implement all the accessors that the interface defines.

The following sample generates CS0551:

interface ii {
   int i {
      get;
      set;
   }
}

public class a : ii {
   int ii.i {   // CS0551
      set {
      }
      /*
      implement the following accessor
      get {
         return 0;
      }
      */
   }
   public static void Main() {
   }
}

See Also

Property Declaration | Accessors


Compiler Error CS0552

'conversion routine' : user defined conversion to/from interface

You cannot create a user-defined conversion to or from an interface. If you need the conversion routine, resolve this error by making the interface a class or derive a class from the interface.

The following sample generates CS0552:

namespace x
{
   interface ii
   {
   }
   public class a
   {
      // delete the routine to resolve CS0552
      public static implicit operator ii(a aa) // CS0552
      {
         return new ii();
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0553

'conversion routine' : user defined conversion to/from base class

User-defined conversions to values of a base class are not allowed; you do not need such an operator.

The following sample generates CS0553:

namespace x
{
   public class ii
   {
   }
   public class a : ii
   {
      // delete the conversion routine to resolve CS0553
      public static implicit operator ii(a aa) // CS0553
      {
         return new ii();
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0554

'conversion routine' : user defined conversion to/from derived class

User-defined conversions to values of a derived class are not allowed; you do not need such an operator.

The following sample generates CS0554:

namespace x
{
   public class ii
   {
      // delete the conversion routine to resolve CS0554
      public static implicit operator ii(a aa) // CS0554
      {
         return new ii();
      }
   }

   public class a : ii
   {
      public static void Main()
      {
      }
   }
}


Compiler Error CS0555

User-defined operator cannot take an object of the enclosing type and convert to an object of the enclosing type

User-defined conversions to values of the enclosing class are not allowed; you do not need such an operator.

The following sample generates CS0555:

public class MyClass {
   // delete the following operator to resolve this CS0555
   public static implicit operator ii(ii aa) { // CS0555
      return new ii();
   }

   public static void Main() {
   }
}


Compiler Error CS0556

User-defined conversion must convert to or from the enclosing type

A user-defined conversion routine must convert to or from the class that contains the routine.

The following sample generates CS0556:

namespace x {
   public class ii {
      public class iii {
         public static implicit operator int(byte aa)   {   // CS0556
         // try the following line instead
         // public static implicit operator int(iii aa)
            return 0;
         }
      }
      public static void Main() {
      }
   }
}


Compiler Error CS0557

Duplicate user-defined conversion in class 'class'

Duplicate conversion routines are not allowed in a class.

The following sample generates CS0557:

namespace x
{
   public class ii
   {
      public class iii
      {
         public static implicit operator int(iii aa)
         {
            return 0;
         }
         public static explicit operator int(iii aa)   // CS0557, delete duplicate
         {
            return 0;
         }
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0558

User-defined operators 'operators' must be declared static and public

Both the static and public access modifiers must be specified on user-defined operators.

The following sample generates CS0558:

namespace x {
   public class ii {
      public class iii {
         static implicit operator int(iii aa) {   // CS0558, add public
            return 0;
         }
      }
      public static void Main() {
      }
   }
}


Compiler Error CS0559

The parameter and return type for ++ or -- operator must be the containing type

The method declaration for an operator overload must follow certain guidelines.

The following sample generates CS0559:

namespace x {
   public class iii {
      public static implicit operator int(iii x) {
         return 0;
      }
      public static implicit operator iii(int x) {
         return null;
      }
      public static int operator ++(int aa)   // CS0559
      // try the following line instead
      // public static iii operator ++(iii aa) {
         return (iii)0;
      }
      public static void Main() {
      }
   }
}


Compiler Error CS0560

Accessor 'accessor1' : cannot override 'accessor2' because it is hidden by 'function'

A property definition is hidden in an indirect base class.

The following sample generates CS0560:

namespace NC
{
   abstract class B
   {
      public int Prop
      {
         abstract get;
         virtual set
         {
            return;
         }
      }
   }

   abstract class D : B
   {
      public int Set_Prop(int i)
      {
         return 0;
      }
// to resolve this CS0560, delete the Set_Prop definition and
// uncomment the definition of Prop
/*
      public override int Prop
      {
         set
         {
            return;
         }
         get
         {
            return 0;
         }
      }
*/
   }

   class DD : D
   {
      // override blocked by non-accessors
      public override int Prop
      {
         get
         {
            return 0;
         }
         set   // CS0560
         {
            return;
         }
      }
      public static void Main()
      {
      }
   }
}

See Also

Property Declaration | Accessors


Compiler Error CS0561

'function' : cannot override 'accessor' because it is a special compiler-generated method

A property declaration created a hidden virtual method (accessor) named Getproperty_name. That accessor name conflicts with a method name. You can only override this accessor method through a property override not a method override.

The following sample generates CS0561:

class B {
   public int Prop {
      virtual get {
         return 0;
      }
   }
}

class C : B {
   public override int GetProp() {   // CS0561
      return 0;
   }
// try the following instead
/*
public override int Prop {
   get {
      return 0;
   }
}
*/
}


Compiler Error CS0562

The parameter of a unary operator must be the containing type

The method declaration for an operator overload must follow certain guidelines.

The following sample generates CS0562:

namespace x
{
   public class iii
   {
      public static implicit operator int(iii x)
      {
         return 0;
      }
      public static implicit operator iii(int x)
      {
         return null;
      }
      public static iii operator +(int aa)   // CS0562
      // try the following line instead
      // public static iii operator +(iii aa)
      {
         return (iii)0;
      }
      public static void Main()
      {
      }
   }

}


Compiler Error CS0563

One of the parameters of a binary operator must be the containing type

The method declaration for an operator overload must follow certain guidelines.

The following sample generates CS0563:

public class iii {
   public static int operator <<(int aa, int bb) {   // CS0563
   // try the following line instead
   // public static int operator <<(int aa, iii bb) {
      return 0;
   }
   public static void Main() {
   }
}


Compiler Error CS0567

Interfaces cannot contain operators

Operators are not permitted in interface definitions.

The following sample generates CS0567:

namespace x {
   interface iii {
      int operator compare(int aa, int bb) {   // CS0567
         return 0;
      }
   }

   public class a {
      public static void Main() {
      }
   }
}


Compiler Error CS0568

Structs cannot contain explicit parameterless contructors

Each struct already has a default constructor that initializes the object to zero. Therefore, the constructors that you can create for a struct must take one or more parameters.

The following sample generates CS0568:

namespace x
{
   public class clx
   {
      public clx(){}
      public static void Main()
      {
      }
   }
   public struct cly
   {
      public cly(){}   // CS0568, specify one or more parameters
   }
}


Compiler Error CS0569

'method2' : cannot override 'method1' because it is not a method

This error occurs when you derive from a base class that was written in another language and when the compiler does not understand the method that you are attempting to override.


Compiler Error CS0570

'class' references a type not unsupported by the language

This error occurs when using imported metadata that was generated by another compiler. Your code attempted to use a class member that the compiler cannot process.


Compiler Error CS0571

'function' : cannot explicitly call operator or accessor

Certain operators have internal names. For example, op_Increment is the internal name of the ++ operator. You should not use or explicitly call such method names.

The following sample generates CS0571:

public class MyClass {
   public static MyClass operator ++ (MyClass c) {
      return null;
}

public static int prop {
   get {
      return 1;
   }

   set {
   }
}

public static void Main() {
   op_Increment(null);   // CS0571
   // use the increment operator as follows
   // MyClass x = new MyClass();
   // x++;

   set_prop(1);      // CS0571
   // try the following line instead
   // prop = 1;
   }
}


Compiler Error CS0572

'type' : cannot reference a type through an expression; try 'path_to_type' instead

An attempt was made to access a member of a class through an identifier, which is not permitted in this situation.

The following sample generates CS0572:

using System;
class C {
   public class Inner {
      public static int v = 9;
   }
}

class D : C {
   public static void Main() {
      C cValue = new C();
      Console.WriteLine(cValue.Inner.v);   // CS0572
      // try the following line instead
      // Console.WriteLine(C.Inner.v);
   }
}


Compiler Error CS0573

'field declaration' : cannot have instance field initializers in structs

You can declare, but not initialize, a variable in a struct.

The following sample generates CS0573:

namespace x {
   public class clx {
      public static void Main(){}
   }
   public struct cly {
      clx a = new clx();  // CS0573
      // clx a;           // OK
      int i = 7;          // CS0573
      // int i;           // OK
   }
}


Compiler Error CS0574

Name of destructor must match name of class

The name of a destructor must be the class name preceded by a tilde (~).

The following sample generates CS0574:

namespace x
{
   public class iii
   {
      ~iiii()   // CS0574
      {
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0575

Only class types can contain destructors

A struct cannot contain a destructor.

The following sample generates CS0575:

namespace x
{
   public struct iii
   {
      ~iii()   // CS0575
      {
      }
      public static void Main()
      {
      }
   }
}


Compiler Error CS0576

Namespace '' already contains an alias definition for 'System'

An attempt was made to use the System namespace twice.

The following sample generates CS0576:

using System.Collections;
using System = System.Collections;   //CS0576, delete this line

namespace a
{
   public class z
   {
      public static void Main()
      {
      }
   }
}


Compiler Error CS0577

Conditional not valid on 'function' because it is a constructor, destructor, operator, or explicit interface implementation

ConditionalAttribute cannot be applied to the specified methods.

The following sample generates CS0577:

interface I {
   void m();
}

public class MyClass : I {
   [System.Diagnostics.ConditionalAttribute("a")]   // CS0577
   // cannot use the above attribute on an explicit interface definition
   void I.m() {
   }

   public static void Main() {
   }
}


Compiler Error CS0578

Conditional not valid on 'function' because its return type is not void

ConditionalAttribute cannot be applied to a method that has a return type other than void. The reason for this is that any other return type for a method may be needed by another part of your program.

The following sample generates CS0578:

public class MyClass {
      [System.Diagnostics.ConditionalAttribute("a")]
      public int TestMethod() {   // CS0578
         return 0;
      }
      public static void Main() {
      }
}

To resolve this error, you must either delete ConditionalAttribute, or you must change the return value of the method to void.


Compiler Error CS0579

Duplicate 'attribute' attribute

It is not possible to specify the same attribute more than once.

public class MyAttribute : System.Attribute {
}

public class z {
   [MyAttribute(), MyAttribute()]   // CS0579
   // try the following line instead
   // [MyAttribute()]
   public void zz() {
   }

   public static void Main() {
   }
}


Compiler Error CS0580

Too many unnamed arguments to attribute 'attribute'

Some attributes only take one argument. You cannot pass multiple arguments in a comma-separated list.

The following sample generates CS0580:

using System;

public class bug {
   [name("abc", "def")]
   // try the following line instead
   // [name("abc")]
   public int this [int index] {
      get {
         return 0;
      }
   }

   public static void Main() {
   }
}


Compiler Error CS0581

Named arguments not allowed on attribute 'attribute'

Some attributes do not take named arguments.

The following sample generates CS0581:

public class MyClass {

   [name("xxx", namedarg=3)]   // CS0581
   // try the following line instead
   // [name("xxx")]
        public int this[int index] {
      get {
         return 0;
      }
   }

   public static void Main() {
   }
}


Compiler Error CS0582

Conditional not valid on interface members

ConditionalAttribute is not valid on an interface member.

The following sample generates CS0582:

interface MyIFace {
   [System.Diagnostics.ConditionalAttribute("DEBUG")]   // CS0582, remove conditional attribute
   void zz();
}
public class MyClass {
   public static void Main() {
   }
}


Compiler Error CS0583

Internal Compiler Error: likely culprit is 'culprit'.

Please create a bug report file with the '/bugreport' option, and submit the report to your assigned problem reporting contact.

Use the /bugreport compiler option to capture the problem.


Compiler Error CS0584

Internal Compiler Error: stage 'stage' symbol 'symbol'

Try to determine if the compiler is failing due to its inability to parse unexpected syntax. Then, contact Microsoft Technical Support.


Compiler Error CS0585

Internal Compiler Error: stage 'stage'

Try to determine if the compiler is failing due to its inability to parse unexpected syntax. Then, contact Microsoft Technical Support.


Compiler Error CS0586

Internal Compiler Error: stage 'stage'

Try to determine if the compiler is failing due to its inability to parse unexpected syntax. Then, contact Microsoft Technical Support.


Compiler Error CS0587

Internal Compiler Error: stage 'stage'

Try to determine if the compiler is failing due to its inability to parse unexpected syntax. Then, contact Microsoft Technical Support.


Compiler Error CS0588

Internal Compiler Error: stage 'LEX'

Try to determine if the compiler is failing due to its inability to parse unexpected syntax. Then, contact Microsoft Technical Support.


Compiler Error CS0589

Internal Compiler Error: stage 'PARSE'

Try to determine if the compiler is failing due to its inability to parse unexpected syntax. Then, contact Microsoft Technical Support.


Compiler Error CS0590

User-defined operators cannot return void

The purpose of a user-defined operator is to return an object.

The following sample generates CS0590:

namespace x
{
   public class a
   {
      public static void operator+(a A1, a A2)   // CS0590
      {
      }
      // try the following user-defined operator
      /*
      public static a operator+(a A1, a A2)
      {
         return A2;
      }
      */
      public static int Main()
      {
          return 1;
      }
   }
}


Compiler Error CS0591

Invalid value for argument to 'attribute' attribute

An attribute was passed either an invalid argument or two mutually exclusive arguments.

The following sample generates CS0591:

using System;
using System.Interop;

[structlayout(LayoutKind.Union | LayoutKind.Explicit)]   // CS0591
// try the following line instead
// [structlayout(LayoutKind.Union)]
class I {
}

public class a {
   public static void Main() {
   }
}


Compiler Error CS0592

Attribute 'attribute' is not valid on this declaration type. It is valid on 'type' declarations only.

An attribute was applied to a keyword for which it was not intended.

The following sample generates CS0592:

[interfacetype()] public class a {   // CS0592, dual is valid for an interface
   public static void Main() {
   }
}


Compiler Error CS0594

Floating-point constant is too large for type 'type'

A value was assigned to a floating-point variable that is too large for the variables of this data type. See Integral Types Table for information about the range of values allowed in data types.

The following sample generates CS0594:

namespace x
{
   public class a
   {
      public static void Main()
      {
         float f = 6.77777777777E400;   // CS0594, value too large
      }
   }
}


Compiler Error CS0595

'attribute' can only be specified on an empty class

Certain attributes can only be applied to an empty class.

The following sample generates CS0595:

[comimport, guid("00000000-0000-0000-0000-000000000001")]   // CS0595
public class MyClass {   // CS0595, class is not empty
   // delete the definition of MyFunc to resolve
   public static void MyFunc() {   // remove Main to resolve CS0595
   }
}

public class MyClass2 {
   public static void Main() {
   }
}


Compiler Error CS0596

The guid attribute must be specified with the comimport attribute

The guid attribute must be present when using the comimport attribute.

The following sample generates CS0596:

namespace x
{
   [comimport] public class a   // CS0596
   // try the following line to resolve this CS0596
   // [comimport, guid("00000000-0000-0000-0000-000000000001")]
   {
   }
   public class b
   {
      public static void Main()
      {
      }
   }
}


Compiler Error CS0597

Too few unnamed arguments for attribute 'attribute'

An attribute did not have the required number of arguments.

The following sample generates CS0597:

[permissionset()]   // CS0597, permissionset requires additional parameters
public class a {
   public static void Main() {
   }
}


Compiler Error CS0598

Invalid guid attribute argument. A guid attribute arguments must be a string constant of the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.

The guid attribute was not passed a valid value.


Compiler Error CS0599

Invalid value for named attribute argument 'format'

An invalid argument was passed to an attribute.

The following sample generates CS0599:

using System;

[sysstruct(format=System.Interop.CharacterSet.Auto)]   // CS0599
// try one of the two possible solutions
// [sysstruct(format=System.Interop.CharacterSet.Ansi)]
// [sysstruct(format=System.Interop.CharacterSet.Unicode)]
public class C {
}

public class A {
   public static void Main () {
   }
}


Compiler Error CS0601

The dllimport attribute must be specified on a method marked static and extern

The dllimport attribute was used on a method that did not have the correct access keywords.

The following sample generates CS0601:

using System;
public class C {
   [dllimport("KERNEL32.DLL")]
   extern int GetCurDirectory(int bufSize, StringBuilder buf);   // CS0601
   // try the following line instead
   // static extern int GetCurDirectory(int bufSize, StringBuilder buf);

}

public class MainClass {
   public static void Main () {
   }
}


Compiler Warning (level 1) CS0602

The feature 'old_feature' is deprecated. Please use 'new_feature' instead

A language feature used in your code (old_feature) is still supported, but that support may be removed in a future release. Instead, you should use the recommended syntax (new_feature).

One situation where this error can occur is with property overrides. Instead of overriding the property, you should override the individual accessor methods. For example:

class B {
   public int Prop {
      virtual get {
         return 0;
      }
   }
}

class C : B {
   public override int Prop {   // CS0602, delete override keyword
      get {
      // try
      // override get {
         return 0;
      }
   }
   public static void Main() {
   }
}


Compiler Error CS0603

Class 'class' marked attribute does not have public access

The attribute attribute can only be applied to classes that are public.

The following sample generates CS0603:

[attribute]   // CS0603
internal class BadAccessOnAttributeClass {   // CS0603
   public static void Main() {
   }
}


Compiler Error CS0604

Class 'attribute class' marked attribute is declared in 'enclosing class' which does not have public access.

The attribute attribute can only be applied to classes that are contained in public classes.

The following sample generates CS0604:

internal class BadAccessOnAttributeClass {   // CS0604, change internal to public
   [attribute]
   public class BadAccessOnNestedAttributeClass {
   }
}


Compiler Error CS0605

Public constructor 'constructor' of class marked attribute has arguments that are not attribute types

In a user-defined attribute, the class constructor can only take parameters of the following types: bool, byte, char, decimal, double, float, int, long, short, System.Variant, string, System.Type, enum (provided that it has public accessibility and that the types in which it is nested (if any) also have public accessibility), and an array type with a rank of one and an element type that is one of the types listed above. (Note that the type int[] is an attribute parameter type, but the type int[][] is not.)

The following sample generates CS0605:

using System;
public struct S {
}

[attribute]
public class PublicBadType {
   // The following constructor is allowed
//   public PublicBadType(string ui, int ii) {
//   }
// the following constructors generate CS0605
   public PublicBadType(object o) {}
   public PublicBadType(S s) {}
   public PublicBadType(int[][] aai) {}
   public PublicBadType(System.UInt32 ui) {}
   public PublicBadType(object[] ao) {}
   public PublicBadType(int[,] aai) {}

   public static void Main () {
   }
}

See Also

Attributes


Compiler Error CS0606

The type of public instance field 'instance' in class marked attribute does not have attribute type

In a user-defined attribute, a public instance can only take parameters of the following types: bool, byte, char, decimal, double, float, int, long, short, System.Variant, string, System.Type, enum (provided that it has public accessibility and that the types in which it is nested (if any) also have public accessibility), and an array type with a rank of one and an element type that is one of the types listed above. (Note that the type int[] is an attribute parameter type, but the type int[][] is not.)

The following sample generates CS0606:

using System;
public struct S {
}
[attribute]
public class PublicBadType {
   // the following line is a valid instance
   // public int i;

   // the following declarations generate CS0606
   public object o;
   public S s;
   public int[][] aai;
   public System.UInt32 ui;
   public object[] ao;
   public int[,] a2i;

   public static void Main () {
   }
}


Compiler Error CS0607

Classes marked with the attribute attribute must have at least one public instance constructor

A public constructor is required for an attribute class.

The following sample generates CS0607:

using System;
[AttributeUsageAttribute(AttributeTargets.All)]
public class MyAttribute : Attribute {
   MyAttribute() {   // CS0607
   // try the following line instead
   // public MyAttribute() {
   }
   public static void Main() {
   }
}


Compiler Error CS0608

Method 'method' cannot be marked with both dllimport and sysnative

The use of the dllimport attribute and sysnative attribute is not allowed on the same method.

The following sample generates CS0608:

public class C1 {
   [dllimport("kernel32.dll"), sysnative]
   public static extern int mf ();   // CS0608
}

public class MainClass {
   public static void Main () {
      }
}


Compiler Error CS0609

Cannot set the name attribute on an indexer marked override

The name attribute cannot be applied to an indexed property that is an override.

The following sample generates CS0609:

using System;

public class idx {
   public int this[int iPropIndex] {
      get {
         return 0;
      }
      set {
      }
   }
}

public class MonthDays : idx {
   [name("MonthInfoIndexer")]   // CS0609, delete to resolve this CS0609
   public override int this[int iPropIndex] {
      get {
         return 0;
      }
      set {
      }
   }
}

public class test {
   public static void Main( string[] args ) {
   }
}

See Also

Indexers


Compiler Error CS0610

Fields cannot be of type refany

The System.TypedReference type can only be applied to method parameters and the declaration of method parameters.

The following sample generates CS0610:

public class MainClass {
// System.TypedReference i;   // CS0610
   public static void Main () {
   }

   public static void Test(System.TypedReference i) {   // OK
   }
}


Compiler Error CS0611

Array elements cannot be of type refany

The System.TypedReference type is not valid for use as the type for elements in an array.

The following sample generates CS0611:

public class a {
   public static void Main() {
      System.TypedReference[] ao = new System.TypedReference [10];   // CS0611
      // try the following line instead
      // int[] ao = new int[10];
   }
}


Compiler Error CS0612

'member' is obsolete

The class designer marked a member with the obsolete attribute. This means that the member might not be supported in a future version of the class.

The following sample shows how accessing an obsolete member generates CS0612:

class MyClass {
   [obsolete]
   public static void ObsoleteMethod() {
   }
   [obsolete]
   public static int ObsoleteField;
}

class MainClass {
   static public void Main() {
      MyClass.ObsoleteMethod();   // CS0612 here: method is deprecated
      MyClass.ObsoleteField = 0;   // CS0612 here: field is deprecated
   }
}


Compiler Error CS0614

Cannot define a user-defined conversion to or from System.Variant

User-defined conversions, either implicit or explicit, cannot convert to or from the variant data type.

The following sample generates CS0614:

namespace VariantConversions {
   class C   {
      public static implicit operator variant (C c) {   // CS0614
         return 0;
      }
      public static void Main () {
      }
   }
}


Compiler Error CS0616

'class': is not an attribute class

An attempt was made to use an undefined attribute.

The following sample generates CS0616:

[CMyClass(i=5)]
public class CMyClass {
   public static void Main() {}
};

The following sample shows how you might define an attribute:

[attribute]
public class MyAttr {
   public int Name = 0;
   public int Count = 0;
   public MyAttr (int iCount, int sName) {
      Count = iCount;
      Name = sName;
   }
   public static void Main() {
   }
}

[MyAttr(5, 50)]
class Class1 {
}
[MyAttr(6, 60)]
interface Interface1 {
}


Compiler Error CS0617

'reference' is not a valid named attribute argument. Named attribute arguments must be public fields which are not readonly, static or const.

An attempt was made to access a private member of an attribute class.

The following sample generates CS0617:

using System;

[attributeusage(AttributeTargets.Struct|AttributeTargets.Class|AttributeTargets.Interface)]
public class MyClass : Attribute {
   public int Name;
   public MyClass (int sName) {Name = sName; Bad = -1;}
   int Bad;
   // try the following line instead
   // public int Bad;
}

[MyClass(5, Bad=0)] class Class1 {   // CS0617
}

public class MyClass2 {
   public static void Main () {
   }
}


Compiler Warning (level 1) CS0618

'member' is obsolete: 'text'

A class member was marked with the obsolete attribute, such that a warning will be issued when the class member is referenced.

The following sample generates CS0618:

public class C {
   [obsolete("Use newMethod instead", false)]   // warn if referenced
   public static void m2() {
   }

   public static void newMethod() {
   }
}

class MyClass {
   public static void Main() {
      C.m2();  // warning here: generates message "C.m() is obsolete
               // "Use newMethod instead""
   }
}


Compiler Error CS0619

'member' is obsolete: 'text'

A class member was marked with the obsolete attribute, such that an error will be issued when the class member is referenced.

The following sample generates CS0619:

public class C {
   [obsolete("Use newMethod instead", true)]   // generates an error on use
   public static void m() {
   }

   // this is the method you should be using
   public static void newMethod() {
   }
}

class MyClass {
   public static void Main() {
      C.m();   // CS0619
   }
}


Compiler Error CS0620

Indexers can't have void type

The return type of an indexer property cannot be void. An indexer must return a value.

The following sample generates CS0620:

class MyClass {
   public static void Main() {
      MyClass test = new MyClass();
      System.Console.WriteLine(test[2]);

   }

   void this [int intI] {   // CS0620, return type cannot be void
      get {
         // will need to return some value
      }
   }
}


Compiler Warning (level 1) CS0621

'member' : virtual or abstract members cannot be private

Private virtual members are not allowed.

The following sample generates CS0621:

class MyClass {
   private virtual void DoNothing() {   // CS0621
   }
   private int Price {
      abstract get;   // CS0621
   }
   public static void Main() {
   }
}


Compiler Error CS0622

Cannot use an array initializer expression to assign to a non-array type. Try using a new expression instead.

Syntax that is appropriate to initialize an array was used in the declaration of a non-array.

The following sample generates CS0622:

using System;

public class MainClass {
   public static void Main () {
      int i = { 1 };   // CS0622
      // try the following instead
      // int i = 1;
      // or
      // int i = new int(1);
      i++;
   }
}


Compiler Error CS0623

Array initializers can only be used in a variable or field initializer. Try using a new expression instead.

An array was initialized incorrectly.

The following sample generates CS0623:

class MyClass {
   public int[] x = { 2, 3, {4}};   // CS0623

   // The following declarations show how to initialize an array:
   // public int[] x = { 2, 3, 4};   // single-dimensional array
   // int[]x = new int[] {1,2,3};   // dynamically created array
   // int[,]x = { {1,2}, {3,4}, {5,6} };   // multidimensional array
   // object[]z = { null, "test", new int[] {2,3} };   // array of objects
   // int[][]x = { new int[]{2,3}, new int[]{4,5} };   // array of array of ints

   public static void Main() {
   }
}


Compiler Error CS0624

'method' : returnshresult attribute can only be placed on methods which are dllimport or members of a COM classic interface.

The returnshresult attribute was not used correctly.

The following sample generates CS0624:

using System;

public class MainClass {
   [returnshresult(true)]
   public void mf () {   // CS0624
   }
   // try the following lines instead
   // [returnshresult(true), dllimport("some.dll")]
   // public static extern void mf();
      public static void Main () {
      }
}


Compiler Error CS0625

'field' : Instance field types marked with structlayout(LayoutKind.Explicit) must have a structoffset attribute.

When a struct is marked with an explicit structlayout attribute, all fields in the struct must have the structoffset attribute.

The following sample generates CS0625:

using System;
using System.Interop;

[structlayout(LayoutKind.Explicit)]
struct Worksheet {
   // uncomment the next line to resolve
   // [structoffset(5)]
   public int i;   // not marked static, i.e., an instance field
}

public class MainClass {
   public static void Main () {
   }
}


Compiler Error CS0626

'method' : extern method has no implementation. Should be marked dllimport or sysnative

A method marked extern must also be marked with either the dllimport or sysnative attribute.

The following sample generates CS0626:

public class MyClass {
   static extern public void mf(); // CS0626
   // try the following
   // [dllimport("mydll.dll")] static extern public void mf();
   // or
   // [sysnative] static extern public void mf();
   public static void Main() {
   }
}


Compiler Error CS0627

the sysnative attribute cannot be placed on non-extern methods

The sysnative attribute must be used on methods that also use extern.

The following sample generates CS0627:

public class b {
   [sysnative] static public void mf() {   // CS0627
   }
   // try the following
   // [dllimport("mydll.dll")] static extern public void mf();
   // or
   // [sysnative] static extern public void mf();
   public static void Main() {
   }
}


Compiler Error CS0628

'member' : new protected member declared in sealed class

A sealed class cannot introduce a protected member because no class will be able to inherit from the sealed class and use the protected member.

The following sample generates CS0628:

sealed class C {
   protected int i;   // CS0628
}

class MyClass {
   public static void Main() {
   }
}


Compiler Error CS0629

Conditional member 'member' cannot implement interface member 'base class member'

ConditionalAttribute cannot be used on the implementation of an interface, for example, on the implementation of an interface method.

The following sample generates CS0629:

interface MyInterface {
   void MyMethod();
}

public class MyClass : MyInterface {
   [System.Diagnostics.ConditionalAttribute("debug")]
   public void MyMethod() {   // CS0629, remove the conditional attribute
   }
   public static void Main() {
   }
}


Compiler Error CS0630

union member 'member' is a reference type, or a struct containing a reference type

A class, or any reference type, cannot be a member of a union.

The following sample generates CS0630:

using System;
using System.Interop;
[structlayout(LayoutKind.Union)]   // CS0630
class MyClass2 {
   MyClass aa;
}
class MyClass {
}


Compiler Error CS0631

Indexers can't have ref or out parameters

The declaration for an indexer property cannot include the use of ref or out.

The following sample generates CS0631:

public class MyClass {
   public int this[ref int i] {   // CS0631
   // try the following line instead
   // public int this[int i] {
      get {
         return 0;
      }
   }
}

public class MyClass2 {
   public static void Main() {
   }
}


Compiler Error CS0632

'property' : Named attribute argument can't be a read only property

A property that is valid for an attribute was used as an argument to the attribute, which is not allowed.

The following sample, which uses the obsolete attribute, generates CS0632:

using System;
[obsolete("Obsolescence message",IsError=false)]   // CS0632
// the following line shows how to pass an argument to this property
// [obsolete("Obsolescence message",false)]
interface I1 {
}

public class CI1 : I1 {
}

public class MainClass {
   public static void Main () {
      object[] attrs = typeof(I1).GetCustomAttributes();
      // the next line shows how this read-only property might be used
      Console.WriteLine (((ObsoleteAttribute)attrs[0]).IsError);
   }
}


Compiler Error CS0633

argument to name attribute must be a valid identifier

Any argument that you pass to the name attribute must be a valid identifier.

The following sample generates CS0633:

using System;

public class MonthDays {
      [name("bad indexer name")]   // CS0633
      public int this[int iPropIndex] {
         get {
            return 0;
         }

         set {
         }
      }
}

public class test {
   public static void Main( string[] args ) {
   }
}


Compiler Error CS0634

'arg' : argument only valid for marshal of type System.Interop.UnmanagedType.CustomMarshaller

One of the arguments that was passed to the marshal attribute can only be used when the marshal format is System.Interop.UnmanagedType.CustomMarshaller.

The following sample generates CS0634:

class MyCustomMarshallerClass {
}

public class C {
   [dllimport("MyDll.dll")]
   public static extern void MyFunc(
   [marshal(System.Interop.UnmanagedType.I4, ComType="MyProgId")]   // CS0634

   // try the following instead
   // [marshal(System.Interop.UnmanagedType.CustomMarshaler,
   //   ComType="MyProgId",
   //   Marshaller=typeof(MyCustomMarshallerClass))
   // ]

   object o);
   public static void Main() {
   }
}


Compiler Error CS0635

'attribute' : System.Interop.UnmanagedType.CustomMarshaller requires named arguments ComType and Marshal

The ComType and Marshal arguments must be specified when the marshal format is System.Interop.UnmanagedType.CustomMarshaller.

The following sample generates CS0635:

class MyCustomMarshallerClass {
}

public class MyClass {
   [dllimport("MyDll.dll")]
   public static extern void MyFunc(
   [marshal(System.Interop.UnmanagedType.CustomMarshaler]   // CS0635

   // try the following lines instead
   // [marshal(System.Interop.UnmanagedType.CustomMarshaler, ComType="MyProgId",
   // Marshaller=typeof(MyCustomMarshallerClass))]

      object o);

   public static void Main() {
   }
}


Compiler Error CS0636

structoffset attribute can only be placed on members of type marked with the structlayout(LayoutKind.Explicit)

The structoffset attribute can only be used in structures that require explicit layout information.

The following sample generates CS0636:

using System;
using System.Interop;

[structlayout(LayoutKind.Sequential)]
struct Worksheet {
   [structoffset(4)]public int i;   // CS0636
   // try the following line instead
   // public int i;
}

public class MainClass {
   public static void Main () {
   }
}


Compiler Error CS0637

structoffset not allowed on static or const fields

The structoffset attribute cannot be used on fields marked with static or const.

The following sample generates CS0637:

using System;
using System.Interop;

[structlayout(LayoutKind.Explicit)]
public class MainClass {

      [structoffset(3)]   // CS0637
      public static int i;

   public static void Main () {
   }
}


Compiler Error CS0638

'string' : unknown global attribute

A string appeared in the position of a global attribute but the string was not a valid global attribute.

The following sample generates CS0638:

using System;
using System.Interop;

[abc : InterfaceType()]   // CS0638, abc is not a global attribute
class MyClass {
   public static void Main() {
   }
}


Compiler Error CS0640

Cannot set assembly global attributes when not generating an assembly

A source code file cannot contain assembly global attributes if it will be compiled with /target:module.

The following sample generates CS0640:

using System;
using System.Interop;

[assembly : AttrClass()]   // CS0640
class AttrClass : Attribute {
}

class MyClass {
   public static void Main() {
   }
}


Compiler Error CS0641

'attribute' : attribute is only valid on classes derived from System.Attribute

An attribute was used that can only be used on a class that derives from the NGWS Framework's System.Attribute.

The following sample generates CS0641:

using System;
using System.Interop;

[attributeusage(AttributeTargets.All)]
public class NonAttrClass {   // CS0641
// try the following line instead
// public class NonAttrClass : Attribute {
}

class MyClass {
   public static void Main() {
   }
}


Compiler Warning (level 3) CS0642

Possible mistaken null statement

A semicolon after a conditional statement may cause your code to not execute as intended.

The following sample generates CS0642:

using System;
class MyClass {
   public static void Main() {
      int i;
      for (i = 0; i < 10; i += 1) ; {   // CS0642, semicolon intentional?
         Console.WriteLine (i);
      }
   }
}


Compiler Error CS0643

'arg' duplicate named attribute argument

A parameter, arg, on a user-defined attribute was specified twice.

The following sample generates CS0643:

using System;
using System.Interop;

[attributeusage(AttributeTargets.Class)]
public class MyAttribute : Attribute {
   public MyAttribute() {
   }

   public int x;
}

[MyAttribute(x = 5, x = 6)]   // CS0643, error setting x twice
// try the following line instead
// [MyAttribute(x = 5)]
class MyClass {
}

public class MainClass {
   public static void Main () {
   }
}


Compiler Error CS0644

'class1' can't derive class from special class 'class2'

Classes cannot explicitly derive from System.Enum or System.ValueType.

The following sample generates CS0644:

class MyClass : System.ValueType {   // CS0644
}


Compiler Error CS0645

Identifier too long

A class name or other identifier can be no longer than 512 characters.


Compiler Error CS0646

Cannot specify System.Reflection.DefaultMemberAttribute on type containing an indexer

If a class or other type specifies System.Reflection.DefaultMemberAttribute, it cannot contain an indexed property.

The following sample generates CS0646:

// to resolve, delete System.Reflection.DefaultMemberAttribute or
// remove the indexed property
[System.Reflection.DefaultMemberAttribute("x")]   // CS0646
class MyClass {
   public int this[int index] {   // an indexed property
   // try the following line instead
   // public int prop {
      get {
         return 0;
      }
   }

   public int x;
   public static void Main() {
   }
}


Compiler Error CS0647

The format of the uuid was not correct.' reason

A syntax error occurred because of an invalid uuid format.

The following sample generates CS0647:

using System.Runtime.InteropServices;

[GuidAttribute("z")]   // CS0647
// try the following line instead
// [GuidAttribute("00000000-0000-0000-0000-000000000001")]
class MyClass {
   public int this[int index] {
      get {
         return 0;
      }
   }

   public static void Main() {
   }
}


Compiler Error CS0648

'type' is a type not supported by the language

Metadata that was generated from another language, possibly using the Managed Extensions for C++, contained a type that was not marked as managed. Information about this type is included in the metadata, but the type is not available to programs written in C#.


Compiler Warning (level 3) CS0649

Field 'field' is never assigned to, and will always have its default value 'value'

The compiler detected an uninitialized, private or internal field declaration that is never assigned a value.

The following sample generates CS0649:

using System.Collections;
class MyClass {
   Hashtable table;  // CS0649
   // You may have intended to initialize the variable to null
   // Hashtable table = null;

   // or, you may have meant to create an object here
   // Hashtable table = new Hashtable();

   public void Func(object o, string p) {
      // or here
      // table = new Hashtable();
      table[p] = o;

   }

   public static void Main() {
   }
}


Compiler Error CS0650

Syntax error, bad array declarator. To declare a managed array the rank specifier precedes the variable's identifier

An array was declared incorrectly.

The following sample generates CS0650:

public class MyClass {
   public static void Main() {
      int myarray[2];   // CS0650
      // try the following lines instead
      // int[] myarray = new int[2] {1,2};
      // myarray[0] = 0;
   }
}


Compiler Error CS1001

Identifier expected

You did not supply an identifier. For example, when declaring an enum, you must specify members.

The following sample generates CS1001:

public class clx {
   enum splitch : int {
      'a', 'b' // CS1001, 'a' is not a valid int identifier
   };

   public static void Main() {
   }
}


Compiler Error CS1002

; expected

The compiler detected a missing semicolon.

The following sample generates CS1002:

namespace x {
   abstract public class clx {
      int i   // CS1002, missing semicolon
      public static int Main() {
         return 0;
      }
   }
}


Compiler Error CS1003

Syntax error, 'char' expected

The compiler will generate this error for any one of several error conditions. Review you code to find the syntax error.

The following sample generates CS1003:

public class b {
   public static void Main() {
      int[] a;
      a[);   // CS1003
   }
}


Compiler Error CS1004

Duplicate 'modifier' modifier

A duplicate modifier, such as an access modifier, was detected.

The following sample generates CS1004:

public class clx {
   public public static void Main() {   // CS1004, two public keywords
   }
}


Compiler Error CS1005

Illegal indirection to managed type

It is not possible to use unsafe references (pointer syntax) on an object or string.

The following sample generates CS1005:

namespace x {
   public class C {
      public object* po;   // CS1005
      // try the following line instead
      // public object po;
   }
   public class a {
      public static int Main() {
         return 1;
      }
   }
}


Compiler Error CS1007

Property accessor already defined

Declaring a property requires you to declare the property’s accessor methods. However, a property cannot have duplicate code for the same accessor method.

The following sample generates CS1007:

namespace x {
   abstract public class clx {
      int i {
         get {
            return 0;
         }
         get {   // CS1007, duplicate get method
            return 0;
         }
      }
      public static int Main() {
         return 0;
      }
   }
}


Compiler Error CS1008

Type byte, sbyte, short, ushort, int, uint, long, or ulong expected

Certain data types, such as enums, can only be declared to hold data of specified types.

The following sample generates CS1008:

abstract public class clx {
   enum splitch : char {   // CS1008, char not valid type for enums
      x, y, z
   }
   public static void Main() {
   }
}


Compiler Error CS1009

Unrecognized escape sequence

An unexpected character followed a backslash (\) in a string.

The following sample generates CS1009:

// CS1009
namespace x {
   public class a {
      public static void f(int i, char j) {
         string a = "\m";   // CS1009
         // try the following line instead
         // string a = "\t";
      }
      public static void Main() {
      }
   }
}


Compiler Error CS1010

Newline in constant

A string was not properly delimited.

The following sample generates CS1010:

namespace x {
   abstract public class clx {
      string a = "Hello World;   // CS1010, add end quote
      public static int Main() {
         return 0;
      }
   }
}


Compiler Error CS1011

Empty character literal

A char was declared and initialized to a null. The initialization of a char must specify a character.

The following sample generates CS1011:

namespace x {
   abstract public class clx    {
      char b = '';   // CS1011
   }
}


Compiler Error CS1012

Too many characters in character literal

An attempt was made to initialize a char constant with more than one character.

The following sample generates CS1012:

namespace x {
   public class b : c {
      char a = 'xx';   // CS1012
      public static void Main() {
      }
   }
}


Compiler Error CS1013

Invalid number

A value was assigned to a long variable that is out of the range of the long data type.

The following sample generates CS1013:

namespace x {
   public class a {
      public static void Main() {
      }
   }
   public class b : a {
      public const long i = 0xFFFFFFFFFFFFFFFFFL;   // CS1013
      // try a smaller value
   }
}

See Also

Integral Types Table


Compiler Error CS1014

A get or set method expected

A method declaration was found in a property declaration. Only get and set methods are allowed to be declared in a property.

The following sample generates CS1014:

namespace x {
   public class clx {
      int j {
         get {
            return 0;
         }

      int z() {   // CS1014, unexpected, not get or set
         return 0;
      }
   }
   public class cly : clx {
      public static void Main() {
      }
   }
}

See Also

Property Declaration | Accessors


Compiler Error CS1015

An object, string, or class type expected

An attempt was made to pass a predefined data type into a catch block. Only data types that derive from System.Exception can be passed into a catch block.

The following sample generates CS1015:

using System;
namespace x {
   public class b : Exception {
   }
   public class a {
      public static void Main() {
         try {
         }
         catch(int) {   // CS1015, int not derived from System.Exception
         }
      }
   }
}

See Also

Exception Handling Statements


Compiler Error CS1016

Named attribute argument expected

Unnamed attribute arguments must appear before the named arguments.

The following sample generates CS1016:

using System;

[attribute(AllowOn = AttributeTargets.Classes)]
public class HelpAttribute {
        public HelpAttribute(string url) {  // url is a positional parameter

                m_url = url;
        }
        public string Topic = null;         // Topic is a named parameter
        private string m_url = null;
        public string Url { get {return m_url;} }
}

[HelpAttribute("http://intranet/inhouse")]
class Class1 {
    }

[HelpAttribute(Topic="Samples", "http://intranet/inhouse")]   // CS1016
// try the following line instead
// [HelpAttribute("http://intranet/inhouse", Topic="Samples")]
class Class2 {
    }

public class MainClass {
    public static void Main () {
        }
    }


Compiler Error CS1017

Try statement already has an empty catch block

A catch block that does not take any parameters must be the last in a series of catch blocks.

The following sample generates CS1017:

using System;
namespace x {
   public class b : Exception {
   }

   public class a {
      public static void Main() {
         try {
         }
         catch {      // CS1017, must be last catch
         }

         catch(b) {
            throw;
         }
      }
   }
}

See Also

Exception Handling Statements


Compiler Error CS1018

Keyword this or base expected

The compiler encountered an incomplete constructor declaration.

The following sample generates CS1018 and suggests several ways to resolve the error:

namespace x {
   public class C {
   }
   public class a : C {
      public a(int i) {
      }
      public a () :   { // CS1018
      // possible resolutions:
      // public a () resolves by removing :
      // public a () : base() calls C's default constructor
      // public a () : this(1) needs a constructor that takes an int
      }
      public static int Main() {
          return 1;
      }
   }
}


Compiler Error CS1019

Overloadable unary operator expected

You have a unary operator that returns a value of another class. If you want this conversion, you need to do an implicit or explicit cast.

The following sample generates CS1019:

public class ii {
   int i {
      get {
         return 0;
      }
   }
}

public class a {
   public static ii operator ii(a aa) { // CS1019
   // try either of the two following declarations
   //   public static implicit operator ii(a aa) {
   //   public static explicit operator ii(a aa) {
      return new ii();
   }
   public static void Main() {
   }
}


Compiler Error CS1020

Overloadable binary operator expected

An attempt was made to define an operator overload, but the operator was not a binary operator (taking two parameters).

The following sample generates CS1020:

public class iii {
   public static int operator ++(iii aa, int bb) {   // CS1020, change ++ to +
      return 0;
   }
   public static void Main() {
   }
}


Compiler Error CS1021

Integral constant is too large

A value was assigned to an integral type that is larger that the largest possible unsigned integer value.

The following sample generates CS1021:

enum F : int {
   a=(int)2590000000000000000000   // CS1021
}

public class clx {
   public static void Main() {
   }

}


Compiler Error CS1022

Type or namespace definition, or end-of-file expected

A program does not have a matching set of braces.

The following sample generates CS1022:

namespace x {
}
}   // CS1022


Compiler Error CS1023

Embedded statement cannot be a declaration or labeled statement

An embedded statement, such as the statements following an if statement, can contain neither declarations nor labeled statements.

The following sample generates CS1023 twice:

namespace x {
   public class a {
      public static void Main() {
         int i = 0;
         if (1)
            int i;   // CS1023, declaration is not valid here
         if (1)
         xx:   i++;   // CS1023, labeled statement is not valid here
      }
   }
}


Compiler Error CS1024

Preprocessor directive expected

A line began with the pound symbol (#), but the subsequent string was not a valid preprocessor directive.

The following sample generates CS1024:

#import System   // CS1024


Compiler Error CS1025

Single-line comment or end-of-line expected

A line with a preprocessor directive cannot have a multiline comment.

The following sample generates CS1025:

#if true /* hello
*/   // CS1025
#endif   // this is a good comment

CS1025 could also occur if you attempt some invalid preprocessor directive:

#define a
namespace _1.b {
   #if a 1   // CS1025, invalid syntax
      public class a {
      }
   #endif
   public class aa {
      public static void Main() {
      }
   }
}


Compiler Error CS1026

) expected

An incomplete preprocessor directive was found. For example:

#if (a == b   // CS1026, add closing )
#endif


Compiler Error CS1027

#endif directive expected

A matching #endif preprocessor directive was not found for a specified #if directive. Or, the compiler may have found a #endregion directive when there was no matching #region directive inside a #if block.

The following sample generates CS1027:

#if true   // CS1027, uncomment next line to resolve
// #endif

namespace x {
   public class clx {
      public static void Main() {
      }
   }
}


Compiler Error CS1028

Unexpected preprocessor directive

A preprocessor directive was found but not expected.

For example, a #endif was found with no preceding #if.

The following sample generates CS1028:

#endif   // CS1028, no matching #if
namespace x {
   public class clx {
      public static void Main() {
      }
   }
}


Compiler Error CS1029

#error: 'text'

CS1029 displays the text of an error defined with the #error directive.

The following sample shows how to create a user-defined error:

namespace x {
   public class a {
      public static void Main() {
         #error Let's give an error here!   // CS1029
      }
   }
}


Compiler Warning (level 1) CS1030

#warning: 'text'

CS1030 displays the text of a warning defined with the #warning directive.

The following sample shows how to create a user-defined warning:

namespace x {
   public class a {
      public static void Main() {
         #warning Let's give a warning here!   // CS1030
      }
   }
}


Compiler Error CS1031

Type expected

A type specifier is expected.

The following sample generates CS1031:

namespace x {
   public class ii {
   }

   public class a {
      public static operator +(a aa) {   // CS1031
      // the following line resolves CS1031
      // public static ii operator +(a aa)
         return new ii();
      }
      public static void Main() {
         e = new base;   // CS1031, not a type
         e = new this;   // CS1031, not a type
         e = new ();      // CS1031, not a type

      }
   }
}


Compiler Error CS1032

Cannot define/undefine preprocessor symbols after first token in file

The #define and #undef preprocessor directives must be used at the beginning of a program, before any other keywords, such as the namespace declaration.

The following sample generates CS1032:

namespace x {
   public class clx {
      #define a   // CS1032, put before namespace
      public static void Main() {
      }
   }
}


Compiler Error CS1033

Compiler limit exceeded: File cannot exceed 'number' lines

A source-code file exceeded the maximum allowable number of lines that the compiler can process. To resolve this error, create two or more source-code files from the original file.


Compiler Error CS1034

Compiler limit exceeded: Line cannot exceed 'number' characters

The limit for the number of characters allowed on a line is 2046.


Compiler Error CS1035

End-of-file found, '*/' expected

An open comment was not matched with a close comment.

The following sample generates CS1035:

public class a {
   public static void Main() {
   }
}
/*
needs closing comment


Compiler Error CS1036

( or . expected

The XML in a doc comment was badly formed.

The following sample generates CS1036:

using System;
/// <seealso cref="System.String*"/>   // CS1036
// try the following line instead
/// <seealso cref="System.String"/>
public class Test {
    public static void Main() {
    }
}


Compiler Error CS1037

Overloadable operator expected

When specifying a comment with /doc, the compiler encountered an invalid link.

The following sample generates CS1037:

public class A {
   public static A operator / (A a1, A a2) {
      return null;
   }

   /// <seealso cref="A.operator@"/>   // CS1037
   // try the following line instead
   // /// <seealso cref="A.operator/"/>
   public static A Divide(A a1, A a2) {
      return a1 / a2;
   }

   public static void Main() {
   }
}


Compiler Error CS1038

#endregion directive expected

A #region directive did not have a matching #endregion directive.

The following sample generates CS1038:

#region testing
public class clx {
   public static void Main() {
   }
}
// CS1038
// uncomment the next line to resolve this CS1038
// #endregion


Compiler Error CS1039

Unterminated string literal

The compiler detected an ill-formed string literal.

See section 2.5.9.5 of the C# Language Reference for a discussion of the various ways to declare string literals.

The following sample generates CS1039:

public class MyClass {
   public static void Main() {
      string b = @"hello, world;   // CS1039
   }
}

To resolve the error, add the terminating ".


Compiler Error CS1040

Preprocessor directives must appear as first non-whitespace character on a line

A preprocessor directive was found on a line and was not the first token on the line. A directive must be the first token on the line.

The following sample generates CS1040:

/* Define a symbol, X */ #define X   // CS1040

// try the following two lines instead
// /* Define a symbol, X */
// #define X

public class MyClass {
   public static void Main() {

   }
}


Compiler Error CS1501

No overload for method 'method' takes 'number' arguments

A call was made to a class method, but there is no form of the method that takes the necessary number of arguments.

CS1501 could occur when you allocate memory for a delegate; you must also specify the name of the method that the delegate represents.

The following sample generates CS1501:

public class a {
   /*
   declare the following constructor to resolve this CS1501
   public a(int i) {
   }
   */
   public static void Main() {
      a aa = new a(2);   // CS1501
   }
}

The following sample generates CS1501:

using System;
delegate string func(int i);   // declare the delegate

class a {
   static func dt;   // class member-field of the declared delegate type
   public static void Main() {
      // dt = new func();   // CS1531
      // try the following line instead
      dt = new func(z);   // this works
      x(dt);
   }

   public static string z(int j) {
      Console.WriteLine(j);
      return j.ToString();
   }

   public static void x(func hello) {
      hello(8);
   }
}


Compiler Error CS1502

The best overloaded method match for 'declaration' has some invalid arguments

A class method was called but there is no form of the method that takes the necessary number of arguments with the necessary data types.

The following sample generates CS1502:

namespace x {
   public class a {
      public a(char i) {
      }
      /* try this constructor instead
      public a(int i) {
      }
      */
      public static void Main() {
      a aa = new a(2222);   // CS1502
      }
   }
}


Compiler Error CS1503

Argument 'arg': cannot convert from 'type1' to 'type2'

The type of one argument in a method does not match the type that was passed when the class was instantiated.

See CS1502 for a discussion on how to resolve this error.


Compiler Error CS1504

Source file 'file' could not be opened ('reason')

A source file could not be opened or read by the compiler. The file may be locked by another application, or there could be some other operating system problem. reason is the operating system's reason for why the compiler could not open or read the file.


Compiler Error CS1505

Missing file specification for 'compiler option' command-line switch

A compiler option did not have a complete specification. For example, when you use the /s compiler option, you must also specify the files that you want to compile. For example: /s:*.src.

For more information, see the list of C# compiler options.


Compiler Error CS1506

Output file 'file' is not in a subdirectory of the assembly path 'directory'

When the compiler creates an assembly (/target compiler option), all output files in the compilation must either be in the same directory as (or in a subdirectory of) the assembly manifest file.

The assembly manifest file is the .exe or the first .dll produced by the compilation.


Compiler Error CS1507

Cannot link resource file 'file' when building a module

/resource or /linkresource was used in the same compilation with /target:module, which is not allowed.


Compiler Error CS1508

Resource identifier 'identifier' has already been used in this assembly

In a compilation, the same identifier (identifier) was passed to two or more /resource or /linkresource compiler options.

For example, the following options would generate CS1508:

/resource:anyfile.bmp,DuplicatIdent /linkresource:a.bmp,DuplicatIdent


Compiler Error CS1509

Referenced file 'file' is not an assembly; use '/addmodule' option instead

An output file (output file 1), produced in a compilation that used /target:module (does not have an assembly manifest), was specified to /reference. So, rather than appending an assembly to the assembly for the current program, the metadata information in output file 1 will be added to the assembly for the current program.

Output file 1 must be in the same directory as (or in a subdirectory of) the assembly manifest file for the current compilation.


Compiler Error CS1510

A ref or out argument must be an lvalue

Only a variable can be passed as a ref parameter in a method call. A ref value is similar to passing a pointer.

The following sample generates CS1510:

public class C {
   public static int i = 0;

   public static void mf(ref int i) {
      i++;
   }

   public static void Main () {
      mf (ref 1);   // CS1510, can't pass a number as a ref parameter
      // try the following to resolve the error
      // mf (ref i);
   }
}


Compiler Error CS1511

Keyword base is not available in a static method

The base keyword was used in a static method.

The following sample generates CS1511:

public class A  {
   public int j = 0;
   }
class C : A {
   public void func() {
      base.j = 3;   // base allowed here
   }

   public static int Main () {
      base.j = 3;   // CS1511
      return 1;
   }
}


Compiler Error CS1512

Keyword base is not available in the current context

The base keyword was used outside of a method, property, or constructor.

The following sample generates CS1512:

using System;
class CMyClass {
   public String xx = base.ToString();   // CS1512
   // try the following line instead
   // public String xx;
   public void x() {
      Console.WriteLine(base.ToString());
   }
   public static void Main() {
      CMyClass z = new CMyClass();
      z.x();
   }
}


Compiler Error CS1513

} expected

The compiler expected a closing curly brace that was not found.

The following sample generates CS1513:

namespace x {   // CS1513, no close curly brace


Compiler Error CS1514

{ expected

The compiler expected an opening curly brace that was not found.

The following sample generates CS1514:

namespace x
   // CS1514, no open curly brace


Compiler Error CS1515

Response file 'file' included multiple times

A command line included the same response file multiple times. A response file can contain an instruction specifying a response file.

For more on response files, see @ (Specify Response File).


Compiler Error CS1516

Source file 'file' specified multiple times

A command line included the same source file multiple times. A response file can contain an instruction specifying a source file.

For more on response files, see @ (Specify Response File).


Compiler Error CS1517

Invalid preprocessor expression

The compiler encountered an invalid preprocessor expression.

The following sample shows some valid and invalid preprocessor expressions:

#if symbol      // OK
#endif
#if !symbol     // OK
#endif
#if (symbol)    // OK
#endif
#if true        // OK
#endif
#if false       // OK
#endif
#if 1           // CS1517
#endif
#if ~symbol     // CS1517
#endif
#if *           // CS1517
#endif

class x {
   public static void Main() {
   }
}

See Also

Preprocessor Directives


Compiler Error CS1518

Expected class, delegate, enum, interface, struct, or union

A declaration was found that is not supported in a namespace. Inside a namespace, the compiler accepts only classes, structs, unions, enums, interfaces, and delegates.

The following sample generates CS1518:

namespace x {
   sealed class c1 {};      // OK
   public namespace f2 {};  // CS1518
   sealed f3 {};            // CS1518
}


Compiler Error CS1519

Invalid token 'token' in class, struct, or interface member declaration

Any class member that does not begin with a class-member type token will generate CS1519.

The following sample generates CS1519:

public class IMyInterface {
   checked void f4();   // CS1519
   lock void f5();      // CS1519
   namespace;            // CS1519
   int i;               // OK
}


Compiler Error CS1520

Class, struct, or interface method must have a return type

A method that is declared in a class, struct, or interface must have an explicit return type.

The following sample generates CS1520:

public class x {
   f7(){}            // CS1520, needs return type
   // try the following definition
   /*
   void f7(){
   }
   */
   public static void Main() {
   }
}


Compiler Error CS1521

Invalid base type

A base class specification was ill formed.

The following sample generates CS1521:

class CMyClass {
   public static void Main() {
   }
}
class CMyClass1 : CMyClass {    // OK
}
class CMyClass2 : CMyClass[] {  // CS1521
}
class CMyClass3 : CMyClass* {   // CS1521
}

See Also

base


Compiler Warning (level 1) CS1522

Empty switch block

The compiler detected a switch block with no case or default statement. A switch block must have one or more case or default statements.

The following sample generates CS1522:

using System;
class x {
   public static void Main() {
      int i = 6;
      switch(i) {   // CS1522
         // add something to the switch block, for example:
         /*
         case (5):
            Console.WriteLine("5");
            return;
         default:
            Console.WriteLine("not 5");
            return;
         */
      }
   }
}


Compiler Warning (level 1) CS1523

The keyword case or default must precede code in switch block

Code was found in a switch statement that was part of neither a case nor a default block.

The following sample generates CS1523:

class x {
   public static void Main() {
      int i = 0;
      switch (i) {
         i = 6;   // CS1523, code not under a label
         // try the following
         /*
         default:
            i = 6;
         */
      }
   }
}


Compiler Error CS1524

Expected catch or finally

A try block must be followed by a catch or finally block.

The following sample generates CS1524:

class x {
   public static void Main() {
      try {
         // Code here
      } catch {
}
      try {
         // Code here
      } finally {
}
      try {
         // Code here
      }
   }   // CS1524, missing catch or finally
}

See Also

Exception Handling Statements


Compiler Error CS1525

Invalid expression term 'character'

The compiler detected an invalid character in an expression.

The following sample generates CS1525:

class x {
   public static void Main() {
      int i = 0;
      i = i +   // OK - identifier
      'c' +     // OK - character
      (5) +     // OK - parenthesis
      [ +       // CS1525, operator not a valid expression element
      throw +   // CS1525, keyword not allowed in expression
      void;     // CS1525, void not allowed in expression
      ;
   }
}


Compiler Error CS1526

'new' expression requires () or [] after type

The new operator, used to dynamically allocate memory for an object, was not specified correctly.

The following sample shows how to use new to allocate space for an array and an object.

public class y {
   public static int i = 0;
   public int myi = 0;
}

public class z {
   public static void Main()
   {
      y py = new y;   // CS1526
      y[] aoys = new y[10];   // Array of Ys
      for (int i = 0; i < aoys.Length; i++)
         aoys[i] = new y();   // an object of type y
   }
}


Compiler Error CS1527

Namespace elements cannot be explicitly declared as private or protected

Only public members are allowed in a namespace. The public keyword must be explicitly specified.

The following sample generates CS1527:

namespace bad {
   private class foo1 {};    // CS1527
   protected class foo2 {};  // CS1527
}


Compiler Error CS1528

Expected ; or = (cannot specify constructor arguments in declaration)

A reference to a class was formed as if an object to the class was being created. For example, there was an attempt to pass a variable to a constructor. Use the new operator to create an object of a class.

The following sample generates CS1528:

using System;
public class B {
   public B(int i) {
      _i = i;
   }
   public void PrintB() {
      Console.WriteLine(_i);
   }
   private int _i;
}

public class mine {
   public static void Main() {
      B b(3);   // CS1528, reference is not an object
      // try one of the following
      // B b;
      // or
      // B bb = new B(3);
      // bb.PrintB();
   }
}


Compiler Error CS1529

A using clauses must precede all other namespace elements

Statements with a using clause must appear first in a namespace.

The following sample generates CS1529:

namespace x {
   namespace subspace {
      using Microsoft;
      class SomeClass {
      };
      using Microsoft;   // CS1529, place before class definition
   }
   using System.Reflection;  // CS1529, place before namespace 'subspace'
}
using System;   // CS1529, place at the beginning of the file


Compiler Error CS1530

Keywords new or unsafe not allowed on namespace elements

It is not necessary to specify the new keyword on any construct that is in a namespace. No construct in namespace scope can be an unsafe construct.

The following sample generates CS1530:

namespace a {
   new class i {   // CS1530
   }
   // try the following instead
   class ii {
      public static void Main() {
      }
   }
}


Compiler Error CS1532

Property or indexer 'property' cannot override one accessor and hide another. Override both accessors by adding the override keyword to both, remove one of the accessors, or add the new keyword to hide the inherited property.

It is not possible to override one property accessor and hide the other accessor in the same property.

The following sample generates CS1532:

public class C {
   public string s {
      virtual get {
         return "test";
      }
      virtual set {
      }
   }
}

public class CD : C {
   public string s {
      override get {
         return "test II";
      }
      set {   // CS1532
      // the following line is one way to resolve this error
      // override set {
      }
   }

   public static void Main () {
   }
}

See Also

Property Declaration | Accessors


Compiler Error CS1533

Invoke cannot be called directly on a delegate

The Invoke method cannot be called on a delegate. For example:

using System;
public class A {
   public static void Main() {
   }
}

public delegate void B(A a);

internal class C {
   public void Baz(A a, B b) {
      b.Invoke(a);   // CS1533
      // try the following line instead
      // b(a);
   }
}


Compiler Error CS1534

Overloaded binary operator 'operator' only takes two parameters

The definition of a binary overloadable operator must take two parameters.

The following sample generates CS1534:

class MyClass {
   public static MyClass operator - (MyClass MC1, MyClass MC2, MyClass MC3) {   // CS1534
   // try the following line instead
   // public static MyClass operator - (MyClass MC1, MyClass MC2) {
      return new MyClass();
   }
   public static int Main() {
      return 1;
   }
}


Compiler Error CS1535

Overloaded unary operator 'operator' only takes one parameter

The definition of a unary overloadable operator must take one parameter.

The following sample generates CS1535:

class MyClass {
   // uncomment the method parameter to resolve this CS1535
   public static MyClass operator ++ (/*MyClass MC1*/) {   // CS1535
      return new MyClass();
   }
   public static int Main() {
      return 1;
   }
}


Compiler Error CS1536

Invalid parameter type void

It is not necessary or valid to specify a void parameter other than a void pointer.

The following sample generates CS1536:

class a {
   public static int x( void ) {   // CS1536
   // try the following line instead
   // public static int x() {
      return 0;
   }
   public static void Main() {
   }
}


Compiler Error CS1537

The using alias 'alias' appeared previously in this namespace

You defined a symbol twice as an alias for a namespace. A symbol can only be defined once.

The following sample generates CS1537:

namespace x {
   using System;
   using Object = System.Object;
   using Object = System.Object;   // CS1537, delete this line to resolve

   using System = System;
}


Compiler Error CS1538

'type' : cannot reference a type through a derived type of its containing class; try 'path_to_type' instead

An attempt was made to access a member of a class through a derived class, which is not permitted in this situation.

The following sample generates CS1538:

class C {
   public class Inner {
      public static int v;
   }
}

class D : C {
   public static void Main() {
      C cValue = new C();

      D.Inner.v = 5;   // CS1538
      // try the following line instead
      // C.Inner.v = 5;
   }
}


Compiler Error CS1539

Illegal back-quote delimiter '`'

It is possible to specify a string literal other than with double quotation marks. You can use the back quote and then enclose the string by using any character that is not a space. CS1539 is generated when you use a space to delimit a string after a back quote.

The following sample generates CS1539:

using System;
public class MyClass {
   public static void Main() {
      string s = ` j ;   // CS1539
      // try the following line instead
      // string s = `[j];
      Console.WriteLine(s);
   }
}


Compiler Error CS1540

Cannot access protected member 'member' via a qualifier of type 'type1'; the qualifier must be of type 'type2' (or derived from it)

Although a derived class can access protected members of its base class, it cannot do so through an instance of the base class.

The following sample generates CS1540:

public class Base {
   protected void func() {
   }
}

public class Derived : Base {
   public static void test(Base anotherInstance) {
   // the method declaration could be changed as follows
   // public static void test(Derived anotherInstance) {
      anotherInstance.func();   // CS1540
   }
}

public class Tester : Derived {
   public static void Main() {
      Base pBase = new Base();
      // the allocation could be changed as follows
      // Derived pBase = new Derived();
      test(pBase);
   }
}


Compiler Error CS1541

Invalid include option: 'symbol' -- cannot include directories

The compiler detected an attempt to specify a directory rather than a specific file. For example, when you use the /reference compiler option, you must specify a file; it is not possible to specify a directory.

For example, the following parameter passed to the /import compiler option would generate CS1541:

/reference:\


Compiler Error CS1542

'dll' cannot be added to this assembly because it already is an assembly; use '/R' option instead

The file that was referenced with the /addmodule compiler option was not built with /target:module; use /reference to reference the file in this compilation.


Compiler Error CS1545

Property or indexer 'property' references a type not supported by the language; try directly calling accessor methods 'set accessor' or 'get accessor'

Your code is consuming an object that has a default indexed property and tried to use the indexed syntax. To resolve this error, call the property's get or set accessor method.


Compiler Error CS1546

Property or indexer 'property' references a type not supported by the language; try directly calling accessor method 'accessor'

Your code is consuming an object that has a default indexed property and tried to use the indexed syntax. To resolve this error, call the property's accessor method.


Compiler Error CS1547

Keyword 'void' cannot be used in this context

The compiler detected an invalid use of the void keyword.

The following sample generates CS1547:

public class MyClass {
   void BadMethod() {
      void i;   // CS1547, cannot have variables of type void
   }
   public static void Main() {
   }
}


Compiler Error CS1548

Cryptographic failure while signing assembly 'assembly' -- 'reason'

A failure occurred in a cryptographic function. This indicates a problem with the operating system installation.


Compiler Error CS1549

Appropriate cryptographic service not found

An RSA compatible cryptographic service provider was not installed.


Compiler Error CS1550

Key file 'file' could not be opened ('reason')

The file specified with the /assembly.keyfile compiler option could not be opened.


Compiler Error CS1551

Indexers must have at least one parameter

An indexer that takes no arguments was declared.

The following sample generates CS1551:

public class MyClass {
   int intI;
   int this[] {   // CS1551
   // try the following line instead
   // int this[int i] {
      get {
         return intI;
      }
      set {
         intI = value;
      }
   }

   public static void Main() {
   }
}


Compiler Error CS1552

Array type specifier, [], must appear before parameter name

The position of the array type specifier is after the variable name in the array declaration.

The following sample generates CS1552:

public class C {
   public static void Main(string args[]) {   // CS1552
   // try the following line instead
   // public static void Main(string [] args) {
   }
}


Compiler Error CS1553

Illegal declaration; use 'implicit operator <dest-type> (...' instead

The return type for an operator must precede the parameter list.

The following sample generates CS1553:

class MyClass {
   public static int implicit operator (MyClass f) {   // CS1553
   // try the following line instead
   // public static implicit operator int (MyClass f) {
      return 6;
   }

   public static void Main() {
   }

}


Compiler Error CS1554

Illegal declaration; use '<type> operator ++ (...' instead

The return type for a user-defined operator must appear before the keyword operator.

The following sample generates CS1554:

class MyClass {
   public static operator ++ MyClass (MyClass f) {   // CS1554
   // try the following line instead
   // public static MyClass operator ++ (MyClass f) {
      return new MyClass ();
   }

   public static void Main() {
   }
}


Compiler Error CS1555

Could not find 'class' for Main method in program 'program'

A class was specified to the /main compiler option, but the class name was not found in the source code.


Compiler Error CS1556

'construct' specified for Main method must be a valid class or struct

The /main compiler option was passed an identifier that was not a class name.


Compiler Error CS1557

'class' specified for Main method not found in program 'output file1', was found in 'output file2'

The /main compiler option was specified for one output file in a multi-output file compilation. However, the class was not found in the source code for the /main compilation; it was found in a source code file for one of the other output files in the compilation.


Compiler Error CS1558

'class' does not have a suitable Main method

The /main compiler option specified a class in which to look for a Main method. However, the Main method was not defined as follows:

public static void Main(){
}


Compiler Error CS1559

'object' is imported and cannot be used as an entry point for program 'program'

An invalid class was specified to the /main compiler option; the class cannot be used as a location for the Main method.


Compiler Error CS1560

Filename specified for #line is too long

The file name that was specified with #line exceeded _MAX_PATH (256 characters) or the line on which #line was found exceeded 2000 characters.


Compiler Error CS1561

Key container 'key' does not exist and could not be loaded

The name of a key container was passed to the /assembly.keyname compiler option, but the name is not the name of a key container.


Compiler Error CS1562

Outputs without source must have the /out option specified

The compilation could create an output file, but there was no source code file as input from which the name of the output file could be implied. For example, you may be trying to compile a metadata- or resource-only file.

Use the /out compiler option to specify the name of the output file.


Compiler Error CS1563

Output 'output file' does not have any source files

The /out compiler option was specified, but no source code files follow. You should either not use /out, or you should specify the source code files following /out.


Compiler Error CS1564

Key file 'key' does not have the private key needed for signing

The key file specified with the /assembly.keyfile compiler option did not have private key information, so a fully signed assembly cannot be created. Use /assembly.sign- to create the assembly or add private key information to the assembly.


Compiler Error CS1565

Conflicting options specified: Win32 resource file; Win32 icon

It is not valid to specify both the /win32res compiler option and the /win32ico compiler option in the same compilation.


Compiler Error CS1566

Error reading resource file 'file' -- 'reason'

The compiler had trouble with the file name passed to the /resource compiler option.


Compiler Error CS1567

Error generating Win32 resource: 'file'

Your compilation either used the /win32icon compiler option or did not use /win32res, which causes the compiler to generate a file that contains resource information, but the compiler was unable to create the file due to insufficient disk space or some other error.

If you are unable to resolve the file-generation problem, you could use /win32res, which does not generate a file that contains resource information.


Compiler Error CS1569

Error generating XML documentation file 'file' ('reason')

When generating an XML file with /doc, the file could not be opened and written to for the indicated reason.


Compiler Warning (level 1) CS1570

XML comment on 'construct' has badly formed XML -- 'reason'

When using /doc, any comments in the source code must be in XML. Any error with your XML markup will generate CS1570. For example:

  • If you are passing a string to a cref, such as in an <exception> tag, the string must be enclosed in double quotation marks.
  • If you are using a tag, such as <seealso>, which does not have a closing tag, you must specify a forward slash before the closing angle bracket.
  • If you need to use a greater-than or less-than symbol in the text of description, you need to represent them with &gt; or &lt;.

The following sample generates CS1570:

namespace ns {
// the following line generates CS1570
/// <summary> returns true if < 5 </summary>
// try this instead
// /// <summary> returns true if &lt;5 </summary>

   public class MyClass {
      public static void Main () {
      }
   }
}


Compiler Error CS1571

XML comment on 'construct' has a duplicate param tag for 'parameter'

When using the /doc compiler option, multiple comments were found for the same method parameter. Remove one of the duplicate lines.

The following sample generates CS1571:

public class MyClass {
   /// <param name='Int1'>Used to indicate status.</param>
   /// <param name='Char1'>An initial.</param>
   /// <param name='Int1'>Used to indicate status.</param> // CS1571
   public static void MyMethod(int Int1, char Char1) {
   }

   public static void Main () {
   }
}


Compiler Error CS1572

XML comment on 'construct' has a param tag for 'parameter', but there is no parameter by that name

When using the /doc compiler option, a comment was specified for a parameter that does not exist for the method. Change the value passed to the name attribute or remove one of the comment lines.

The following sample generates CS1572:

public class MyClass {
   /// <param name='Int1'>Used to indicate status.</param>
   /// <param name='Char1'>Used to indicate status.</param>
   /// <param name='Char2'>???</param> // CS1572
   public static void MyMethod(int Int1, char Char1) {
   }

   public static void Main () {
   }
}


Compiler Warning (level 4) CS1573

Parameter 'parameter' has no matching param tag in XML comment (but other parameters do)

When using the /doc compiler option, a comment was specified for some but not all parameters in a method. You may have forgotten to enter a comment for these parameters.

The following sample generates CS1573:

public class MyClass {
   /// <param name='Int1'>Used to indicate status.</param>
   // enter a comment for Char1?
   public static void MyMethod(int Int1, char Char1) {
   }

   public static void Main () {
   }
}


Compiler Warning (level 1) CS1574

XML comment on 'construct' has cref attribute 'item' that could not be found

A string passed to a cref tag, for example, within an <exception> tag, referred to a member that is not available within the current build environment. The string that you pass to a cref tag must be the syntactically correct name of a member or field.

For more information, see Tags for Documentation Comments.

The following sample generates CS1574:

// compile with /doc
using System;
/// <exception cref="System.Console.WriteLin">An exception class.</exception>  // CS1574
// instead, uncomment and try the following line
// /// <exception cref="System.Console.WriteLine">An exception class.</exception>
class EClass : Exception {
}

class TestClass {
   public static void Main() {
      try {
      }
      catch(EClass) {
      }
   }
}


Compiler Error CS1575

A stackalloc expression requires [] after type

The size of the requested allocation, with stackalloc, must be specified in square brackets.

The following sample generates CS1575:

public class MyClass {
   unsafe public static void Main() {
      int *p = stackalloc int (30);   // CS1575
      // try the following line instead
      // int *p = stackalloc int [30];
   }
}


Compiler Error CS1576

The line number specified for #line directive is missing or invalid

The compiler detected an error with the value passed to the #line directive.

The following sample generates CS1576:

using System;
public class MyClass {
   public static void Main() {
      #line  "abc.sc"         // CS1576
      // try the following line instead
      // #line  100 "abc.sc"
      intt i;  // error will be reported on line 101
   }
}


Compiler Error CS1577

Assembly generation failed -- reason

The assembly-generation part of the compilation failed. See the errors documentation for the alink utility (al.exe) for more information.


Compiler Error CS1578

Filename, single-line comment or end-of-line expected

After a #line directive, only a file name (in double quotation marks) or a single-line comment is allowed.

The following sample generates CS1578:

using System;
public class MyClass {
   public static void Main() {
      #line 3 abc.cs  // CS1578
      // try the following line instead
      // #line 3 "abc.cs"
      intt i;  // error will be reported on line 101
   }
}


Compiler Error CS1579

foreach statement cannot operate on variables of type 'type1' because 'type2' does not contain a definition for 'member', or it is inaccessible

A data type (type1) does not contain a member that allows foreach to iterate through type2.

The following sample generates CS1579:

using System;
public class C1 {
   public int GetEnumerator () {
      return 0;
   }
}

public class MainClass {
   public static void Main () {
      C1 c1 = new C1();
      foreach (int x in c1) {   // CS1579
      }
   }
}


Compiler Error CS1580

Invalid type for parameter 'parameter number' in XML comment cref attribute

When attempting to reference an overload form of a method, the compiler detected an error. Typically, this indicates that the parameter name, and not the type, was specified.

The following sample generates CS1580:

using System;

/// <seealso cref="Test(i)"/>   // CS1580
// try the following line instead
// /// <seealso cref="Test(int)"/>
public class MyClass {
   public static void Main() {
   }

   public void Test(int i) {
   }

   public void Test(char i) {
   }
}


Compiler Error CS1581

Invalid return type in XML comment cref attribute

When attempting to reference a method, the compiler detected an error due to an invalid return type.

The following sample generates CS1581:

public class MyClass {
   public static void Main() {
   }

   public static explicit operator int(MyClass f) {
      return 0;
   }
}

/// <seealso cref="MyClass.explicit operator intt(MyClass)"/>   // CS1581
// try the following line instead
// /// <seealso cref="MyClass.explicit operator int(MyClass)"/>
public class MyClass2 {
}


Compiler Error CS1600

Compilation cancelled by user

A compilation with the C# compiler was cancelled while using the Visual C++ IDE.


Compiler Error CS1900

Warning level must be in the range 0-4

The /warn compiler option can only take one of five possible values (0, 1, 2, 3, or 4). Any other value passed to /warn will result in CS1900. For example, the following compiler option will result in CS1900:

/W:5


Compiler Error CS1901

Conflicting options specified: Warning level 0; Treat warnings as errors

The /warn and /warnaserror compiler options are mutually exclusive; you cannot both turn warnings off and have warnings reported as errors.


Compiler Error CS1902

'number' is an invalid assembly version number

The /assembly.version compiler option was not specified correctly.


Compiler Error CS1904

'number' is not a valid warning number

A number that was passed to the /nowarn compiler option was not a valid warning.


Compiler Error CS2000

Compiler initialization failed unexpectedly

This error indicates various initialization failures. Reinstall Visual C++.


Compiler Error CS2001

Source file 'file' could not be found

A source file name was passed to the compiler, but the compiler was unable to find the file. Check the spelling of the file name.


Compiler Error CS2002

Source file 'file' specified multiple times

A source file name was passed to the compiler more than once. You can only specify a file once to the compiler to build an output file.


Compiler Error CS2003

Response file 'file' included multiple times

A response file was passed to the compiler more than once. A response file can only be passed to the compiler once per output file.

For more on response files, see @ (Specify Response File).


Compiler Error CS2005

Missing file specification for 'option' command-line option

A compiler option was only partially specified. For example, when using /recurse, you must specify the directory to search (/s:mydir).


Compiler Error CS2006

Command-line syntax error: Missing 'text' for 'option' switch

The syntax for option requires additional text. Refer to the documentation for this compiler option.


Compiler Error CS2007

Unrecognized command-line option: 'option'

The compiler was passed a string that began with a forward slash (/) but was not a compiler option.


Compiler Error CS2008

No inputs specified

The compiler was invoked and compiler options were specified but no source-code files were passed.


Compiler Error CS2011

Unable to open response file 'file'

A response file was specified in a compilation, but the compiler was unable to locate and open the file.

For more on response files, see @ (Specify Response File).


Compiler Error CS2012

Can't open 'file' for writing

While using the /bugreport: file compiler option, the file could not be opened for writing. Make sure you specified a valid file name and that the file is not read-only.


Compiler Error CS2013

Invalid image base number 'value'

An invalid value (not a number) was passed to the /baseaddress compiler option.


Compiler Error CS2014

Compiler option 'old option' is obsolete, please use 'new option' instead

The form of the compiler option is deprecated. See C# Compiler Options for more information.


Compiler Error CS2015

'file' is a binary file instead of a source code file

A file was passed to the compiler that was a binary file. The compiler expects a source code file.


Compiler Error CS2016

Code page 'codepage' is invalid or not installed

The /codepage compiler option was passed an invalid value.


Compiler Error CS2017

Cannot specify /main and /dll for the same output file

You cannot specify a Main entry point when you are building a /target:library.


Compiler Error CS2018

Unable to find messages file 'cscmsgs.dll'

The .dll that contains the compiler’s error and warning messages was not found. This file must be present in the same directory as the other compiler support files.


Compiler Error CS2019

Invalid target type for /target: must specify 'exe', 'winexe', 'library', or 'module'

The /target compiler option was used, but an invalid parameter was passed. To resolve this error, recompile the program using the form of the /target option that is appropriate to your output file.


Compiler Error CS2020

Only the first set of input files can build a target other than 'module'

In a compilation that produces multiple output files, the output file that receives the assembly must be the first output file specified on the command line.


Compiler Error CS3000

Methods with variable arguments are not CLS-compliant

A public or protected method cannot accept a variable number of arguments and still be compliant with the Common Language Subset (CLS).

The following sample generates CS3000:

[assembly:System.CLSCompliant(true)]
public class a {
   internal void good(__arglist) {   // OK, not a public method
   }

   public void bad2(__arglist) {   // CS3000, public method with varargs
   }

   public static void Main() {
   }
}


Compiler Error CS3001

Argument type 'type' is not CLS-compliant

A public or protected method cannot accept a parameter whose type is not compliant with the Common Language Subset (CLS).

The following sample generates CS3001:

[assembly:System.CLSCompliant(true)]

public class a {
   public void bad(ushort i) {   // CS3001
   }
   private void OK(ushort i) {   // OK, private method
   }
   public static void Main() {
   }
}


Compiler Error CS3002

Return type of 'method' is not CLS-compliant

A public or protected method cannot return a value whose type is not compliant with the Common Language Subset (CLS).

The following sample generates CS3002:

[assembly:System.CLSCompliant(true)]
public class a {
   public ushort bad() {   // CS3002, public method
      ushort a;
      a = ushort.MaxValue;
      return a;
   }
   private ushort OK() {   // OK, private method
      ushort a;
      a = ushort.MaxValue;
      return a;
   }
   public static void Main() {
   }
}


Compiler Error CS3003

Type of 'variable' is not CLS-compliant

A public or protected variable cannot be of a type that is not compliant with the Common Language Subset (CLS).

The following sample generates CS3003:

[assembly:System.CLSCompliant(true)]
public class a {
   public ushort a1;      // CS3003, public variable
   public static void Main() {
   }
}


Compiler Error CS3004

Mixed and decomposed unicode characters are not CLS-compliant

Only composed UNICODE characters are allowed in public or protected identifiers in order to be compliant with the Common Language Subset (CLS).


Compiler Error CS3005

Identifier 'identifier' differing only in case is not CLS-compliant

A public or protected identifier that differs from another public or protected identifier only in the case of one or more letters is not compliant with the Common Language Subset (CLS).

The following sample generates CS3003:

using System;
[assembly:CLSCompliant(true)]
public class a {
   public static int a1 = 0;
   public static int A1 = 1;   // CS3005
   public static void Main() {
      Console.WriteLine(a1);
      Console.WriteLine(A1);
   }
}


Compiler Error CS3006

Overloaded method 'method' differing only in ref or out is not CLS-compliant

A method cannot be overloaded based on the ref or out parameter and be compliant with the Common Language Subset (CLS).

The following sample generates CS3006:

using System;
[assembly: CLSCompliant(true)]

public class MyClass {
   public void f(int i) {
   }
   public void f(ref int i) {   // CS3006
   }

   public static void Main() {
   }
}

To resolve this error, comment out the assembly-level attribute or remove one of the method definitions.


Compiler Error CS3008

Identifier 'identifier' is not CLS-compliant

A public or protected identifier breaks compliance with the Common Language Subset (CLS) if it begins with an underscore character (_).

The following sample generates CS3008:

using System;
[assembly:CLSCompliant(true)]
public class a {
   public static int _a = 0;  // CS3008

   // OK, private
   // private static int _a1 = 0;

   public static void Main() {
   }
}


Compiler Error CS3009

'type': base type or interface is not CLS-compliant

A base type was marked as not having to be compliant with the Common Language Subset (CLS) in a module that was marked as being CLS compliant. Either remove the attribute that specifies the module is CLS compliant or remove the attribute that indicates the type is not CLS compliant.

The following sample generates CS3009:

using System;
[assembly:CLSCompliant(true)]

[CLSCompliant(false)]
public interface I {
   void mf();
}

public class C : I {   // CS3009
   public void mf () {
   }

   public static void Main () {
   }
}


Compiler Error CS3010

'member': CLS-compliant interfaces cannot have non CLS-compliant members

In a module marked with [assembly:CLCSompliant(true)], an interface contains a member marked with [CLCSompliant(false)]. Remove one of the CLS compliance attributes.

The following sample generates CS3010:

using System;
[assembly:CLSCompliant(true)]

public interface I {
   [CLSCompliant(false)]
   int mf();   // CS3010

}

public class C : I {
   public int mf() {
      return 1;
   }
   public static void Main() {
   }
}


Compiler Error CS3011

'member': non CLS-compliant members cannot be abstract

A class member cannot be both abstract and non-compliant with the Common Language Subset (CLS). The CLS specifies that all class members shall be implemented.

The following sample generates CS3011:

using System;
[assembly:CLSCompliant(true)]

public abstract class I {
   [CLSCompliant(false)]
   public abstract int mf();   // CS3011

   // OK
   [CLSCompliant(false)]
   public void mf2() {
   }
}

public class C : I {
   public override int mf() {
      return 1;
   }

   public static void Main() {
   }
}


Compiler Error CS3012

You cannot specify CLSCompliant attribute on modules

In order for a module to be compliant with the Common Language Subset (CLS), via [module:System.CLCSompliant(true)], it must be built with the /target:module compiler option.

The following sample, when built without /target:module, generates CS3012:

[module:System.CLSCompliant(true)]

public class C {
   public static void Main() {
   }
}


Compiler Error CS3013

Added module is not CLS-compliant, or is missing CLS-compliant attribute

A module that was compiled with the /target:module compiler option was added to a compilation via /addmodule. However, the module’s compliance with the Common Language Subset (CLS) does not agree with the CLS state of the current compilation.

CLS compliance is indicated with the module attribute. For example, [module:CLSCompliant(true)] indicates that the module is CLS compliant, and [module:CLSCompliant(false)] indicates that the module is not CLS compliant. The default is [module:CLSCompliant(false)].


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

Compiler Error CS5000

Unknown compiler option '/option'

An invalid compiler option was specified.


Compiler Warning (level 1) CS5001

Program 'output file' does not have an entry point defined

No Main method was found in the code that produces an executable file.

The following sample generates CS5001:

namespace x
{
   public class a
   {
   // uncomment the following line to resolve CS5001
   //   public static void Main(){}
   }
}


C# Compiler Options

The compiler produces executable (.exe) files or dynamic-link libraries (DLLs).

Every compiler option is available in two forms: -option and /option. The documentation only shows the /option form.

To find a particular compiler option, see one of the following lists:

  • C# Compiler Options Listed Alphabetically
  • C# Compiler Options Listed by Category

For information about building a C# application from the command line, see Building from the Command Line.

See Also

Building a C# Program


Building from the Command Line

As an alternative to building a program from within Visual Studio, you may want to build at the command line.

Invoking the Compiler

The compiler can be invoked by typing the name of its executable (csc.exe) on the command line. You may need to adjust your path if you want csc.exe to be invoked from any subdirectory on your computer. See Setting the Path and Environment Variables for Command-Line Builds for more information.

Differences Between C# Compiler and C++ Compiler Output

There are no object (.obj) files created as a result of invoking the C# compiler; .exe and .dll output files are created directly. As a consequence of this, the C# compiler does not need a linker.

Command-Line Syntax

Parsing C++ Command-Line Arguments discusses how to specify command-line arguments.

Sample Command Lines

  • Compiles File.cs producing File.exe:
    csc File.cs
  • Compiles File.cs producing File.dll:
    csc /target:library File.cs
  • Compiles File.cs but no executable is created (useful for simply checking syntax):
    csc /nooutput File.cs
  • Compiles File.cs and creates My.exe:
    csc /out:My.exe File.cs
  • Compiles all of the C# files in the current directory, with optimizations on and defines the DEBUG symbol. The output is File2.exe:
    csc /define:DEBUG /optimize /out:File2.exe *.cs
  • Compiles all of the C# files in the current directory producing a debug version of File2.dll. No logo and no warnings are displayed:
    csc /target:library /out:File2.dll /warn:0 /nologo /debug *.cs
  • Compiles all of the C# files in the current directory to Something.xyz (a DLL):
    csc /target:library /out:Something.xyz *.cs

See Also

C# Compiler Options


C# Compiler Options Listed Alphabetically

The following compiler options are sorted alphabetically.

Option | Purpose
@ | Specify a response file.
/? | List compiler options to stdout.
/addmodule | Specify one or more modules to be part of this assembly.
/baseaddress | Specify the base address of a DLL.
/bugreport | Create a file that contains information that makes it easy to report a bug.
/checked | Specify whether integer arithmetic that overflows the bounds of the data type will cause an exception at run time.
/codepage | Specify the code page to use for all source code files in the compilation.
/debug | Emit debugging information.
/define | Define preprocessor symbols.
/doc | Process documentation comments to an XML file.
/fullpaths | Specify the absolute path to the file in compiler output.
/help | List compiler options to stdout.
/incremental | Enable incremental compilation of source code files.
/linkresource | Link a NGWS resource to an assembly.
/main | Specify the location of the Main method.
/nologo | Suppress compiler banner information.
/nooutput | Compile but do not create an output file.
/nostdlib | Do not import standard library (mscorlib.dll).
/nowarn | Suppress the compiler’s ability to generate specified warnings.
/optimize | Enable/disable optimizations.
/out | Specify output file.
/recurse | Search subdirectories for source files to compile.
/reference | Import metadata from a file that contains an assembly.
/resource | Embed a NGWS resource into the output file.
/target | Specify the format of the output file using one of four options: /target:exe /target:library /target:module /target:winexe
/unsafe | Compile code that uses the unsafe keyword.
/warn | Set warning level.
/warnaserror | Promote warnings to errors.
/win32icon | Insert a .ico file into the output file.
/win32res | Insert a Win32 resource into the output file.

See Also

C# Compiler Options | C# Compiler Options Listed by Category | Building from the Command Line


C# Compiler Options Listed by Category

The following compiler options are sorted by category.

Optimization

Option | Purpose
/optimize | Enable/disable optimizations.

Output Files

Option | Purpose
/doc | Process documentation comments to an XML file.
/out | Specify output file.
/nooutput | Compile but do not create an output file.
/target | Specify the format of the output file using one of four options: /target:exe /target:library /target:module /target:winexe.

NGWS Assemblies

Option | Purpose
/addmodule | Specify one or more modules to be part of this assembly.
/nostdlib | Do not import standard library (mscorlib.dll).
/reference | Import metadata from a file that contains an assembly.

Debugging/Error Checking

Option | Purpose
/bugreport | Create a file that contains information that makes it easy to report a bug.
/checked | Specify whether integer arithmetic that overflows the bounds of the data type will cause an exception at run time.
/debug | Emit debugging information.
/fullpaths | Specify the absolute path to the file in compiler output.
/nowarn | Suppress the compiler’s ability to generate specified warnings.
/warn | Set warning level.
/warnaserror | Promote warnings to errors.

Preprocessor

Option | Purpose
/define | Define preprocessor symbols.

Resources

Option | Purpose
/linkresource | Link a NGWS resource to an assembly.
/resource | Embed a NGWS resource into the output file.
/win32icon | Insert a .ico file into the output file.
/win32res | Insert a Win32 resource into the output file.

Miscellaneous

Option | Purpose
@ | Specify a response file.
/? | List compiler options to stdout.
/baseaddress | Specify the base address of a DLL.
/codepage | Specify the code page to use for all source code files in the compilation.
/help | List compiler options to stdout.
/incremental | Enable incremental compilation of source code files.
/main | Specify the location of the Main method.
/nologo | Suppress compiler banner information.
/recurse | Search subdirectories for source files to compile.
/unsafe | Compile code that uses the unsafe keyword.

See Also

C# Compiler Options | C# Compiler Options Listed Alphabetically | Building from the Command Line


@ (Specify Response File)

@response_file

where:

response_file
A file that lists compiler options or source code files to compile.

Remarks

The @ option lets you specify a file that contains compiler options and source code files to compile. These compiler options and source code files will be processed by the compiler just as if they had been specified on the command line.

To specify more than one response file in a compilation, specify multiple response file options. For example:

@file1.rsp @file2.rsp

In a response file, multiple compiler options and source code files can appear on one line. A single compiler option specification must appear on one line (cannot span multiple lines). Response files can have comments that begin with the # symbol.

Specifying compiler options from within a response file is just like issuing those commands on the command line. See Building from the Command Line for more information.

This option is not available from within the development environment; it is only available when compiling from the command line.

Example

The following are a few lines from a sample response file:

# build the first output file
/target:exe /out:MyExe.exe source1.cs source2.cs

See Also

C# Compiler Options


/addmodule (Import Metadata)

/addmodule:file[;file2]

where:

file, file2
An output file that contains metadata. The file cannot contain an assembly manifest. To import more than one file, separate file names with either a comma or a semicolon.

Remarks

The /addmodule option causes the compiler to make all type information from file available to the project you are currently compiling.

file cannot contain an assembly manifest. For example, if the output file was created with /target:module, its metadata can be imported with /addmodule.

If you (implicitly or explicitly) specify any /target option other than /target:module with /addmodule, the files that you pass to /addmodule will become part of the project's assembly. An assembly is required to run an output file that has one or more files added with /addmodule.

Use /reference to import metadata from an output file that does contain an assembly manifest.

Example

Compile source file input.cs and add metadata from metad1.dll and metad2.dll to produce out.exe:

csc /addmodule:metad1.dll;metad2.dll /out:out.exe input.cs

See Also

C# Compiler Options


/baseaddress (Specify Base Address of DLL)

/baseaddress:address

where:

address
The base address for the DLL. This address can be specified as a decimal, hexadecimal, or octal number.

Remarks

The default base address for a DLL is set by the NGWS runtime.

Be aware that the lower-order word in this address will be rounded. For example, if you specify 0x11110001, it will be rounded to 0x11110000.

To complete the signing process for a DLL, use –R of SN.EXE.

See Also

C# Compiler Options


/bugreport (Report a Problem)

/bugreport:file

where:

file
The name of the file that you want to contain your bug report.

Remarks

The /bugreport option will cause the following information to be placed in file:

  • A copy of all source code files in the compilation.
  • A listing of the compiler options used in the compilation.
  • Version information about your compiler, runtime, and operating system.
  • Compiler output, if any.
  • A description of the problem, which you will be prompted for.
  • A description of how you think the problem should be fixed, which you will be prompted for.

Because a copy of all source code files will be placed in file, you may want to reproduce the (suspected) code defect in the shortest possible program.

Example

Compile t2.cs and put all bug reporting information in the file problem.txt:

csc /bugreport:problem.txt t2.cs

See Also

C# Compiler Options


/checked (Check Integer Arithmetic)

/checked[+ | -]

Remarks

The /checked option specifies whether an integer arithmetic statement that is not in the scope of the checked or unchecked keywords and that results in a value outside the range of the data type shall cause a run-time exception.

An integer arithmetic statement that is in the scope of the checked or unchecked keywords is not subject to the effect of the /checked option.

If an integer arithmetic statement that is not in the scope of the checked or unchecked keywords results in a value outside the range of the data type, and /checked+ (/checked) is used in the compilation, then that statement will cause an exception at run time. If /checked- is used in the compilation, then that statement will not cause an exception at run time.

Example

Compile t2.cs and specify that an integer arithmetic statement that is not in the scope of the checked or unchecked keywords and results in a value outside the range of the data type will cause an exception at run time.

csc t2.cs /checked

See Also

C# Compiler Options


/codepage (Specify Code Page for Source Code Files)

/codepage:id

where:

id
The id of the code page to use for all the source code files in the compilation.

Remarks

If you compile one or more source code files that were not created to use the default code page on your computer, you can use the /codepage option to specify which code page should be used. /codepage applies to all source code files in your compilation.

If the source code files were created with the same codepage that is in effect on your computer or if the source code files were created with UNICODE or UTF-8, you need not use /codepage.

See GetCPInfo for information on how to find which code pages are supported on your system.

See Also

C# Compiler Options


/debug (Emit Debugging Information)

/debug[+ | -]

Remarks

The /debug option causes the compiler to generate debugging information and place it in the output file(s). Use this option to create debug builds. If /debug (or /debug+) is not specified, you will not be able to debug the output file of your program.

By default, debugging information is not emitted (/debug-). To emit debugging information, specify /debug or /debug+.

Example

Place debugging information in output file app.exe:

csc /debug /out:app.exe test.cs

See Also

C# Compiler Options


/define (Preprocessor Definition)

/define:name[;name2]

where:

name[;name2]
The name of one or more symbols that you want to define.

Remarks

The /define option defines name as a symbol in your program. It has the same effect as using a #define preprocessor directive in your source file. A symbol remains defined until an #undef directive in the source file removes the definition or the compiler reaches the end of the file.

You can use symbols created by this option with #if, #else, #elif, and #endif to compile source files conditionally.

/d is the short form of /define.

You can define multiple symbols with /define by using a semicolon or comma to separate symbol names. For example:

/define:DEBUG;TUESDAY

Example

// compile with /define:xx or uncomment the next line
// #define xx
using System;
public class Test
{
   public static void Main()
   {
      #if (xx)
         Console.WriteLine("xx exists");
      #else
         Console.WriteLine("xx does not exist");
      #endif
   }
}

See Also

C# Compiler Options


/doc (Process Documentation Comments)

/doc:file

where:

file
The output file for XML, which is populated with the comments in the source code files of the compilation.

Remarks

The /doc option allows you to place documentation comments in an XML file.

In source code files, lines that begin with /// and that precede a user-defined type such as a class, delegate, or interface; member such as a field, event, property, or method; or a namespace declaration can be processed as comments and placed in a file.

The source code file that contains Main is output first into the XML.

Unless you compile with /target:module, file will contain <assembly></assembly> tags specifying the name of the file containing the assembly manifest for the output file of the compilation.

See Tags for Documentation Comments for ways to generate documentation from comments in your code.

Example

// compile with /doc:outfile.xml
using System;

/// A delegate that ...
delegate int MyDelegate();

namespace a {

   /// MyI defines ...
   public interface MyI {
   }

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

   /// MyClass is a class that ...
   public class MyClass {

      /// An enum in MyClass that defines ...
      enum Days {Sun=1, Sat, Mon, Tue, Wed, Thu, Fri};

      /// A public field in MyClass that ...
      public static int i = 0;

      /// An event in MyClass that ...
      public event EventHandler Click {
      get {
         return null;
      }

      set {
      }
   }

      /// A property in MyClass that ...
      public int Prop {
         set {
         }

         get {
            return 0;
         }
      }

      /// A method in MyClass, Main ...
      public static void Main () {
      }
   }
}

Program Output

See Processing the XML File to understand how the <member> tag is formed.

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>outfile.exe</name>
    </assembly>
    <members>
        <member name="T:MyDelegate">
            A delegate that ...
        </member>
        <member name="T:a.MyI">
            MyI defines ...
        </member>
        <member name="T:a.MyClass">
            MyClass is a class that ...
        </member>
        <member name="F:a.MyClass.i">
            A public field in MyClass that ...
        </member>
        <member name="M:a.MyClass.Main">
            A method in MyClass, Main ...
        </member>
        <member name="E:a.MyClass.Click">
            An event in MyClass that ...
        </member>
        <member name="P:a.MyClass.Prop">
            A property in MyClass that ...
        </member>
        <member name="T:a.MyClass.Days">
            An enum in MyClass that defines ...
        </member>
    </members>
</doc>

See Also

C# Compiler Options | XML Documentation Tutorial


Tags for Documentation Comments

The C# compiler will process documentation comments in your code to an XML file. Processing the XML file to create documentation is a detail that needs to be implemented at your site.

The following tags provide commonly used functionality in user documentation:

<c> | <param> | <see>
<code> | <paramref> | <seealso>
<example> | <permission> | <summary>
<exception> | <remarks> | <value>
<list> | <returns> |

See Also

/doc (Process Documentation Comments)


<c>

<c>text</c>

where:

text
The text you would like to indicate as code.

Remarks

The <c> tag gives you a way to indicate that text within a description should be marked as code. Use <code> to indicate multiple lines as code.

Compile with /doc to process documentation comments to a file.

Example

public class MyClass {
   /// <remarks><c>MyMethod</c> is a method in the <c>MyClass</c> class.
   /// </remarks>
   public static void MyMethod(int Int1) {
   }

   public static void Main () {
   }
}

See Also

Tags for Documentation Comments


<code>

<code>content</code>

where:

content
The text you want marked as code.

Remarks

The <code> tag gives you a way to indicate multiple lines as code. Use <c> to indicate that text within a description should be marked as code.

Compile with /doc to process documentation comments to a file.

Example

See <example> for an example of using <code>.

See Also

Tags for Documentation Comments


<example>

<example>description</example>

where:

description
A description of the code sample.

Remarks

The <example> tag lets you specify an example of how to use a method or other library member. Commonly, this would involve use of the <code> tag.

Compile with /doc to process documentation comments to a file.

Example

public class MyClass {
   /// <example> This sample shows how to call the GetZero method.
   /// <code>
   ///   class MyClass {
   ///      public static int Main() {
   ///         return GetZero();
   ///      }
   ///   }
   /// </code>
   /// </example>
   public static int GetZero() {
      return 0;
   }

   public static void Main () {
   }
}

See Also

Tags for Documentation Comments


<exception>

<exception cref="member">description</exception>

where:

cref="member"
A reference to a member or field that is available to be called from the current compilation environment. The compiler checks that the given code element exists and translates member to the canonical element name in the output XML. member must appear within double quotation marks (" ").
description
A description for the exception class.

Remarks

The <exception> tag lets you document an exception class.

Compile with /doc to process documentation comments to a file.

Example

using System;
/// <exception cref="System.Exception">This is an exception class.</exception>
class EClass : Exception {
}

class TestClass {
   public static void Main() {
      try {
      }
      catch(EClass) {
      }
   }
}

See Also

Tags for Documentation Comments


<list>

<list type="bullet" | "number" | "table">
   <listheader>
      <term>term</term>
      <description>description</description>
   </listheader>
   <item>
      <term>term</term>
      <description>description</description>
   </item>
</list>

where:

term
A term to define, which will be defined in text.
description
Either an item in a bullet or numbered list or the definition of a term.

Remarks

The <listheader> block is used to define the heading row of either a table or definition list. When defining a table, you only need to supply an entry for term in the heading.

Each item in the list is specified with an <item> block. When creating a definition list, you will need to specify both term and text. However, for a table, bulleted list, or numbered list, you only need to supply an entry for text.

A list or table can have as many <item> blocks as needed.

Compile with /doc to process documentation comments to a file.

Example

public class MyClass {
   /// <remarks>Here is an example of a bulleted list:
   /// <list type="bullet">
   /// <item>
   /// <description>Item 1.</description>
   /// </item>
   /// <item>
   /// <description>Item 2.</description>
   /// </item>
   /// </list>
   /// </remarks>
   public static void Main () {
   }
}

See Also

Tags for Documentation Comments


<para>

<para>content</para>

where:

content
The text of the paragraph.

Remarks

The <para> tag is for use inside a tag, such as <remarks> or <returns>, and lets you add structure to the text.

Compile with /doc to process documentation comments to a file.

Example

See <remarks> for an example of using <para>.

See Also

Tags for Documentation Comments


<param>

<param name='name'>description</param>

where:

name
The name of a method parameter. Enclose the name in single quotation marks (' ').
description
A description for the parameter.

Remarks

The <param> tag should be used in the comment for a method declaration to describe one of the parameters for the method.

Compile with /doc to process documentation comments to a file.

Example

public class MyClass {
   /// <param name="Int1">Used to indicate status.</param>
   public static void MyMethod(int Int1) {
   }

   public static void Main () {
   }
}

See Also

Tags for Documentation Comments


<paramref>

<paramref name="name"/>

where:

name
The name of the parameter to refer to. Enclose the name in double quotation marks (" ").

Remarks

The <paramref> tag gives you a way to indicate that a word is a parameter. The XML file can be processed to format this parameter in some distinct way.

Compile with /doc to process documentation comments to a file.

Example

public class MyClass {
   /// <remarks>MyMethod is a method in the MyClass class.
   /// The <paramref name="Int1"/> parameter takes a number.
   /// </remarks>
   public static void MyMethod(int Int1) {
   }

   public static void Main () {
   }
}

See Also

Tags for Documentation Comments


<permission>

<permission cref="member">description</permission>

where:

cref="member"
A reference to a member or field that is available to be called from the current compilation environment. The compiler checks that the given code element exists and translates member to the canonical element name in the output XML. member must appear within double quotation marks (" ").
description
A description of the access to the member.

Remarks

The <permission> tag lets you document the access of a member. The System.Security.PermissionSet lets you specify access to a member.

Compile with /doc to process documentation comments to a file.

Example

using System;
class TestClass {
   /// <permission cref="System.Security.PermissionSet">Everyone can access this method.</permission>
   public static void Test() {
   }
   public static void Main() {
   }
}

See Also

Tags for Documentation Comments


<remarks>

<remarks>description</remarks>

where:

description
A description of the member.

Remarks

The <remarks> tag is where you should specify the bulk of the description for the member. <summary> is where you can specify a brief description.

Compile with /doc to process documentation comments to a file.

Example

public class MyClass {
   /// <remarks>MyMethod is a method in the MyClass class.
   /// <para>Here's how you could make a second paragraph in a description. <see cref="System.Console.WriteLine"/> for information about output statements.</para>
   /// <seealso cref="MyClass.Main"/>
   /// </remarks>
   public static void MyMethod(int Int1) {
   }

   public static void Main () {
   }
}

See Also

Tags for Documentation Comments


<returns>

<returns>description</returns>

where:

description
A description of the return value.

Remarks

The <returns> tag should be used in the comment for a method declaration to describe the return value.

Compile with /doc to process documentation comments to a file.

Example

public class MyClass {
   /// <returns>Returns zero.</returns>
   public static int GetZero() {
      return 0;
   }

   public static void Main () {
   }
}

See Also

Tags for Documentation Comments


<see>

<see cref="member"/>

where:

cref="member"
A reference to a member or field that is available to be called from the current compilation environment. The compiler checks that the given code element exists and passes member to the element name in the output XML. member must appear within double quotation marks (" ").

Remarks

The <see> tag lets you specify a link from within text. Use <seealso> to indicate text that you might want to appear in a See Also section.

Compile with /doc to process documentation comments to a file.

Example

See <remarks> for an example of using <see>.

See Also

Tags for Documentation Comments


<seealso>

<seealso cref="member"/>

where:

cref="member"
A reference to a member or field that is available to be called from the current compilation environment. The compiler checks that the given code element exists and passes member to the element name in the output XML. member must appear within double quotation marks (" ").

Remarks

The <seealso> tag lets you specify the text that you might want to appear in a See Also section. Use <see> to specify a link from within text.

Compile with /doc to process documentation comments to a file.

Example

See <remarks> for an example of using <seealso>.

See Also

Tags for Documentation Comments


<summary>

<summary>description</summary>

where:

description
A summary of the object.

Remarks

The <summary> tag should be used in the comment to provide a short description. Use <remarks> to indicate a longer description.

Compile with /doc to process documentation comments to a file.

Example

public class MyClass {
   /// <summary>MyMethod doesn't do very much.</summary>
   public static void MyMethod(int Int1) {
   }

   public static void Main () {
   }
}

See Also

Tags for Documentation Comments


<value>

<value>property description</value>

where:

property description
A description for the property.

Remarks

The <value> tag lets you describe a property.

Compile with /doc to process documentation comments to a file.

Example

using System;
public class Employee {
   private string name;
   /// <value>Name accesses the value of the name data member</value>
   public string Name {
      get {
         return name;
      }
      set {
         name = value;
      }
   }

}

public class MainClass {
   public static void Main() {
   }
}

See Also

Tags for Documentation Comments


Processing the XML File

The compiler generates an ID string for each construct in your code that is tagged to generate documentation. (For information on how to tag your code, see Tags for Documentation Comments.) The ID string uniquely identifies the construct. Programs that process the XML file can use the ID string to identify the corresponding NGWS metadata/reflection item that the documentation applies to.

The compiler observes the following rules when it generates the ID strings:

  • No whitespace is placed in the string.
  • The first part of the ID string identifies the kind of member being identified, via a single character followed by a colon. The following member types are used:
    Character | Description
    N | namespace
    T | type: class, interface, struct, enum, delegate
    F | field
    P | property (including indexers or other indexed properties)
    M | method (including such special methods as constructors, operators, and so on)
    E | event
    ! error string; the rest of the string provides information about the error. The C# compiler generates error information for links that cannot be resolved.
  • The second part of the string is the fully qualified name of the item, starting at the root of the namespace. The name of the item, its enclosing type(s), and namespace are separated by periods. If the name of the item itself has periods, they are replaced by the hash-sign ('#'). It is assumed that no item has an hash-sign directly in its name. For example, the fully qualified name of the String constructor would be "System.String.#ctor".
  • For properties and methods, if there are arguments to the method, the argument list enclosed in parentheses follows. If there are no arguments, no parentheses are present. The arguments are separated by commas. The encoding of each argument follows directly how it is encoded in a NGWS signature:
    • Base types. Regular types (ELEMENT_TYPE_CLASS or ELEMENT_TYPE_VALUETYPE) are represented as the fully qualified name of the type.
    • Intrinsic types (for example, ELEMENT_TYPE_I4, ELEMENT_TYPE_OBJECT, ELEMENT_TYPE_STRING, ELEMENT_TYPE_TYPEDBYREF) are represented as the fully qualified name of the corresponding full type. For example, System.Int32 or System.TypedReference.
    • ELEMENT_TYPE_VOID is represented as "=VOID".
    • ELEMENT_TYPE_PTR is represented as a '*' following the modified type.
    • ELEMENT_TYPE_BYREF is represented as a '@' following the modified type.
    • ELEMENT_TYPE_PINNED is represented as a '^' following the modified type. The C# compiler never generates this.
    • ELEMENT_TYPE_CMOD_REQ is represented as a '|' and the fully qualified name of the modifier class, following the modified type. The C# compiler never generates this.
    • ELEMENT_TYPE_CMOD_OPT is represented as a '!' and the fully qualified name of the modifier class, following the modified type.
    • ELEMENT_TYPE_SZARRAY is represented as "[]" following the element type of the array.
    • ELEMENT_TYPE_GENERICARRAY is represented as "[?]" following the element type of the array. The C# compiler never generates this.
    • ELEMENT_TYPE_ARRAY is represented as [lowerbound:size,lowerbound:size] where the number of commas is the rank - 1, and the lower bounds and size of each dimension, if known, are represented in decimal. If a lower bound or size is not specified, it is simply omitted. If the lower bound and size for a particular dimension are omitted, the ':' is omitted as well. For example, a 2-dimensional array with 1 as the lower bounds and unspecified sizes is [1:,1:].
    • ELEMENT_TYPE_FNPTR is represented as "=FUNC:type(signature)", where type is the return type, and signature is the arguments of the method. If there are no arguments, the parentheses are omitted. The C# compiler never generates this.

    The following signature components are not represented because they are never used for differentiating overloaded methods:

    • calling convention
    • return type
    • ELEMENT_TYPE_SENTINEL
  • For conversion operators only (op_Implicit and op_Explicit), the return value of the method is encoded as a '~' followed by the return type, as encoded above.

Examples

The following examples show how the ID strings for a class and its members would be generated:

namespace N  // "N:N"
{
class X    // "T:N.X"
{
    X();   // "M:N.X.#ctor"
    static X(); // "M:N.X.#cctor"  a class constructor
    X(int);  // "M:N.X.#ctor(System.Int32)"
    ~X();  // "M:N.X.Finalize"   destructor's representation in metadata
    string q;  // "F:N.X.q"
    const double PI = 3.14;  // "F:N.X.PI"
    int f();  // "M:N.X.f"
    int bb(string s, ref int y, void * z); // "M:N.X.bb(System.String,System.Int32@,=VOID*)"
    int gg(short[] array1, int[,] array); // "M:N.X.gg(System.Int16[], System.Int32[0:,0:])"
    X operator+(X, X); // "M:N.X.op_Addition(N.X,N.X)"
    int prop {get; set;} // "P:N.X.prop"
    event MyDelegate d; // "E:N.X.d"
    int this[string s]; // "P:N.X.Item(System.String)"
    class Nested{} // "T:N.X.Nested"
    delegate void D(int i); // "T:N.X.D"
    explicit operator int(X x); // "M:N.X.op_Explicit(N.X)~System.Int32"
}
}

See Also

/doc (Process Documentation Comments)


/fullpaths (Specify Absolute Path in Compiler Output)

/fullpaths

Remarks

By default, errors and warnings that result from compilation specify the name of the file in which an error was found. The /fullpaths option causes the compiler to specify the full path to the file.

See Also

C# Compiler Options


/help, /? (Compiler Command-Line Help)

/help
/?

Remarks

This option sends a listing of compiler options, and a brief description of each option, to stdout. If this option is included in a compilation, no output file will be created and no compilation will take place.

This option is not available from within the development environment; it is only available when compiling from the command line.

See Also

C# Compiler Options


/incremental (Enable Incremental Compilation)

/incremental[+ | -]

Remarks

The /incremental compiler option enables the incremental compiler, which compiles only those functions that changed since the last compilation. Information about the state of the previous compilation is stored in the following files, which are created by the compiler:

output_file_name.dbg
When compiling with /debug, the status of debug information is stored in the .pdb file.
output_file_name.extension.incr
Information about the status of the compilation, other than debug information, is stored in a .incr file.

The first time you use /incremental, the .incr and .pdb filesare updated and all subsequent compilations will be incremental. Changing the compiler options from the previous /incremental compilation will cause a full rebuild of the .incr and .pdb files.

If the compiler cannot find the project's .pdb or .incr files, a full rebuild will occur.

By default, /incremental- is in effect. Specifying /incremental is the same as specifying /incremental+.

Output files created with the /incremental option may be larger than those created with incremental compilation disabled. Because the output files can be larger, you should use /incremental- in the final build of your output file.

/incremental will speed compilation on projects with many, smaller files, and will have little or no speed improvement on projects with fewer, large files.

Example

Compile in.cs using the incremental compiler:

csc /incremental in.cs

See Also

C# Compiler Options


/linkresource (Link to NGWS Resource)

/linkresource:filename[,identifier[,mimetype]]

where:

filename
The NGWS resource file to which you want to link from the assembly.
identifier (optional)
The logical name for the resource; the name used to load the resource. The default is the name of the file name.
mimetype (optional)
A string representing the media type for the resource. The default is none.

Remarks

The /linkresource option creates a link to a NGWS resource in the output file; the resource file is not placed in the output file. /resource embeds a resource file in the output file.

/linkresource requires one of the /target options other than /target:module.

NGWS resources can be accessed with members in the System.Resources namespace.

/linkres is the short form of /linkresource.

Example

Compile in.cs and link to resource file rf.resource:

csc /linkresource:rf.resource in.cs

See Also

C# Compiler Options


/main (Specify Location of Main Method)

/main:class

where:

class
The type that contains the Main method.

Remarks

If your compilation includes more than one type with a Main method, you can specify which type contains the Main method that you want to use as the entry point into the program.

This option is for use when compiling a .exe.

Example

Compile t2.cs and t3.cs, specifying that the Main method will be found in Test2:

csc t2.sc t3.sc /main:Test2

See Also

C# Compiler Options


/nologo (Suppress Banner Information)

/nologo

Remarks

The /nologo option suppresses display of the sign-on banner when the compiler starts up and display of informational messages during compiling.

This option is not available from within the development environment; it is only available when compiling from the command line.

See Also

C# Compiler Options


/nooutput (Do Not Create Output File)

/nooutput

Remarks

The /nooutput option causes compilation to take place but for no output file to be created. This lets you see any compilation errors or warnings.

On the command line, specify /nooutput in front of the source code file names.

Example

Compile t2.cs but do not create an output file and compile t3.cs into t3.exe:

csc /nooutput t2.cs /out:t3.exe t3.cs

See Also

C# Compiler Options


/nostdlib (Do Not Import Standard Library)

/nostdlib[+ | -]

Remarks

The /nostdlib option prevents the import of mscorlib.dll, which defines the entire System namespace. Use this option if you want to define or create your own System namespace and objects.

If you do not specify /nostdlib, mscorlib.dll will be imported into your program (same as specifying /nostdlib-). If you specify /nostdlib, it is the same as specifying /nostdlib+.

See Also

C# Compiler Options


/nowarn (Suppress Specified Warnings)

/nowarn:number1[,number2[...]]

where:

number1
A warning number that you want the compiler to suppress.

Remarks

The /nowarn option lets you suppress the compiler’s ability to generate one or more warnings. Separate multiple warning numbers with a comma.

You only need to specify the numeric part of the warning identifier. For example, if you want to suppress CS0050, you could specify /nowarn:50.

See Also

C# Compiler Options


/optimize (Enable/disable Optimizations)

/optimize[+ | -]

Remarks

The /optimize option enables or disables optimizations performed by the compiler to make your output file smaller, faster, and more efficient. By default, /optimize is in effect. Specifying /optimize is the same as specifying /optimize+.

To prevent an optimized output file, specify /optimize-.

/o is the short form of /optimize.

It is possible to combine the /optimize and /debug options.

Example

Compile t2.sc and enable compiler optimizations:

csc t2.cs /optimize

See Also

C# Compiler Options


/out (Set Output Filename)

/out:filename

where:

filename
The name of the output file created by the compiler.

Remarks

The /out option specifies the name of the output file. The compiler expects to find one or more source code files following the /out option.

If you do not specify the name of the output file:

  • A .exe will take its name from the source code file that contains the Main method.
  • A .dll will take its name from the first source code file.

On the command line, it is possible to specify multiple output files for your compilation. All source code files specified after an /out option will be compiled into the output file specified by that /out option. A source code file used to compile one output file cannot be used in the same compilation for the compilation of another output file.

Specify the full name and extension of the file you want to create.

Example

Compile t2.cs and create output file t2.exe and build t3.cs and create output file t3.exe:

csc t2.cs /out:t3.exe t3.cs

See Also

C# Compiler Options


/recurse (Find Source Files in Subdirectories)

/recurse:[dir\]file

where:

dir (optional)
The directory in which you want the search to begin. If this is not specified, the search begins in the project directory.
file
The file(s) to search for. Wildcard characters are allowed.

Remarks

The /recurse option lets you compile source code files in all child directories of either the specified directory (dir) or of the project directory.

You can use wildcards in a file name to compile all matching files in the project directory without using /recurse.

Example

Compiles all C# files in the current directory:

csc *.cs

Compiles all of the C# files in the dir1\dir2 directory and any directories below it and generates dir2.dll:

csc /target:library /out:dir2.dll /recurse: dir1\dir2\*.cs

See Also

C# Compiler Options


/reference (Import Metadata)

/reference:file[;file2]

where:

file[;file2]
One or more files that contains an assembly manifest. To import more than one file, separate file names with either a comma or a semicolon. The compiler will look for file anywhere specified by the PATH environment variable.

Remarks

The /reference option causes the compiler to make public type information in the specified files available to the project you are currently compiling.

The file(s) you import must contain a manifest; the output file must have been compiled with one of the /target options other than /target:module.

Use /addmodule to import metadata from an output file that does not contain an assembly manifest.

/r is the short form of /reference.

Example

Compile source file input.cs and import metadata from metad1.dll and metad2.dll to produce out.exe:

csc /reference:metad1.dll;metad2.dll /out:out.exe input.cs

See Also

C# Compiler Options


/resource (Embed Resource File to Output)

/resource:filename[,identifier[,mimetype]]

where:

filename
The NGWS resource file you want to embed in the output file.
identifier (optional)
The logical name for the resource; the name used to load the resource. The default is the name of the file name.
mimetype (optional)
A string representing the media type for the resource. The default is none.

Remarks

The /resource option embeds a NGWS resource to the output file in an assembly. /linkresource links a resource to an assembly but does not place the resource file in the output file.

/resource requires one of the /target options other than /target:module.

NGWS resources can be accessed with members in the System.Resources namespace.

Example

Compile in.cs and attach resource file rf.resource:

csc /resource:rf.resource in.cs

See Also

C# Compiler Options


/target (Specify Output File Format)

The /target compiler option can be specified in one of four forms:

/target:exe
Create an .exe file.
/target:library
Create a code library.
/target:module
Create a module.
/target:winexe
Create a Windows program.

Unless you specify /target:module, /target causes a NGWS assembly manifest to be placed in an output file. See Global Attributes for information on how to specify assembly attributes.

The assembly manifest is placed in the first .exe output file in the compilation or in the first .dll, if there is no .exe output file. For example, in the following command line, the manifest will be placed in 1.exe:

csc /out:1.exe t1.cs /out:2.exe t2.cs

The compiler creates only one assembly manifest per compilation. Information about all files in a compilation is placed in the assembly manifest. All output files except those created with /target:module can contain an assembly manifest. When producing multiple output files at the command line, only one assembly manifest can be created and it must go into the first output file specified on the command line. If the first output file is an exe (/target:exe or /target:winexe) or code library (/target:library), then any other output files produced in the same compilation must be modules (/target:module).

The C# compiler can only produce .dlls or .exes.

See Also

C# Compiler Options


/target:exe (Create Console Application)

/target:exe

Remarks

The /target:exe option causes the compiler to create an executable (EXE), console application. The /target:exe option is in effect by default. The executable file will be created with the .exe extension.

Use /target:winexe to create a Windows program executable.

Unless otherwise specified with the /out option, the output file name takes the name of the input file that contains the Main method.

When specified at the command line, all files up to the next /out, /target:winexe, or /target:dll option are used to create the .exe. The /target:exe option is in effect for all files since the previous /out or /target:dll option.

One and only one Main method is required in the source code files that are compiled into a .exe. The /main compiler option lets you specify which class contains the Main method, in cases where your code has more than one class with a Main method.

Example

Each of the following command lines will compile in.cs, creating in.exe:

csc /target:exe in.cs
csc in.cs

See Also

/target (Specify Output File Format) | C# Compiler Options


/target:library (Create a Code Library)

/target:library

Remarks

The /target:library option causes the compiler to create a DLL rather than an executable file (EXE). The DLL will be created with the .dll extension.

Unless otherwise specified with the /out option, the output file name takes the name of the first input file.

When specified at the command line, all files up to the next /out, /target:winexe, or /target:exe option are used to create the .dll.

When building a .dll, a Main method is not required.

Example

Compile in.cs, creating in.dll:

csc /target:library in.cs

See Also

/target (Specify Output File Format) | C# Compiler Options


/target:module (Create Module to Add to Assembly)

/target:module

Remarks

To not generate an assembly manifest, use the /target:module option. The output file will have an extension of .dll.

A file that does not have an assembly cannot be loaded by the NGWS runtime. However, such a file can be incorporated into the assembly manifest of an assembly via /addmodule.

If more than one module is created in a single compilation, internal types in one module will be available to other modules in the compilation. When code in one module references internal types in another module, then both modules must be incorporated into an assembly manifest, via /addmodule.

Example

Compile in.cs, creating in.dll:

csc /target:module in.cs

See Also

/target (Specify Output File Format) | C# Compiler Options


/target:winexe (Create a Windows Program)

/target:winexe

Remarks

The /target:winexe option causes the compiler to create an executable (EXE), Windows program. The executable file will be created with the .exe extension. A Windows program is one that provides a user interface from either the NGWS framework library or with the Win32 APIs.

Use /target:exe to create a console application.

Unless otherwise specified with the /out option, the output file name takes the name of the input file that contains the Main method.

When specified at the command line, all files until the next /out or /target option are used to create the Windows program.

One and only one Main method is required in the source code files that are compiled into a .exe. The /main compiler option lets you specify which class contains the Main method, in cases where your code has more than one class with a Main method.

Example

Compile in.cs into a Windows program:

csc /target:winexe in.cs

See Also

/target (Specify Output File Format) | C# Compiler Options


/unsafe (Enable Unsafe Mode)

/unsafe

Remarks

The /unsafe option allows code that uses the unsafe keyword to compile.

Example

Compile in.cs for unsafe mode:

csc /unsafe in.cs

See Also

C# Compiler Options


/warn (Specify Warning Level)

/warn:option

where:

option
The minimum warning level you want displayed for the build. Valid values are 0-4:
Warning level | Meaning
0 | Turns off emission of all warning messages.
1 | Displays severe warning messages.
2 | Displays level 1 warnings plus certain, less-severe warnings, such as warnings about hiding class members.
3 | Displays level 2 warnings plus certain, less-severe warnings, such as warnings about expressions that always evaluate to true or false.
4 | Displays all level 3 warnings plus informational warnings. This is the default warning level at the command line.

Remarks

The /warn option specifies the warning level for the compiler to display.

The Build Errors documentation describes the warnings, indicates each warning's level, and indicates potential problems (rather than actual coding errors) with statements that may not compile as you intend.

Use /warnaserror to treat all warnings as errors.

/w is the short form of /warn.

Example

Compile in.cs and have the compiler only display level 1 warnings:

csc /warn:1 in.cs

See Also

C# Compiler Options


/warnaserror (Treat Warnings as Errors)

/warnaserror[+ | -]

Remarks

The /warnaserror+ option treats all warnings as errors. Any messages that would ordinarily be reported as warnings are instead reported as errors, and the build process is halted (no output files are built).

By default, /warnaserror- is in effect, which causes warnings to not prevent the generation of an output file. /warnaserror, which is the same as /warnaserror+, causes warnings to be treated as errors.

Use /warn to specify the level of warnings that you want the compiler to display.

Example

Compile in.cs and have the compiler display no warnings:

csc /warnaserror in.cs

See Also

C# Compiler Options


/win32icon (Import a .ico File)

/win32icon:filename

where:

filename
The .ico file that you want to add to your output file.

Remarks

The /win32icon option inserts a .ico file in the output file, which gives the output file the desired appearance in the Windows Explorer. A .ico file can be created with the Resource Compiler. The Resource Compiler is invoked when you compile a Visual C++ program; a .ico file is created from the .rc file.

See /linkresource (to reference) or /resource (to attach) a NGWS resource file. See /win32res to import a .res file.

Example

Compile in.cs and attach a .ico file rf.ico to produce in.exe:

csc /win32icon:rf.ico in.cs

See Also

C# Compiler Options


/win32res (Import a Win32 Resource File)

/win32res:filename

where:

filename
The resource file that you want to add to your output file.

Remarks

The /win32res option inserts a Win32 resource in the output file. A Win32 resource file can be created with the Resource Compiler. The Resource Compiler is invoked when you compile a Visual C++ program; a .res file is created from the .rc file.

A Win32 resource can contain version or bitmap (icon) information that would help identify your application in the Windows Explorer. If you do not specify /win32res, the compiler will generate version information based on the assembly version.

See /linkresource (to reference) or /resource (to attach) a NGWS resource file.

Example

Compile in.cs and attach a Win32 resource file rf.res to produce in.exe:

csc /win32res:rf.res in.cs

See Also

C# Compiler Options


C# Start Page

C#, pronounced C-sharp, is a new programming language introduced in Visual Studio 7.0. An evolution of C and C++, C# is simple, modern, type safe, and object oriented. C# was designed for building a wide range of enterprise applications that run on the Web Services and Windows DNA platforms. Code that you write with C# is compiled as managed code, which means it benefits from the services of the NGWS runtime. These services include language interoperability, garbage collection, enhanced security, and improved versioning support.

C# is fully supported within Visual Studio by project templates, designers and property pages, code assistants, an object model, and other features of the development environment. The framework library for C# programming is the NGWS Base Framework.

Visual Studio and the MSDN library include the following documentation to help you learn the C# language and write C# applications.

For information on | See
C# features, quick tour, and reasons for using C# | C# Language Tour
Sample programs | C# Samples
Tutorials | C# Tutorials
The C# compiler | C# Compiler Options
The syntax, semantics, and design of the language | C# Language Reference
C++ compared to C# | Comparison Between C++ and C#
Keywords, types, operators, attributes, and preprocessor directives | C# Programmer's Reference
Arrays, properties, indexers, and other language features | C# Language Features
Projects and the project model | Projects in the PDC Technology Preview
Code assistants |
Win Forms | Controls and Win Forms in the PDC Technology Preview
Web Forms | Web Forms in the PDC Technology Preview
Components | Component Programming in the PDC Technology Preview
Data access | Data in the PDC Technology Preview
Middle-tier programming | Framework Services Features in the PDC Technology Preview
Debugging | Debugging C#
Distributing a finished program | Deployment in the PDC Technology Preview
NGWS runtime and frameworks | See the NGWS SDK node in the Contents pane of the MSDN Library - PDC Tech Preview.

See Also

Welcome to the Visual C++ PDC Technology Preview | Visual C++ Start Page


C# Language Tour

This section provides a quick tour of the C# language. It also introduces a simple C# program and examines its structure and important features.

The following topics are discussed:

  • Quick Survey of C# Features
  • Why Use C#
  • The C# Version of Hello World!
    • Comments
    • The Main Method
    • Input and Output
    • Compilation and Execution
  • General Structure of a C# Program
  • Using Fully Qualified Names
  • Creating and Using DLLs
  • Comparison Between C++ and C#

See Also

Getting Started | Documentation Map


Quick Survey of C# Features

C# is a simple but powerful programming language intended for writing enterprise applications.

The C# language is an evolution of C and C++. It uses many C++ features in the areas of statements, expressions, and operators.

C# introduces considerable improvement and innovations in areas such as type safety, versioning, events, and garbage collection.

C# provides access to the common API styles: NGWS, COM, Automation, and C-style APIs. It also supports unsafe mode, where you can use pointers to manipulate memory that is not under the control of the garbage collector.

See Also

C# Language Tour


Why Use C#

C# is an elegant, simple, type-safe, object-oriented language that allows enterprise programmers to build a breadth of applications.

C# also gives you the capability to build durable system-level components by virtue of the following features:

  • Full COM/Platform support for existing code integration.
  • Robustness through garbage collection and type safety.
  • Security provided through intrinsic code trust mechanisms.
  • Full support of extensible metadata concepts.

You can also interoperate with other languages, across platforms, with legacy data, by virtue of the following features:

  • Full interoperability support through COM+ 1.0 and NGWS services with tight library-based access.
  • XML support for web-based component interaction.
  • Versionability to provide ease of administration and deployment.

See Also

C# Language Tour


The C# Version of Hello World!

The following console program is the C# version of the traditional "Hello World!" program, which displays the string Hello World!.

// A "Hello World!" program in C#
class Hello {
   public static void Main() {
      System.Console.WriteLine("Hello World!");
   }
}

The following are important points of this program.

Comments

The first line contains a comment:

// A "Hello World!" program in C#

The characters // convert the rest of the line to a comment. You can also comment a block of text by placing it between the characters /* and */, for example:

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

The Main Method

The C# program 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 public static method that resides inside a class or a struct. In the "Hello World!" example, it resides inside the Hello class. There are three ways to declare the Main method:

  • It can return void:
    public static void Main() {
...
}
  • It can also return an int:
    public static int Main() {
   ...
   return 0;
}
  • It can also take arguments:
    public static int Main(string[] args) {
   ...
   return 0;
}

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

For more information on using command-line arguments, see the example in Main and Creating and Using DLLs.

Input and Output

C# programs generally use the input/output services provided by run-time library of the NGWS Framework. The statement:

System.Console.WriteLine("Hello World!");

uses the WriteLine method, 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 used for different input and output operations. If you include the following using statement at the beginning of the program:

using System;

you can directly use the System classes and methods without fully qualifying them. For example:

Console.WriteLine("Hello World!");

For more information on input/output methods, see the Reference section of the NGWS SDK.

Compilation and Execution

You can compile the "Hello World!" program either by Creating a C# Client EXE Project in the Visual Studio IDE, or by using the command line.

To compile the program from the command line:

  • Create the source file using any text editor and save it using a name such as Hello.cs. C# source code files use the extension .cs.
  • To invoke the compiler, enter the command:
    csc Hello.cs

    If your program does not contain any compilation errors, a Hello.exe file will be created.

  • To run the program, enter the command:
    Hello

For more information on compilation options, see C# Compiler Options.

See Also

C# Language Tour


General Structure of a C# Program

C# programs can consist of one or more files. Each file can contain one 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.

// A skeleton of a C# program
using System;
namespace MyNamespace1 {
   class MyClass1 {
   }
   struct MyStruct {
   }
   interface IMyInterface {
   }
   delegate int MyDelegate();
   enum MyEnum {}
   namespace MyNamespace2 {
   }
   class MyClass2 {
      public static void Main(string[] args) {
      }
   }
}

See Also

C# Language Tour


Using Fully Qualified Names

Namespaces and types have unique names, which are described by fully qualified names that indicate a logical hierarchy. For example, A.B is the name of the namespace or type B nested in the namespace or type A.

In the following code segment, there are nested classes and namespaces. The fully qualified name is indicated as a comment following each entity.

namespace N1 {         // N1
   class C1 {          // N1.C1
      class C2 {}      // N1.C1.C2
   }
   namespace N2 {      // N1.N2
      class C2 {}      // N1.N2.C2
   }
}

In the preceding code segment:

  • The namespace N1 is a member of the global namespace. Its fully qualified name is N1.
  • The namespace N2 is a member of N1. Its fully qualified name is N1.N2.
  • The class C1 is a member of the N1. Its fully qualified name is N1.C1.
  • The class name C2 is used twice in this code. However, the fully qualified names are unique. The first one is declared inside C1, thus its fully qualified name is: N1.C1.C2. The second is declared inside a namespace N2; thus, its fully qualified name is N1.N2.C2.

Using the preceding code segment, you can add a new class member C3 to the namespace N1.N2 as follows:

namespace N1.N2 {
   class C3 {}        // N1.N2.C3
}

For more information on namespaces, see Namespaces.

See Also

C# Language Tour


Creating and Using C# DLLs

A dynamic linking library (DLL) is linked to your program at run time. To demonstrate building and using a DLL, consider the following scenario:

  • MyLibrary.DLL: The library file that contains the methods to be called at run time. In this example, the DLL contains two methods, Add and Multiply.
  • Add.cs: The source file that contains the method Add(long i, long j). It returns the sum of its parameters. The class AddClass that contains the method Add is a member of the namespace MyMethods.
  • Mult.cs: The source code that contains the method Multiply(long x, long y). It returns the product of its parameters. The class MultiplyClass that contains the method Multiply is also a member of the namespace MyMethods.
  • MyClient.cs: The file that contains the Main method. It uses the methods in the DLL file to calculate the sum and the product of the run-time arguments.

Source files

File: Add.cs

// Add two numbers
using System;
namespace MyMethods {
   public class AddClass {
      public static long Add(long i, long j) {
         return(i+j);
      }
   }
}

File: Mult.cs

// Multiply two numbers
using System;
namespace MyMethods {
   public class MultiplyClass {
      public static long Multiply(long x, long y) {
         return (x*y);
      }
   }
}

File: MyClient.cs

// Calling methods from a DLL file
using System;
using MyMethods;
class MyClient {
   public static void Main(string[] args) {
      Console.WriteLine("Calling methods from MyLibrary.DLL:");
      if (args.Length != 2) {
         Console.WriteLine("Usage: MyClient <num1> <num2>");
         return;
      }
      long num1 = long.Parse(args[0]);
      long num2 = long.Parse(args[1]);
      long sum = AddClass.Add(num1, num2);
      long product = MultiplyClass.Multiply(num1, num2);
      Console.WriteLine("The sum of {0} and {1} is {2}",
                   num1, num2, sum);
      Console.WriteLine("The product of {0} and {1} is {2}",
                  num1, num2, product);
   }
}

This file contains the algorithm that uses the DLL methods, Add and Multiply. It starts with parsing the arguments entered from the command line, num1 and num2. Then it calculates the sum by using the Add method on the AddClass class, and the product by using the Multiply method on the MultiplyClass class.

Notice that the using directive at the beginning of the file makes MyMethods namespace available to the file and, consequently, the unqualified class names can reference the DLL at compile time.

Compilation

To build the file MyLibrary.DLL, compile the two files Add.cs and Mult.cs using the following command line:

csc /target:library /out:MyLibrary.DLL Add.cs Mult.cs

The /target:library compiler option tells the compiler to output a DLL instead of an EXE file. The /out compiler option followed by a file name is used to specify the DLL file name. Otherwise, the compiler uses the first file (Add.cs) as the name of the DLL.

To build the executable file, MyClient.exe, use the following command line:

csc /out:MyClient.exe /reference:MyLibrary.DLL MyClient.cs

The /out compiler option tells the compiler to output an EXE file and specifies the name of the output file (MyClient.exe). This compiler option is optional. The /reference compiler option specifies the DLL file(s) that this program uses.

Execution

To run the program, enter the name of the EXE file, followed by two numbers, for example:

MyClient 1234 5678

Output

Calling methods from MyLibrary.DLL:
The sum of 1234 and 5678 is 6912
The product of 1234 and 5678 is 7006652

See Also

C# Language Tour


Comparison Between C++ and C#

The following table contains important comparisons between C++ and C# features. If you are a C++ programmer, this table will give you the most important differences between the two languages at a glance. For details, use the link to browse to the detailed C# document.

Feature | Refer to the topic
Inheritance: A class can inherit implementation from one base class only. Also a class or an interface can implement multiple interfaces. | class interface
Arrays: The syntax of declaring C# arrays is different from that of C++ arrays. The tokens "[]" appear following the array type in C#. | Arrays
The bool type: There is no conversion between the bool type and other types (specifically int). | bool
The struct type: In C#, classes and structs are semantically different. A struct is a value type, while a class is a reference type. | struct class
The switch statement: Unlike the C++ switch statement, C# does not support fall through from one case label to another. | switch
The delegate type: Delegates are roughly similar to function pointers in C++, but they are type-safe and secure. | delegate
Calling the overridden base class members from derived classes. | base See also the examples for override
Using the new modifier to explicitly hide an inherited member. | new
Declaring override methods requires the override keyword. | override
Preprocessor directives are used for conditional compilation. No header files are used in C#. | Preprocessor Directives
Exception handling: Using the finally statement. | try-finally try-catch-finally
C# operators: C# supports additional operators such as is and typeof. It also introduced different functionality of some logical operators. | & Operator | Operator ^ Operator is typeof
Use of the extern keyword. | extern
Use of the static keyword. | static
An alternative way to the C++ initialization list in constructing the base class. | See the examples for virtual
The general structure of the C# program: namespaces, classes, structs, delegates, and enumerations. | General structure of a C# program
The Main method is declared differently from the main function in C++. Also, compare the usage of the command-line arguments for each language. | Main
Method parameters: C# supports ref and out parameters, which are used instead of pointers in passing parameters by reference. | ref out
Pointers are allowed in C# but only in unsafe mode. | unsafe
Overloading operators is performed differently in C#. | C# Operators
Strings: C# strings are different from C++ strings. | string

For complete comparisons between C# and other programming languages, see the Language Equivalents documentation.

See Also

C# Language Tour


C# Reference

The C# documentation includes the following references:

Name | Description
C# Language Reference | The syntax, semantics, and design of the language
C# Programmer's Reference | Keywords, operators, preprocessor directives, and other language features

See Also

C# Compiler Options


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

C# Language Reference

Version: 0.17

Authors: Anders Hejlsberg and Scott Wiltamuth


1. Introduction

C# is a simple, modern, object oriented, and type-safe programming language derived from C and C++. C# (pronounced "C sharp") is firmly planted in the C and C++ family tree of languages, and 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++.

C# is provided as a part of Microsoft Visual Studio 7.0. In addition to C#, Visual Studio supports Visual Basic, Visual C++, and the scripting languages VBScript and JScript. All of these languages provide access to the Next Generation Windows Services (NWGS) platform, which includes a common execution engine and a rich class library. The NGWS software development kit defines a "Common Language Subset" (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 and Visual C++. C# itself does not include a class library.

The rest of this chapter describes the essential features of the language. While later chapters describe rules and exceptions in a detail-oriented and sometimes mathematical manner, this chapter strives for clarity and brevity at the expense of completeness. The intent is to provide the reader with an introduction to the language that will facilitate the writing of early programs and the reading of later chapters.


1.1 Hello, world

The canonical "Hello, world" program can be written in C# as follows:

using System;
class Hello
{
   static void Main() {
      Console.WriteLine("Hello, world");
   }
}

The default file extension for C# programs is .cs, as in hello.cs. Such a program can be compiled with the command line directive

csc hello.cs

which produces an executable program named hello.exe. The output of the program is:

Hello, world

Close examination of this program is illuminating:

  • The using System; directive references a namespace called System that is provided by the NGWS runtime. This namespace contains the Console class referred to in the Main method. Namespaces provide a hierarchical means of organizing the elements of a class library. A "using" directive enables unqualified use of the members of a namespace. The "Hello, world" program uses Console.WriteLine as a shorthand for System.Console.WriteLine. What do these identifiers denote? System is a namespace, Console is a class defined in that namespace, and WriteLine is a static method defined on that class.
  • The Main function is a static member of the class Hello. Functions and variables are not supported at the global level; such elements are always contained within type declarations (e.g., class and struct declarations).
  • The "Hello, world" output is produced through the use of a class library. C# does not itself provide a class library. Instead, C# uses a common class library that is also used by other languages such as Visual Basic and Visual C++.

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 either "::" or "->" operators. The "::" is not an operator in C# at all, and the "->" operator is used in only a small fraction of C# programs. C# programs use "." as a separator in compound names such as Console.WriteLine.
  • The program does not contain forward declarations. Forward declarations are never needed in C# programs, as declaration order is not significant.
  • The program does not use #include to import program text. Dependencies between programs are handled symbolically rather than with program text. This system eliminates barriers between programs written in different languages. For example, the Console class could be written in C# or in some other language.


1.2 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. C# provides automatic memory management so that developers are freed from this burdensome task. In the vast majority of cases, this automatic memory management increases code quality and enhances developer productivity without negatively impacting either expressiveness or performance.

The example

using System;
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);
      while (!s.Empty)
         Console.WriteLine(s.Pop());
   }
}

shows a test program 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.

For developers who are generally content with automatic memory management but sometimes need fine-grained control or that extra iota of performance, C# provides the ability to write "unsafe" code. Such code can deal directly with pointer types, and fix objects to temporarily prevent the garbage collector from moving them. This "unsafe" code feature is in fact "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 features accidentally, and the C# compiler and the execution engine work together to ensure that unsafe code cannot masquerade as safe code.

The example

using System;
class Test
{
   unsafe static void WriteLocations(byte[] arr) {
      fixed (byte *p_arr = arr) {
         byte *p_elem = p_arr;
         for (int i = 0; i < arr.Length; i++) {
            byte value = *p_elem;
            string addr = int.Format((int) p_elem, "X");
            Console.WriteLine("arr[{0}] at 0x{1} is {2}", i,  addr, value);
            p_elem++;
         }
      }
   }
   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 and write out the index, value, and location of each. One possible output of the program is:

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 are subject to change.


1.3 Types

C# supports two major 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

using System;
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 of the program 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 (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.

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

using System;
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 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");
   }
}
public delegate void EmptyDelegate();

shows an example or two for each kind of type declaration. Later sections describe type declarations in greater detail.


1.4 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 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 for developers 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();

is invalid 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 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 each of the predefined types, and provides examples of each.

Type | Description | Examples
object | The ultimate base type of all other types | object o = new Stack();
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;
byte val2 = 34U;
ushort 16-bit unsigned integral type ushort val1 = 12;
ushort val2 = 34U;
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 value = 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 value = true;
char | Character type; a char value is a Unicode character | char value = 'h';
decimal | Precise decimal type with 28 significant digits | decimal value = 1.23M;

Each of the predefined types is shorthand for a system-provided type. For example, the keyword int is shorthand for a struct named System.Int32. The two names can be used interchangeably, though it is considered good style to use the keyword rather than 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. The special treatment the these types includes literal support and efficient code generation. C#’s operator overloading feature enables developers to define types that behave like the predefined value types. For instance, a Digit struct that supports the same mathematical operations as the predefined integral types, and that conversion to and from these types.

using System;
struct Digit
{...}
class Test
{
   static void TestInt() {
      int a = 1;
      int b = 2;
      int c = a + b;
      Console.WriteLine(c);
   }
   static void TestDigit() {
      Digit a = (Digit) 1;
      Digit b = (Digit) 2;
      Digit c = a + b;
      Console.WriteLine(c);
   }
   static void Main() {
      TestInt();
      TestDigit();
   }
}


1.5 Array types

Arrays in C# may be single-dimensional or multi-dimensional. Both "rectangular" and "jagged" arrays are supported.

Single-dimensional arrays are the most common type, so this is a good starting point. The example

using System;
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 program output 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.

Arrays 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 length 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.

It is important to note that the element type and number of dimensions are part of an array’s type, but that the length of each dimension is not part of the 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.

It is important to note that 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 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. The example

class Test
{
   static void F(int[] arr) {}
   static void Main() {
      F({1, 2, 3});
   }
}

is not valid because the array initializer {1, 2, 3} is not a valid expression. The example can be rewritten to explicitly specify the type of array being created, as in

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


1.6 Type system unification

C# provides a "unified type system". All types – including value types – can be treated like objects. Conceptually speaking, all types derive from object, and so it is possible to call object methods on any value, even values of "primitive" types such as int. The example

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

calls the object-defined ToString method on a constant value of type int.

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, and does so without introducing unnecessary overhead. For programs that don’t need int values to act like object, int values are simply 32 bit values. For programs that need int’s to behave like objects, this functionality 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, the NGWS class library includes a Hashtable class that provides an Add method that takes a Key and a Value.

public class Hashtable
{
   public void Add(object Key, object Value) {...}
   ...
}

Because C# has a unified type system, the users of the Hashtable class can use keys and values of any type, including value types.


1.7 Statements

C# borrows most of its statements directly from C and C++, though there are some noteworthy additions and modifications.


1.7.1 Statement lists and blocks

A statement list consists of one or more statements written in sequence, and a block permits multiple statements to be written in contexts where a single statement is expected. For instance, the example

using System;
class Test
{
   static void Main() { // begin block 1
      Console.WriteLine("Test.Main");
      { // begin block 2
         Console.WriteLine("Nested block");
      }
   }
}

shows two blocks.


1.7.2 Labeled statements and goto statements

A labeled statement permits a statement to be prefixed by a label, and goto statements can be used to transfer control to a labeled statement.

The example

using System;
class Test
{
   static void Main() {
      goto H;
      W: Console.WriteLine("world");
      return;
      H: Console.Write("Hello, ");
      goto W;
   }
}

is a convoluted version of the "Hello, world" program. The first statement transfers control to the statement labeled H. The first part of the message is written and then the next statement transfers control to the statement labeled W. The rest of the message is written, and the method returns.


1.7.3 Local declarations of constants and variables

A local constant declaration declares one or more local constants, and a local variable declaration declares one or more local variables.

The example

class Test
{
   static void Main() {
      const int a = 1;
      const int b = 2, c = 3;
      int d;
      int e, f;
      int g = 4, h = 5;
      d = 4;
      e = 5;
      f = 6;
   }
}

shows a variety of local constant and variable declarations.


1.7.4 Expression statements

An expression statement evaluates a given expression. The value computed by the expression, if any, is discarded. Not all expressions are permitted as statements. In particular, expressions such as x + y and x == 1 that have no side effects, but merely compute a value (which will be discarded), are not permitted as statements.

The example

using System;
class Test
{
   static int F() {
      Console.WriteLine("Test.F");
      return 0;
   }
   static void Main() {
      F();
   }
}

shows an expression statement. The call to the function F made from Main constitutes an expression statement. The value that F returns is simply discarded.


1.7.5 The if statement

An if statement selects a statement for execution based on the value of a boolean expression. An if statement may optionally include an else clause that executes if the boolean expression is false.

The example

using System;
class Test
{
   static void Main(string[] args) {
      if (args.Length == 0)
         Console.WriteLine("No arguments were provided");
      else
         Console.WriteLine("Arguments were provided");
   }
}

shows a program that uses an if statement to write out two different messages depending on whether command-line arguments were provided or not.


1.7.6 The switch statement

A switch statement executes the statements that are associated with the value of a given expression, or a default of statements if no match exists.

The example

using System;
class Test
{
   static void Main(string[] args) {
      switch (args.Length) {
         case 0:
            Console.WriteLine("No arguments were provided");
            break;
         case 1:
            Console.WriteLine("One arguments was provided");
            break;
         default:
            Console.WriteLine("{0} arguments were provided");
            break;
      }
   }
}

switches on the number of arguments provided.


1.7.7 The while statement

A while statement conditionally executes a statement zero or more times – as long as a boolean test is true.

using System;
class Test
{
   static int Find(int value, int[] arr) {
      int i = 0;
      while (arr[i] != value) {
         if (++i > arr.Length)
            throw new ArgumentException();
      }
      return i;
   }
   static void Main() {
      Console.WriteLine(Find(3, new int[] {5, 4, 3, 2, 1}));
   }
}

uses a while statement to find the first occurrence of a value in an array.


1.7.8 The do statement

A do statement conditionally executes a statement one or more times.

The example

using System;
class Test
{
   static void Main() {
      string s;
      do {
         s = Console.ReadLine();
      }
      while (!string.Equals(s, "Exit"));
   }
}

reads from the console until the user types "Exit" and presses the enter key.


1.7.9 The for statement

A for statement evaluates a sequence of initialization expressions and then, while a condition is true, repeatedly executes a statement and evaluates a sequence of iteration expressions.

The example

using System;
class Test
{
   static void Main() {
      for (int i = 0; i < 10; i++)
         Console.WriteLine(i);
   }
}

uses a for statement to write out the integer values 1 through 10.


1.7.10 The foreach statement

A foreach statement enumerates the elements of a collection, executing a statement for each element of the collection.

The example

using System;
using System.Collections;
class Test
{
   static void WriteList(ArrayList list) {
      foreach (object o in list)
         Console.WriteLine(o);
   }
   static void Main() {
      ArrayList list = new ArrayList();
      for (int i = 0; i < 10; i++)
         list.Add(i);
      WriteList(list);
   }
}

uses a foreach statement to iterate over the elements of a list.


1.7.11 The break statement and the continue statement

A break statement exits the nearest enclosing switch, while, do, for, or foreach statement; a continue starts a new iteration of the nearest enclosing while, do, for, or foreach statement.


1.7.12 The return statement

A return statement returns control to the caller of the member in which the return statement appears. A return statement with no expression can be used only in a member that does not return a value (e.g., a method that returns void). A return statement with an expression can only be used only in a function member that returns an expression.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

1.7.13 The throw statement

The throw statement throws an exception.


1.7.14 The try statement

The try statement provides a mechanism for catching exceptions that occur during execution of a block. The try statement furthermore provides the ability to specify a block of code that is always executed when control leaves the try statement.


1.7.15 The checked and unchecked statements

The checked and unchecked statements are used to control the overflow checking context for arithmetic operations and conversions involving integral types. The checked statement causes all expressions to be evaluated in a checked context, and the unchecked statement causes all expressions to be evaluated in an unchecked context.


1.7.16 The lock statement

The lock statement obtains the mutual-exclusion lock for a given object, executes a statement, and then releases the lock.


1.8 Classes

Class declarations are used to define new reference types. C# supports single inheritance only, but a class may implement multiple interfaces.

Class members can include constants, fields, methods, properties, indexers, events, operators, constructors, destructors, and nested type declaration.

Each member of a class has a form of accessibility. There are five forms of accessibility:

  • public members are available to all code;
  • protected members are accessible only from derived classes;
  • internal members are accessible only from within the same assembly;
  • protected internal members are accessible only from derived classes within the same assembly;
  • private members are accessible only from the class itself.


1.9 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 either "on the stack" or "in-line". Careful programmers can 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 allocations. The program below creates and initializes an array of 100 points. With Point implemented as a class, the program instantiates 101 separate objects – one for the array and one each for the 100 elements.

class Point
{
   public int x, y;
   public Point() {
      x = 0;
      y = 0;
   }
   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 the test program instantiates just one object, for the array. The Point instances are allocated in-line within the array. Of course, this optimization can be mis-used. Using structs instead of classes can also make your programs fatter and slower, as the overhead of passing a struct instance by value is slower than passing an object instance by reference. There is no substitute for careful data structure and algorithm design.


1.10 Interfaces

Interfaces are used to define a contract; a class or struct that implements the interface must adhere to this contract. Interfaces can contain methods, properties, indexers, and events as members.

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, Event 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 below, the interface IComboBox inherits from both ITextBox and IListBox.

interface IControl
{
   void Paint();
}
interface ITextBox: IControl
{
   void SetText(string text);
}
interface IListBox: IControl
{
   void SetItems(string[] items);
}
interface IComboBox: ITextBox, IListBox {}

Classes and structs can implement multiple interfaces. In the example below, the class EditBox derives from the class Control and implements both IControl and IDataBound.

interface IDataBound
{
   void Bind(Binder b);
}
public class EditBox: Control, IControl, IDataBound
{
   public void Paint();
   public void Bind(Binder b) {...}
}

In the example above, 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 by 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 member implementations" because each method explicitly designates the interface method being implemented.

Explicit interface methods 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: EditBox does not have a Paint method
      IControl control = editbox;
      control.Paint();   // calls EditBox’s implementation of Paint
   }
}


1.11 Delegates

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

Delegates are reference types that derive from a common base class: System.Delegate. A delegate instance encapsulates a method – a callable entity. For instance methods, a callable entity consists of an instance and a method on the instance. If you have a delegate instance and an appropriate set of arguments, you can invoke the delegate with the arguments. Similarly, for static methods, a callable entity consists of a class and a static method on the class.

An interesting and useful property of a delegate is that it does not know or care about the class of the object that it references. Any object will do; all that matters is that the method’s signature matches the delegate’s. 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. A delegate that takes no arguments and returns void can be declared with

delegate void SimpleDelegate();

A delegate instance can be instantiated using the new keyword, and referencing either an instance or class method that conforms to the signature specified by the delegate. Once a delegate has been instantiated, it can be called using method call syntax. In the example

class Test
{
   static void F() {
      System.Console.WriteLine("Test.F");
   }
   static void Main() {
      SimpleDelegate d = new SimpleDelegate(F);
      d();
   }
}

a SimpleDelegate instance is created and then immediately invoked.

Of course, there is not much point in instantiating a delegate for a method and then immediately calling via the delegate, as it would be simpler to call the method directly. Delegates show their usefulness when their anonymity is used. For example, we could define a MultiCall method that can call repeatedly call a SimpleDelegate.

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


1.12 Enums

An enum type declaration defines a type name for a related group of symbolic constants. Enums are typically used when for "multiple choice" scenarios, in which a runtime decision is made from a number of options 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.


1.13 Namespaces

C# programs are organized using namespaces. 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.

Earlier, we presented a "Hello, world" program. We’ll now rewrite this program in two pieces: a HelloMessage component that provides messages and a console application that displays messages.

First, we’ll provide a HelloMessage class in a namespace. What should we call this namespace? By convention, developers put all of their classes in a namespace that represents their company or organization. We’ll put our class in a namespace named Microsoft.CSharp.Introduction.

namespace Microsoft.CSharp.Introduction
{
   public class HelloMessage
   {
      public string GetMessage() {
         return "Hello, world";
      }
   }
}

Namespaces are hierarchical, and the name Microsoft.CSharp.Introduction is actually shorthand for defining a namespace named Microsoft that contains a namespace named CSharp that itself contains a namespace named Introduction, as in:

namespace Microsoft
{
   namespace CSharp
   {
      namespace Introduction
      {....}
   }
}

Next, we’ll write a console application that uses the HelloMessage class. We could just use the fully qualified name for the class – Microsoft.CSharp.Introduction.HelloMessage – but this name is quite long and unwieldy. An easier way is to use a "using" directive, which makes it possible to use all of the types in a namespace without qualification.

using Microsoft.CSharp.Introduction;
class Hello
{
   static void Main() {
      HelloMessage m = new HelloMessage();
      System.Console.WriteLine(m.GetMessage());
   }
}

Note that the two occurrences of HelloMessage are shorthand for Microsoft.CSharp.Introduction.HelloMessage.

C# also enables the definition and use of aliases. Such aliases can be useful in situation in which name collisions occur between two libraries, or when a small number of types from a much larger namespace are being used. Our example can be rewritten using aliases as:

using MessageSource = Microsoft.CSharp.Introduction.HelloMessage;
class Hello
{
   static void Main() {
      MessageSource m = new MessageSource();
      System.Console.WriteLine(m.GetMessage());
   }
}


1.14 Properties

A property is a named attribute associated with 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 execute in order to read or write their values. Properties thus provide a mechanism for associating actions with the reading and writing of an object’s attributes, and they furthermore permit such attributes to be computed.

The success of rapid application development tools like Visual Basic can, to some extent, be attributed to the inclusion of properties as a first-class element. VB developers can think of a property as being field-like, and this allows them to focus on their own application logic rather than on the details of a component they happen to be using. On the face of it, this difference might not seem like a big deal, but modern component-oriented programs tend to be chockfull of property reads and writes. Languages with method-like usage of properties (e.g., o.SetValue(o.GetValue() + 1);) are clearly at a disadvantage compared to languages that feature field-like usage of properties (e.g., o.Value++;).

Properties are defined in C# using property declaration syntax. The first part of the syntax 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: Control
{
   private string caption;
   public string Caption {
      get {
         return caption;
      }
      set {
         caption = value;
         Repaint();
      }
   }
}

Properties that can be both read and written, like the Caption property, 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 given in an implicit value parameter.

Declaration of properties is relatively straightforward, but the true value of properties shows itself is in their usage rather than in their declaration. The Caption property can read and written in the same way that fields can be read and written:

Button b = new Button();
b.Caption = "ABC";      // set
string s = b.Caption;   // get
b.Caption += "DEF";      // get & set


1.15 Indexers

If properties in C# can be likened to "smart fields", then indexers can be likened to "smart arrays". Whereas properties enable field-like access, indexers enable array-like access.

As an example, consider a ListBox control, which displays strings. This class wants to expose an array-like data structure that exposes the list of strings it contains, but also wants to be able to automatically update its contents when a value is altered. These goals can be accomplished by providing an indexer. The syntax for an indexer declaration is similar to that of a property declaration, with the main differences being that indexers are nameless (the "name" used in the declaration is this, since this is being indexed) and that additional indexing parameters are provided between square brackets.

public class ListBox: Control
{
   private string[] items;
   public string this[int index] {
      get {
         return items[index];
      }
      set {
         items[index] = value;
         Repaint();
      }
   }
}

As with properties, the convenience of indexers is best shown by looking at use rather than declaration. The ListBox class can be used as follows:

ListBox listBox = ...;
listBox[0] = "hello";
Console.WriteLine(listBox[0]);


1.16 Events

Events permit a class to declare notifications for which clients can attach executable code in the form of event handlers. Events are an important aspect of the design of class libraries in general, and of the system-provided class library in particular. C# provides an integrated solution for events.

A class defines an event by providing an event declaration, which looks quite similar to a field or event declaration but with an added event keyword. The type of this declaration must be a delegate type. In the example below, the Button class defines a Click event of type EventHandler.

public delegate void EventHandler(object sender, Event e);
public class Button: Control
{
   public event EventHandler Click;
   public void Reset() {
      Click = null;
   }
}

Inside the Button class, the Click member can be corresponds exactly to 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. This restricts client code to adding or removing an event handler. In the client code example below, the Form1 class adds Button1_Click as an event handler for Button1’s Click event. In the Disconnect method, the event handler is removed.

using System;
public class Form1: Form
{
   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, Event e) {
      Console.WriteLine("Button1 was clicked!");
   }
   public void Disconnect() {
      Button1.Click -= new EventHandler(Button1_Click);
   }
}

The Button class could be rewritten to use a property-like event declaration rather than a field-like event declaration. This change has no effect on client code.

public class Button: Control
{
      public event EventHandler Click {
         get {...}
         set {...}
      }
      public void Reset() {
         Click = null;
      }
}


1.17 Versioning

Versioning is an after-thought in most languages, but not in C#.

"Versioning" actually has two different meanings. 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, for a "binary compatible" component, a program 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 some client code.

As an example, consider the situation of a base class author who ships a class named Base. In this 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 that it depends on, is released to customers, who deploy to numerous clients and servers.

// Author A
namespace A
{
   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
{
   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 is 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 does Derived’s F adhere to the contract specified by Base? This seems even more unlikely, since it is pretty darn difficult for Derived’s F to adhere to a contract that didn’t exist when it was written. 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 since it cannot call a method that does not yet exist.

In practice, will name collisions of this kind actually occur? Let’s consider the factors involved. First, it is important to note that the authors are working completely independently – possibly in separate corporations – so no collaboration is possible. Second, there may be many derived classes. If there are more derived classes, then name collisions are more likely to occur. Imagine that the base class is Form, and that all VB, VC++ and C# developers are creating derived classes – that’s a lot of derived classes. Finally, name collisions are more likely if the base class is in a specific domain, as authors of both a base class and its derived classes are likely to choose names from this domain.

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.

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

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 C# compiler produces a warning, and by default makes Derived’s F hide Base’s F – duplicating the semantics for the case in which Derived is not recompiled. This warning 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
{
   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, and clearly specify this intent through specification of the override keyword, as shown below.

// Author A
namespace A
{
   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.


1.18 Attributes

C# is a procedural language, but like all procedural languages it does have some declarative elements. For example, the accessibility of a method in a class is specified by decorating 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, specify this declarative information for 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 to provide a mapping from program elements to documentation for them. The example

[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute: System.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 constructors of the attribute class; named parameters are defined by public read-write properties of the attribute class. The square brackets in the example indicate the use of an attribute in defining the Help attribute. In this case, the AttributeUsage attribute indicates that any program element can be decorated with the Help attribute.

The example

[Help("http://www.mycompany.com/…/Class1.htm")]
public class Class1
{
   [Help("http://www.mycompany.com/…/Class1.htm", 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 the NGWS runtime’s reflection support. The example

using System;
class Test
{
   static void Main() {
      Type type = typeof(Class1);
      object[] arr = type.GetCustomAttributes(typeof(HelpAttribute));
      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.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

2. Lexical structure

© 2000 Microsoft Corporation. All rights reserved.


2.1 Phases of translation

A C# program consists of one or more source files. A source file is an ordered sequence of Unicode characters. Source files typically have a one-to-one correspondence with files in a file system, but this correspondence is not required by C#.

Conceptually speaking, a program is compiled using four steps:

  • Pre-processing, a text-to-text translation that enables conditional inclusion and exclusion of program text.
  • Lexical analysis, which translates a stream of input characters into a stream of tokens.
  • Syntactic analysis, which translates the stream of tokens into executable code.


2.2 Grammar notation

Lexical and syntactic grammars for C# are interspersed throughout this specification. The lexical grammar defines how characters can be combined to form tokens; the syntactic grammar defines how tokens can be combined to form C# programs.

Grammar productions include non-terminal symbols and terminal symbols. In grammar productions, non-terminal symbols are shown in italic type, and terminal symbols are shown in a fixed-width font. Each non-terminal is defined by a set of productions. The first line of a set of productions is the name of the non-terminal, followed by a colon. Each successive indented line contains the right-hand side for a production that has the non-terminal symbol as the left-hand side. The example:

nonsense:
terminal1
terminal2

defines the nonsense non-terminal as having two productions, one with terminal1 on the right-hand side and one with terminal2 on the right-hand side.

Alternatives are normally listed on separate lines, though in cases where there are many alternatives, the phrase "one of" precedes a list of the options. This is simply shorthand for listing each of the alternatives on a separate line. The example:

letter: one of
A B C a b c

is shorthand for:

letter: one of
A
B
C
a
b
c

A subscripted suffix "opt", as in identifieropt, is used as shorthand to indicate an optional symbol. The example:

whole:
first-part second-partopt last-part

is shorthand for:

whole:
first-part last-part
first-part second-part last-part


2.3 Pre-processing

C# enables conditional inclusion and exclusion of code through pre-processing.

pp-unit:
pp-groupopt
pp-group:
pp-group-part
pp-group pp-group-part
pp-group-part:
pp-tokensopt new-line
pp-declaration
pp-if-section
pp-control-line
pp-line-number
pp-tokens:
pp-token
pp-tokens pp-token
pp-token:
identifier
keyword
literal
operator-or-punctuator
new-line:
The carriage return character (U+000D)
The line feed character (U+000A)
The carriage return character followed by a line feed character
The line separator character (U+2028)
The paragraph separator character (U+2029)


2.3.1 Pre-processing declarations

Names can be defined and undefined for use in pre-processing. A #define defines an identifier. A #undef "undefines" an identifier – if the identifier was defined earlier then it becomes undefined. If an identifier is defined then it is semantically equivalent to true; if an identifier is undefined then it is semantically equivalent to false.

pp-declaration:
#define pp-identifier
#undef pp-identifier

The example:

#define A
#undef B
class C
{
#if A
   void F() {}
#else
   void G() {}
#endif
#if B
   void H() {}
#else
   void I() {}
#endif
}

becomes:

class C
{
   void F() {}
   void I() {}
}

Within a pp-unit, declarations must precede pp-token elements. In other words, #define and #undef must precede any "real code" in the file, or a compile-time error occurs. Thus, it is possible to intersperse #if and #define as in the example below:

#define A
#if A
   #define B
#endif
namespace N
{
   #if B
   class Class1 {}
   #endif
}

The following example is illegal because a #define follows real code:

#define A
namespace N
{
   #define B
   #if B
   class Class1 {}
   #endif
}

A #undef may "undefine" a name that is not defined. The example below defines a name and then undefines it twice; the second #undef has no effect but is still legal.

#define A
#undef A
#undef A


2.3.2 #if, #elif, #else, #endif

A pp-if-section is used to conditionally include or exclude portions of program text.

pp-if-section:
pp-if-group pp-elif-groupsopt pp-else-groupopt pp-endif-line
pp-if-group:
#if pp-expression new-line pp-groupopt
pp-elif-groups
pp-elif-group
pp-elif-groups pp-elif-group
pp-elif-group:
#elif pp-expression new-line groupopt
pp-else-group:
#else new-line groupopt
pp-endif-line
#endif new-line

The example:

#define Debug
class Class1
{
#if Debug
   void Trace(string s) {}
#endif
}

becomes:

class Class1
{
   void Trace(string s) {}
}

If sections can nest. Example:

#define Debug      // Debugging on
#undef Trace      // Tracing off
class PurchaseTransaction
{
   void Commit {
      #if Debug
         CheckConsistency();
         #if Trace
            WriteToLog(this.ToString());
         #endif
      #endif
      CommitHelper();
   }
}


2.3.3 Pre-processing control lines

The #error and #warning features enable code to report warning and error conditions to the compiler for integration with standard compile-time warnings and errors.

pp-control-line:
#error pp-message
#warning pp-message
pp-message:
pp-tokensopt

The example

#warning Code review needed before check-in
#define DEBUG
#if DEBUG && RETAIL
   #error A build can't be both debug and retail!
#endif
class Class1
{…}

always produces a warning ("Code review needed before check-in"), and produces an error if the pre-processing identifiers DEBUG and RETAIL are both defined.


2.3.4 #line

The #line feature enables a developer to alter the line number and source file names that are used by the compiler in output such as warnings and errors. If no line directives are present then the line number and file name are determined automatically by the compiler. The #line directive is most commonly used in meta-programming tools that generate C# source code from some other text input.

pp-line-number:
#line integer-literal
#line integer-literal string-literal
pp-integer-literal:
decimal-digit
decimal-digits decimal-digit
pp-string-literal:
" pp-string-literal-characters "
pp-string-literal-characters:
pp-string-literal-character
pp-string-literal-characters pp-string-literal-character


2.3.5 pp-string-literal-character: Any character except " (U+0022), and white-spacePre-processing identifiers

NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

2.3.5 pp-string-literal-character:
Any character except " (U+0022), and white-spacePre-processing identifiers

Pre-processing identifiers employ a grammar similar to the grammar used for regular C# identifiers:

pp-identifier:
pp-available-identifier
pp-available-identifier:
A pp-identifier-or-keyword that is not true or false
pp-identifier-or-keyword:
identifier-start-character identifier-part-charactersopt

The symbols true and false are not legal pre-processing identifiers, and so cannot be defined with #define or undefined with #undef.


2.3.6 Pre-processing expressions

The operators !, ==, !=, && and || are permitted in pre-processing expressions. Parentheses can be used for grouping in pre-processing expressions.

pp-expression:
pp-equality-expression
pp-primary-expression:
true
false
pp-identifier
( pp-expression )
pp-unary-expression:
pp-primary-expression
! pp-unary-expression
pp-equality-expression:
pp-equality-expression == pp-logical-and-expression
pp-equality-expression != pp-logical-and-expression
pp-logical-and-expression:
pp-unary-expression
pp-logical-and-expression && pp-unary-expression
pp-logical-or-expression:
pp-logical-and-expression
pp-logical-or-expression || pp-logical-and-expression


2.3.7 Interaction with white space

Conditional compilation directives must be the first non-white space for a line.

A single-line comment may follow on the same line as conditional-compilation directives other than pp-control-line directives. For example,

#define Debug         // Defined if the build is a debug build

For pp-control-line directives, the remainder of the line constitutes the pp-message, independent of the contents of the line. The example

#warning             // TODO: Add a better warning

results in a warning with the contents "// TODO: Add a better warning".

A multi-line comment may not begin or end on the same line as a conditional compilation directive. The example

/* This comment is illegal because it
ends on the same line*/ #define Debug
/* This is comment is illegal because it is on the same line */ #define Retail
#define A /* This is comment is illegal because it is on the same line */
#define B /* This comment is illegal because it starts
on the same line */

results in a compile-time error.

Text that otherwise might form a conditional compilation directive can be hidden in a comment. The example

// This entire line is a commment.   #define Debug
/* This text would be a cc directive but it is commented out:
      #define Retail
*/

contains no conditional compilation directives, and consists entirely of white space.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

2.4 Lexical analysis

© 2000 Microsoft Corporation. All rights reserved.


2.4.1 Input

input:
input-elementsopt
input-elements:
input-element
input-elements input-element
input-element:
comment
white-space
token


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

2.4.2 Input characters

input-character:
any Unicode character


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

2.4.3 Line terminators

line-terminator:
TBD


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

2.4.4 Comments

comment:
TBD

Example:

// This is a comment
int i;
/* This is a
   multiline comment */
int j;


2.4.5 White space

white-space:
new-line
The tab character (U+0009)
The vertical tab character (U+000B)
The form feed character (U+000C)
The "control-Z" or "substitute" character (U+001A)
All characters with Unicode class "Zs"


2.4.6 Tokens

There are five kinds of tokens: identifiers, keywords, literals, operators, and punctuators. White space, in its various forms (described below), is ignored, though it may act as a separator for tokens.

token:
identifier
keyword
literal
operator-or-punctuator


2.5 Processing of Unicode character escape sequences

A Unicode character escape sequence represents a Unicode character. Unicode character escape sequences are permitted in identifiers, string literals, and character literals.

unicode-character-escape-sequence:
\u hex-digit hex-digit hex-digit hex-digit

Multiple translations are not performed. For instance, the string literal "\u005Cu005C" is equivalent to "\u005C" rather than "\\". (The Unicode value \u005C is the character "\".)

The example

class Class1
{
   static void Test(bool \u0066) {
      char c = '\u0066';
      if (\u0066)
         Console.WriteLine(c.ToString());
   }
}

shows several uses of \u0066, which is the character escape sequence for the letter "f". The program is equivalent to

class Class1
{
   static void Test(bool f) {
      char c = 'f';
      if (f)
         Console.WriteLine(c.ToString());
   }
}


2.5.1 Identifiers

These identifier rules exactly correspond to those recommended by the Unicode 2.1 standard except that underscore and similar characters are allowed as initial characters, formatting characters (class Cf) are not allowed in identifiers, and Unicode escape characters are permitted in identifiers.

identifier:
available-identifier
@ identifier-or-keyword
available-identifier:
An identifier-or-keyword that is not a keyword
identifier-or-keyword:
identifier-start-character identifier-part-charactersopt
identifier-start-character:
letter-character
underscore-character
identifier-part-characters:
identifier-part-character
identifier-part-characters identifier-part-character
identifier-part-character:
letter-character
combining-character
decimal-digit-character
underscore-character
letter-character:
A Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl
A unicode-character-escape-sequence representing a character of classes Lu, Ll, Lt, Lm, Lo, or Nl
combining-character:
A Unicode character of classes Mn or Mc
A unicode-character-escape-sequence representing a character of classes Mn or Mcdecimal-digit-character:
A Unicode character of the class Nd
A unicode-character-escape-sequence representing a character of the class Nd
underscore-character:
A Unicode character of the class Pc
A unicode-character-escape-sequence representing a character of the class Pc

Examples of legal identifiers include "identifier1", "_identifier2", and "@if".

The prefix "@" enables the use of keywords as identifiers. The character @ is not actually part of the identifier, and so might be seen in other languages as a normal identifier, without the prefix. Use of the @ prefix for identifiers that are not keywords is permitted, but strongly discouraged as a matter of style.

The example:

class @class
{
   static void @static(bool @bool) {
      if (@bool)
         Console.WriteLine("true");
      else
         Console.WriteLine("false");
   }
}
class Class1
{
   static void M {
      @class.@static(true);
   }
}

defines a class named "class" with a static method named "static" that takes a parameter named "bool".


2.5.2 Keywords

keyword: one of
abstract base bool break byte
case catch char checked class
const continue decimal default delegate
do double else enum event
explicit extern false finally fixed
float for foreach goto if
implicit in int interface internal
is lock long namespace new
null object operator out override
params private protected public readonly
ref return sbyte sealed short
sizeof static string struct switch
this throw true try typeof
uint ulong unchecked unsafe ushort
using virtual void while


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

2.5.3 Literals

literal:
boolean-literal
integer-literal
real-literal
character-literal
string-literal
null-literal


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

2.5.3.1 Boolean literals

There are two boolean literal values: true and false.

boolean-literal:
true
false


2.5.3.2 Integer literals

Integer literals have two possible forms: decimal and hexadecimal.

integer-literal:
decimal-integer-literal
hexadecimal-integer-literal
decimal-integer-literal:
decimal-digits integer-type-suffixopt
decimal-digits:
decimal-digit
decimal-digits decimal-digit
decimal-digit: one of
0 1 2 3 4 5 6 7 8 9
integer-type-suffix: one of
U u L l UL Ul uL ul LU Lu lU lu
hexadecimal-integer-literal:
0x hex-digits integer-type-suffixopt
hex-digits:
hex-digit
hex-digits hex-digit
hex-digit: one of
0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f

The type of an integer literal is determined as follows:

  • If the literal has no suffix, it has the first of these types in which its value can be represented: int, uint, long, ulong.
  • If the literal is suffixed by U or u, it has the first of these types in which its value can be represented: uint, ulong.
  • If the literal is suffixed by L or l, it has the first of these types in which its value can be represented: long, ulong.
  • If the literal is suffixed by UL, Ul, uL, ul, LU, Lu, lU, or lu, it is of type ulong.

If the value represented by an integer literal is outside the range of the ulong type, an error occurs.

To permit the smallest possible int and long values to be written as decimal integer literals, the following two rules exist:

  • When a decimal-integer-literal with the value 2147483648 (231) and no integer-type-suffix appears as the operand of the unary - operator (§7.6.2), the result is a constant of type int with the value -2147483648 (-231). In all other situations, such a decimal-integer-literal is of type uint.
  • When a decimal-integer-literal with the value 9223372036854775808 (263) and no integer-type-suffix or the integer-type-suffix L or l appears as the operand of the unary - operator (§7.6.2), the result is a constant of type long with the value -9223372036854775808 (-263). In all other situations, such a decimal-integer-literal is of type ulong.


2.5.3.3 Real literals

real-literal:
decimal-digits . decimal-digits exponent-partopt real-type-suffixopt
. decimal-digits exponent-partopt real-type-suffixopt
decimal-digits exponent-part real-type-suffixopt
decimal-digits real-type-suffix
exponent-part:
e signopt decimal-digits
E signopt decimal-digits
sign: one of
+ -
real-type-suffix: one of
F f D d M m

If no real type suffix is specified, the type of the real literal is double. Otherwise, the real type suffix determines the type of the real literal, as follows:

  • A real literal suffixed by F or f is of type float. For example, the literals 1f, 1.5f, 1e10f, and -123.456F are all of type float.
  • A real literal suffixed by D or d is of type double. For example, the literals 1d, 1.5d, 1e10d, and -123.456D are all of type double.
  • A real literal suffixed by M or m is of type decimal. For example, the literals 1m, 1.5m, 1e10m, and -123.456M are all of type decimal.

If the specified literal cannot be represented in the indicated type, then a compile-time error occurs.


2.5.3.4 Character literals

A character literal is a single character enclosed in single quotes, as in 'a'.

character-literal:
' character '
character:
single-character
simple-escape-sequence
hexadecimal-escape-sequence
unicode-character-escape-sequence
single-character:
Any character except ' (U+0027), \ (U+005C), and white-space other than space (U+0020)
simple-escape-sequence: one of
\' \" \\ \0 \a \b \f \n \r \t \v
hexadecimal-escape-sequence:
\x hex-digit hex-digitopt hex-digitopt hex-digitopt

A character that follows a backslash character (\) in a simple-escape-sequence or hexadecimal-escape-sequence must be one of the following characters: ', ", \, 0, a, b, f, n, r, t, x, v. Otherwise, a compile-time error occurs.

A simple escape sequence represents a Unicode character encoding, as described in the table below.

Escape sequence | Character name | Unicode encoding
\' | Single quote | 0x0027
\" | Double quote | 0x0022
\\ | Backslash | 0x005C
\0 | Null | 0x0000
\a | Alert | 0x0007
\b | Backspace | 0x0008
\f | Form feed | 0x000C
\n | New line | 0x000A
\r | Carriage return | 0x000D
\t | Horizontal tab | 0x0009
\v | Vertical tab | 0x000B


2.5.3.5 String literals

C# supports two forms of string literals: regular string literals and verbatim string literals. A regular string literal consists of zero or more characters enclosed in double quotes, as in "Hello, world", and may include both simple escape sequences (such as \t for the tab character) and hexadecimal escape sequences.

A verbatim string literal consists of an @ character followed by a double-quote character, zero or more characters, and a closing double-quote character. A simple examples is @"Hello, world". In a verbatim string literal, the characters between the delimiters are interpreted verbatim, with the only exception being a quote escape sequence. In particular, simple escape sequences and hexadecimal escape sequences are not processed in verbatim string literals. A verbatim string literal may span multiple lines.

string-literal:
regular-string-literal
verbatim-string-literal
regular-string-literal:
" regular-string-literal-charactersopt "
regular-string-literal-characters:
regular-string-literal-character
regular-string-literal-characters regular-string-literal-character
regular-string-literal-character:
single-regular-string-literal-character
simple-escape-sequence
hexadecimal-escape-sequence
unicode-character-escape-sequence
single-regular-string-literal-character:
Any character except " (U+0022), \ (U+005C), and white-space other than space (U+0020)
verbatim-string-literal:
@" verbatim -string-literal-charactersopt "
verbatim-string-literal-characters:
verbatim-string-literal-character
verbatim-string-literal-characters verbatim-string-literal-character
verbatim-string-literal-character:
single-verbatim-string-literal-character
quote-escape-sequence
single-verbatim-string-literal-character:
any character except "
quote-escape-sequence:
""

The example

string a = "hello, world";                  // hello, world
string b = @"hello, world";               // hello, world
string c = "hello \t world";               // hello     world
string d = @"hello \t world";               // hello \t world
string e = "Joe said \"Hello\" to me";      // Joe said "Hello"
string f = @"Joe said ""Hello"" to me";   // Joe said "Hello"
string g = "\\\\sever\\share\\file.txt";   // \\server\share\file.txt
string h = @"\\server\share\file.txt";      // \\server\share\file.txt
string i = "one\ntwo\nthree";
string j = @"one
two
three";

shows a variety of string literals. The last string literal, j, is a verbatim string literal that spans multiple lines. The characters between the quotation marks, including white space such as newline characters, are duplicated verbatim.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

2.5.3.6 The null literal

null-literal:
null


2.5.4 Operators and punctuators

operator-or-punctuator: one of
{ } [ ] ( ) . , : ;
+ - * / % & | ^ ! ~
= < > ? ++ -- && || << >>
== != <= >= += -= *= /= %= &=
|= ^= <<= >>= ->


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

3. Basic concepts

© 2000 Microsoft Corporation. All rights reserved.


3.1 Declarations

Declarations in a C# program define the constituent elements of the program. C# programs are organized using namespaces (§9), which can contain type declarations and nested namespace declarations. Type declarations (§9.5) are used to define classes (§10), structs (§11), interfaces (§13), enums (§14), and delegates (§15). The kinds of members permitted in a type declaration depends on the form of the type declaration. For instance, class declarations can contain declarations for instance constructors (§10.10), destructors (§10.11), static constructors (§10.12), constants (§10.3), fields (§10.4), methods (§10.5), properties (§10.6), events (§10.7), indexers (§10.8), operators (§10.9), and nested types.

A declaration defines a name in the declaration space to which the declaration belongs. Except for overloaded constructor, method, indexer, and operator names, it is an error to have two or more declarations that introduce members with the same name in a declaration space. It is never possible for a declaration space to contain different kinds of members with the same name. For example, a declaration space can never contain a field and a method by the same name.

There are several different types of declaration spaces, as described in the following.

  • Within all source files of a program, namespace-member-declarations with no enclosing namespace-declaration are members of a single combined declaration space called the global declaration space.
  • Within all source files of a program, namespace-member-declarations within namespace-declarations that have the same fully qualified namespace name are members of a single combined declaration space.
  • Each class, struct, or interface declaration creates a new declaration space. Names are introduced into this declaration space through class-member-declarations, struct-member-declarations, or interface-member-declarations. Except for overloaded constructor declarations and static constructor declarations, a class or struct member declaration cannot introduce a member by the same name as the class or struct. A class, struct, or interface permits the declaration of overloaded methods and indexers. A class or struct furthermore permits the declaration of overloaded constructors and operators. For instance, a class, struct, or interface may contain multiple method declarations with the same name, provided these method declarations differ in their signature (§3.4). Note that base classes do not contribute to the declaration space of a class, and base interfaces do not contribute to the declaration space of an interface. Thus, a derived class or interface is allowed to declare a member with the same name as an inherited member. Such a member is said to hide the inherited member.
  • Each enumeration declaration creates a new declaration space. Names are introduced into this declaration space through enum-member-declarations.
  • Each block or switch-block creates a separate declaration space for local variables. Names are introduced into this declaration space through local-variable-declarations. If a block is the body of a constructor or method declaration, the parameters declared in the formal-parameter-list are members of the block’s local variable declaration space. The local variable declaration space of a block includes any nested blocks. Thus, within a nested block it is not possible to declare a local variable with the same name as a local variable in an enclosing block.
  • Each block or switch-block creates a separate declaration space for labels. Names are introduced into this declaration space through labeled-statements, and the names are referenced through goto-statements. The label declaration space of a block includes any nested blocks. Thus, within a nested block it is not possible to declare a label with the same name as a label in an enclosing block.

The textual order in which names are declared is generally of no significance. In particular, textual order is not significant for the declaration and use of namespaces, types, constants, methods, properties, events, indexers, operators, constructors, destructors, and static constructors. Declaration order is significant in the following ways:

  • Declaration order for field declarations and local variable declarations determines the order in which their initializers (if any) are executed.
  • Local variables must be defined before they are used (§3.5).
  • Declaration order for enum member declarations (§Error! Reference source not found.) is significant when constant-expression values are omitted.

The declaration space of a namespace is "open ended", and two namespace declarations with the same fully qualified name contribute to the same declaration space. For example

namespace Megacorp.Data
{
   class Customer
   {
      ...
   }
}
namespace Megacorp.Data
{
   class Order
   {
      ...
   }
}

The two namespace declarations above contribute to the same declaration space, in this case declaring two classes with the fully qualified names Megacorp.Data.Customer and Megacorp.Data.Order. Because the two declarations contribute to the same declaration space, it would have been an error if each contained a declaration of a class with the same name.

The declaration space of a block includes any nested blocks. Thus, in the following example, the F and G methods are in error because the name i is declared in the outer block and cannot be redeclared in the inner block. However, the H and I method is valid since the two i’s are declared in separate non-nested blocks.

class A
{
   void F() {
      int i = 0;
      if (true) {
         int i = 1;
      }
   }
void G() {
      if (true) {
         int i = 0;
      }
      int i = 1;
   }
void H() {
      if (true) {
         int i = 0;
      }
      if (true) {
         int i = 1;
      }
   }
void I() {
      for (int i = 0; i < 10; i++)
         H();
      for (int i = 0; i < 10; i++)
         H();
   }
}


3.2 Members

Namespaces and types have members. The members of an entity are generally available through the use of a qualified name that starts with a reference to the entity, followed by a "." token, followed by the name of the member.

Members of a type are either declared in the type or inherited from the base class of the type. When a type inherits from a base class, all members of the base class, except constructors and destructors, become members of the derived type. The declared accessibility of a base class member does not control whether the member is inherited—inheritance extends to any member that isn’t a constructor or destructor. However, an inherited member may not be accessible in a derived type, either because of its declared accessibility (§3.3) or because it is hidden by a declaration in the type itself (§3.5.1.2).


3.2.1 Namespace members

Namespaces and types that have no enclosing namespace are members of the global namespace. This corresponds directly to the names declared in the global declaration space.

Namespaces and types declared within a namespace are members of that namespace. This corresponds directly to the names declared in the declaration space of the namespace.

Namespaces have no access restrictions. It is not possible to declare private, protected, or internal namespaces, and namespace names are always publicly accessible.


3.2.2 Struct members

The members of a struct are the members declared in the struct and the members inherited from class object.

The members of a simple type correspond directly to the members of the struct type aliased by the simple type:

  • The members of sbyte are the members of the System.SByte struct.
  • The members of byte are the members of the System.Byte struct.
  • The members of short are the members of the System.Int16 struct.
  • The members of ushort are the members of the System.UInt16 struct.
  • The members of int are the members of the System.Int32 struct.
  • The members of uint are the members of the System.UInt32 struct.
  • The members of long are the members of the System.Int64 struct.
  • The members of ulong are the members of the System.UInt64 struct.
  • The members of char are the members of the System.Char struct.
  • The members of float are the members of the System.Single struct.
  • The members of double are the members of the System.Double struct.
  • The members of decimal are the members of the System.Decimal struct.
  • The members of bool are the members of the System.Boolean struct.


3.2.3 Enumeration members

The members of an enumeration are the constants declared in the enumeration and the members inherited from class object.


3.2.4 Class members

The members of a class are the members declared in the class and the members inherited from the base class (except for class object which has no base class). The members inherited from the base class include the constants, fields, methods, properties, events, indexers, operators, and types of the base class, but not the constructors, destructors, and static constructors of the base class. Base class members are inherited without regard to their accessibility.

A class declaration may contain declarations of constants, fields, methods, properties, events, indexers, operators, constructors, destructors, static constructors, and types.

The members of object and string correspond directly to the members of the class types they alias:

  • The members of object are the members of the System.Object class.
  • The members of string are the members of the System.String class.


3.2.5 Interface members

The members of an interface are the members declared in the interface and in all base interfaces of the interface, and the members inherited from class object.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

3.2.6 Array members

The members of an array are the members inherited from class System.Array.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

3.2.7 Delegate members

The members of a delegate are the members inherited from class System.Delegate.


3.3 Member access

Declarations of members allow control over member access. The accessibility of a member is established by the declared accessibility (§3.3.1) of the member combined with the accessibility of the immediately containing type, if any.

When access to a particular member is allowed, the member is said to be accessible. Conversely, when access to a particular member is disallowed, the member is said to be inaccessible. Access to a member is permitted when the textual location in which the access takes place is included in the accessibility domain (§3.3.2) of the member.


3.3.1 Declared accessibility

The declared accessibility of a member can be one of the following:

  • Public, which is selected by including a public modifier in the member declaration. The intuitive meaning of public is "access not limited".
  • Protected internal (meaning protected or internal), which is selected by including both a protected and an internal modifier in the member declaration. The intuitive meaning of protected internal is "access limited to this project or types derived from the containing class".
  • Protected, which is selected by including a protected modifier in the member declaration. The intuitive meaning of protected is "access limited to the containing class or types derived from the containing class".
  • Internal, which is selected by including an internal modifier in the member declaration. The intuitive meaning of internal is "access limited to this project".
  • Private, which is selected by including a private modifier in the member declaration. The intuitive meaning of private is "access limited to the containing type".

Depending on the context in which a member declaration takes place, only certain types of declared accessibility are permitted. Furthermore, when a member declaration does not include any access modifiers, the context in which the declaration takes place determines the default declared accessibility.

  • Namespaces implicitly have public declared accessibility. No access modifiers are allowed on namespace declarations.
  • Types declared in compilation units or namespaces can have public or internal declared accessibility and default to internal declared accessibility.
  • Class members can have any of the five types of declared accessibility and default to private declared accessibility. (Note that a type declared as a member of a class can have any of the five types of declared accessibility, whereas a type declared as a member of a namespace can have only public or internal declared accessibility.)
  • Struct members can have public, internal, or private declared accessibility and default to private declared accessibility. Struct members cannot have protected or protected internal declared accessibility.
  • Interface members implicitly have public declared accessibility. No access modifiers are allowed on interface member declarations.
  • Enumeration members implicitly have public declared accessibility. No access modifiers are allowed on enumeration member declarations.


3.3.2 Accessibility domains

The accessibility domain of a member is the (possibly disjoint) sections of program text in which access to the member is permitted. For purposes of defining the accessibility domain of a member, a member is said to be top-level if it is not declared within a type, and a member is said to be nested if it is declared within another type. Furthermore, the program text of a project is defined as all program text contained in all source files of the project, and the program text of a type is defined as all program text contained between the opening and closing "{" and "}" tokens in the class-body, struct-body, interface-body, or enum-body of the type (including, possibly, types that are nested within the type).

The accessibility domain of a predefined type (such as object, int, or double) is unlimited.

The accessibility domain of a top-level type T declared in a project P is defined as follows:

  • If the declared accessibility of T is public, the accessibility domain of T is the program text of P and any project that references P.
  • If the declared accessibility of T is internal, the accessibility domain of T is the program text of P.

From these definitions it follows that the accessibility domain of a top-level type is always at least the program text of the project in which the type is declared.

The accessibility domain of a nested member M declared in a type T within a project P is defined as follows (noting that M may itself possibly be a type):

  • If the declared accessibility of M is public, the accessibility domain of M is the accessibility domain of T.
  • If the declared accessibility of M is protected internal, the accessibility domain of M is the intersection of the accessibility domain of T with the program text of P and the program text of any type derived from T declared outside P.
  • If the declared accessibility of M is protected, the accessibility domain of M is the intersection of the accessibility domain of T with the program text of T and any type derived from T.
  • If the declared accessibility of M is internal, the accessibility domain of M is the intersection of the accessibility domain of T with the program text of P.
  • If the declared accessibility of M is private, the accessibility domain of M is the program text of T.

From these definitions it follows that the accessibility domain of a nested member is always at least the program text of the type in which the member is declared. Furthermore, it follows that the accessibility domain of a member is never more inclusive than the accessibility domain of the type in which the member is declared.

In intuitive terms, when a type or member M is accessed, the following steps are evaluated to ensure that the access is permitted:

  • First, if M is declared within a type (as opposed to a compilation unit or a namespace), an error occurs if that type is not accessible.
  • Then, if M is public, the access is permitted.
  • Otherwise, if M is protected internal, the access is permitted if it occurs within the project in which M is declared, or if it occurs within a class derived from the class in which M is declared and takes place through the derived class type (§3.3.3).
  • Otherwise, if M is protected, the access is permitted if it occurs within the class in which M is declared, or if it occurs within a class derived from the class in which M is declared and takes place through the derived class type (§3.3.3).
  • Otherwise, if M is internal, the access is permitted if it occurs within the project in which M is declared.
  • Otherwise, if M is private, the access is permitted if it occurs within the type in which M is declared.
  • Otherwise, the type or member is inaccessible, and an error occurs.

In the example

public class A
{
   public static int X;
   internal static int Y;
   private static int Z;
}
internal class B
{
   public static int X;
   internal static int Y;
   private static int Z;
   public class C
   {
      public static int X;
      internal static int Y;
      private static int Z;
   }
   private class D
   {
      public static int X;
      internal static int Y;
      private static int Z;
   }
}

the classes and members have the following accessibility domains:

  • The accessibility domain of A and A.X is unlimited.
  • The accessibility domain of A.Y, B, B.X, B.Y, B.C, B.C.X, and B.C.Y is the program text of the containing project.
  • The accessibility domain of A.Z is the program text of A.
  • The accessibility domain of B.Z and B.D is the program text of B, including the program text of B.C and B.D.
  • The accessibility domain of B.C.Z is the program text of B.C.
  • The accessibility domain of B.D.X, B.D.Y, and B.D.Z is the program text of B.D.

As the example illustrates, the accessibility domain of a member is never larger than that of a containing type. For example, even though all X members have public declared accessibility, all but A.X have accessibility domains that are constrained by a containing type.

As described in §3.2, all members of a base class, except for constructors and destructors, are inherited by derived types. This includes even private members of a base class. However, the accessibility domain of a private member includes only the program text of the type in which the member is declared. In the example

class A
{
   int x;
   static void F(B b) {
      b.x = 1;      // Ok
   }
}
class B: A
{
   static void F(B b) {
      b.x = 1;      // Error, x not accessible
   }
}

the B class inherits the private member x from the A class. Because the member is private, it is only accessible within the class-body of A. Thus, the access to b.x succeeds in the A.F method, but fails in the B.F method.


3.3.3 Protected access

When a protected member is accessed outside the program text of the class in which it is declared, and when a protected internal member is accessed outside the program text of the project in which it is declared, the access is required to take place through the derived class type in which the access occurs. Let B be a base class that declares a protected member M, and let D be a class that derives from B. Within the class-body of D, access to M can take one of the following forms:

  • An unqualified type-name or primary-expression of the form M.
  • A type-name of the form T.M, provided T is D or a class derived from D.
  • A primary-expression of the form E.M, provided the type of E is D or a class derived from D.
  • A primary-expression of the form base.M.

In addition to these forms of access, a derived class can access a protected constructor of a base class in a constructor-initializer (§10.10.1).

In the example

public class A
{
   protected int x;
   static void F(A a, B b) {
      a.x = 1;      // Ok
      b.x = 1;      // Ok
   }
}
public class B: A
{
   static void F(A a, B b) {
      a.x = 1;      // Error, must access through instance of B
      b.x = 1;      // Ok
   }
}

within A, it is possible to access x through instances of both A and B, since in either case the access takes place through an instance of A or a class derived from A. However, within B, it is not possible to access x through an instance of A, since A does not derive from B.


3.3.4 Accessibility constraints

Several constructs in the C# language require a type to be at least as accessible as a member or another type. A type T is said to be at least as accessible as a member or type M if the accessibility domain of T is a superset of the accessibility domain of M. In other words, T is at least as accessible as M if T is accessible in all contexts where M is accessible.

The following accessibility constraints exist:

  • The direct base class of a class type must be at least as accessible as the class type itself.
  • The explicit base interfaces of an interface type must be at least as accessible as the interface type itself.
  • The return type and parameter types of a delegate type must be at least as accessible as the delegate type itself.
  • The type of a constant must be at least as accessible as the constant itself.
  • The type of a field must be at least as accessible as the field itself.
  • The return type and parameter types of a method must be at least as accessible as the method itself.
  • The type of a property must be at least as accessible as the property itself.
  • The type of an event must be at least as accessible as the event itself.
  • The type and parameter types of an indexer must be at least as accessible as the indexer itself.
  • The return type and parameter types of an operator must be at least as accessible as the operator itself.
  • The parameter types of a constructor must be at least as accessible as the constructor itself.

In the example

class A {...}
public class B: A {...}

the B class is in error because A is not at least as accessible as B.

Likewise, in the example

class A {...}
public class B
{
   A F() {...}
   internal A G() {...}
   public A H() {...}
}

the H method in B is in error because the return type A is not at least as accessible as the method.


3.4 Signatures and overloading

Methods, constructors, indexers, and operators are characterized by their 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 specifically does not include the return type.
  • The signature of a constructor consists of the number, modifiers, and types of its formal parameters.
  • The signature of an indexer consists of the number and types of its formal parameters. The signature of an indexer specifically does not include the element type.
  • The signature of an operator consists of the name of the operator and the number and types of its formal parameters. The signature of an operator specifically does not include the result type.

Signatures are the enabling mechanism for overloading of members in classes, structs, and interfaces:

  • Overloading of methods permits a class, struct, or interface to declare multiple methods with the same name, provided the signatures of the methods are all unique.
  • Overloading of constructors permits a class or struct to declare multiple constructors, provided the signatures of the constructors are all unique.
  • Overloading of indexers permits a class, struct, or interface to declare multiple indexers, provided the signatures of the indexers are all unique.
  • Overloading of operators permits a class or struct to declare multiple operators with the same name, provided the signatures of the operators are all unique.

The following example shows a set of overloaded method declarations along with their signatures.

interface ITest
{
   void F();               // F()
   void F(int x);            // F(int)
   void F(ref int x);      // F(ref int)
   void F(out int x);      // F(out int)
   void F(int x, int y);   // F(int, int)
   int F(string s);         // F(string)
   int F(int x);            // F(int)
}

Note that parameter modifiers are part of a signature. Thus, F(int), F(ref int), and F(out int) are all unique signatures. Furthermore note that even though the second and last method declarations differ in return types, their signatures are both F(int). Thus, compiling the above example would produce errors for the second and last methods.


3.5 Scopes

The scope of a name is the region of program text within which it is possible to refer to the entity declared by the name without qualification of the name. Scopes can be nested, and an inner scope may redeclare the meaning of a name from an outer scope. The name from the outer scope is then said to be hidden in the region of program text covered by the inner scope, and access to the outer name is only possible by qualifying the name.

  • The scope of a namespace member declared by a namespace-member-declaration with no enclosing namespace-declaration is the entire program text of each compilation unit.
  • The scope of a namespace member declared by a namespace-member-declaration within a namespace-declaration whose fully qualified name is N is the namespace-body of every namespace-declaration whose fully qualified name is N or starts with the same sequence of identifiers as N.
  • The scope of a name defined or imported by a using-directive extends over the namespace-member-declarations of the compilation-unit or namespace-body in which the using-directive occurs. A using-directive may make zero or more namespace or type names available within a particular compilation-unit or namespace-body, but does not contribute any new members to the underlying declaration space. In other words, a using-directive is not transitive but rather affects only the compilation-unit or namespace-body in which it occurs.
  • The scope of a member declared by a class-member-declaration is the class-body in which the declaration occurs. In addition, the scope of a class member extends to the class-body of those derived classes that are included in the accessibility domain (§3.3.2) of the member.
  • The scope of a member declared by a struct-member-declaration is the struct-body in which the declaration occurs.
  • The scope of a member declared by an enum-member-declaration is the enum-body in which the declaration occurs.
  • The scope of a parameter declared in a constructor-declaration is the constructor-initializer and block of that constructor-declaration.
  • The scope of a parameter declared in a method-declaration is the method-body of that method-declaration.
  • The scope of a parameter declared in an indexer-declaration is the accessor-declarations of that indexer-declaration.
  • The scope of a parameter declared in an operator-declaration is the block of that operator-declaration.
  • The scope of a local variable declared in a local-variable-declaration is the block in which the declaration occurs. It is an error to refer to a local variable in a textual position that precedes the variable-declarator of the local variable.
  • The scope of a local variable declared in a for-initializer of a for statement is the for-initializer, the for-condition, the for-iterator, and the contained statement of the for statement.
  • The scope of a label declared in a labeled-statement is the block in which the declaration occurs.

Within the scope of a namespace, class, struct, or enumeration member it is possible to refer to the member in a textual position that precedes the declaration of the member. For example

class A
{
   void F() {
      i = 1;
   }
   int i = 0;
}

Here, it is valid for F to refer to i before it is declared.

Within the scope of a local variable, it is an error to refer to the local variable in a textual position that precedes the variable-declarator of the local variable. For example

class A
{
   int i = 0;
   void F() {
      i = 1;               // Error, use precedes declaration
      int i;
      i = 2;
   }
   void G() {
      int j = (j = 1);      // Legal
   }
   void H() {
      int a = 1, b = ++a;   // Legal
   }
}

In the F method above, the first assignment to i specifically does not refer to the field declared in the outer scope. Rather, it refers to the local variable and it is in error because it textually precedes the declaration of the variable. In the G method, the use of j in the initializer for the declaration of j is legal because the use does not precede the variable-declarator. In the H method, a subsequent variable-declarator legally refers to a local variable declared in an earlier variable-declarator within the same local-variable-declaration.

The scoping rules for local variables are designed to guarantee that the meaning of a name used in an expression context is always the same within a block. If the scope of a local variable was to extend only from its declaration to the end of the block, then in the example above, the first assignment would assign to the instance variable and the second assignment would assign to the local variable, possibly leading to errors if the statements of the block were later to be rearranged.

The meaning of a name within a block may differ based on the context in which the name is used. In the example

class Test
{
   static void Main() {
      string A = "hello, world";
      string s = A;                        // expression context
      Type t = typeof(A);                  // type context
      Console.WriteLine(s);               // writes "hello, world"
      Console.WriteLine(t.ToString());      // writes "Type: A"
   }
}

the name A is used in an expression context to refer to the local variable A and in a type context to refer to the class A.


3.5.1 Name hiding

The scope of an entity typically encompasses more program text than the declaration space of the entity. In particular, the scope of an entity may include declarations that introduce new declaration spaces containing entities of the same name. Such declarations cause the original entity to become hidden. Conversely, an entity is said to be visible when it is not hidden.

Name hiding occurs when scopes overlap through nesting and when scopes overlap through inheritance. The characteristics of the two types of hiding are described in the following sections.


3.5.1.1 Hiding through nesting

Name hiding through nesting can occur as a result of nesting namespaces or types within namespaces, as a result of nesting types within classes or structs, and as a result of parameter and local variable declarations. Name hiding through nesting of scopes always occurs "silently", i.e. no errors or warnings are reported when outer names are hidden by inner names.

In the example

class A
{
   int i = 0;
   void F() {
      int i = 1;
   }
   void G() {
      i = 1;
   }
}

within the F method, the instance variable i is hidden by the local variable i, but within the G method, i still refers to the instance variable.

When a name in an inner scope hides a name in an outer scope, it hides all overloaded occurrences of that name. In the example

class Outer
{
   static void F(int i) {}
   static void F(string s) {}
   class Inner
   {
      void G() {
         F(1);            // Invokes Outer.Inner.F
         F("Hello");      // Error
      }
      static void F(long l) {}
   }
}

the call F(1) invokes the F declared in Inner because all outer occurrences of F are hidden by the inner declaration. For the same reason, the call F("Hello") is in error.


3.5.1.2 Hiding through inheritance

Name hiding through inheritance occurs when classes or structs redeclare names that were inherited from base classes. This type of name hiding takes one of the following forms:

  • A constant, field, property, event, or type introduced in a class or struct hides all base class members with the same name.
  • A method introduced in a class or struct hides all non-method base class members with the same name, and all base class methods with the same signature (method name and parameter count, modifiers, and types).
  • An indexer introduced in a class or struct hides all base class indexers with the same signature (parameter count and types).

The rules governing operator declarations (§10.9) make it impossible for a derived class to declare an operator with the same signature as an operator in a base class. Thus, operators never hide one another.

Contrary to hiding a name from an outer scope, hiding an accessible name from an inherited scope causes a warning to be reported. In the example

class Base
{
   public void F() {}
}
class Derived: Base
{
   public void F() {}      // Warning, hiding an inherited name
}

the declaration of F in Derived causes a warning to be reported. Hiding an inherited name is specifically not an error, since that would preclude separate evolution of base classes. For example, the above situation might have come about because a later version of Base introduced a F method that wasn’t present in an earlier version of the class. Had the above situation been an error, then any change made to a base class in a separately versioned class library could potentially cause derived classes to become invalid.

The warning caused by hiding an inherited name can be eliminated through use of the new modifier:

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

The new modifier indicates that the F in Derived is "new", and that it is indeed intended to hide the inherited member.

A declaration of a new member hides an inherited member only within the scope of the new member.

class Base
{
   public static void F() {}
}
class Derived: Base
{
   new private static void F() {}   // Hides Base.F in Derived only
}
class MoreDerived: Derived
{
   static void G() { F(); }         // Invokes Base.F
}

In the example above, the declaration of F in Derived hides the F that was inherited from Base, but since the new F in Derived has private access, its scope does not extend to MoreDerived. Thus, the call F() in MoreDerived.G is valid and will invoke Base.F.


3.6 Namespace and type names

Several contexts in a C# program require a namespace-name or a type-name to be specified. Either form of name is written as one or more identifiers separated by "." tokens.

namespace-name:
namespace-or-type-name
type-name:
namespace-or-type-name
namespace-or-type-name:
identifier
namespace-or-type-name . identifier

A type-name is a namespace-or-type-name that refers to a type. Following resolution as described below, the namespace-or-type-name of a type-name must refer to a type, or otherwise an error occurs.

A namespace-name is a namespace-or-type-name that refers to a namespace. Following resolution as described below, the namespace-or-type-name of a namespace-name must refer to a namespace, or otherwise an error occurs.

The meaning of a namespace-or-type-name is determined as follows:

  • If the namespace-or-type-name consists of a single identifier:
    • If the namespace-or-type-name appears within the body of a class or struct declaration, then starting with that class or struct declaration and continuing with each enclosing class or struct declaration (if any), if a member with the given name exists, is accessible, and denotes a type, then the namespace-or-type-name refers to that member. Note that non-type members (constructors, constants, fields, methods, properties, indexers, and operators) are ignored when determining the meaning of a namespace-or-type-name.
    • Otherwise, starting with the namespace declaration in which the namespace-or-type-name occurs (if any), continuing with each enclosing namespace declaration (if any), and ending with the global namespace, the following steps are evaluated until an entity is located:
      • If the namespace contains a namespace member with the given name, then the namespace-or-type-name refers to that member and, depending on the member, is classified as a namespace or a type.
      • Otherwise, if the namespace declaration contains a using-alias-directive that associates the given name with an imported namespace or type, then the namespace-or-type-name refers to that namespace or type.
      • Otherwise, if the namespaces imported by the using-namespace-directives of the namespace declaration contain exactly one type with the given name, then the namespace-or-type-name refers to that type.
      • Otherwise, if the namespaces imported by the using-namespace-directives of the namespace declaration contain more than one type with the given name, then the namespace-or-type-name is ambiguous and an error occurs.
    • Otherwise, the namespace-or-type-name is undefined and an error occurs.
  • Otherwise, the namespace-or-type-name is of the form N.I, where N is a namespace-or-type-name consisting of all identifiers but the rightmost one, and I is the rightmost identifier. N is first resolved as a namespace-or-type-name. If the resolution of N is not successful, an error occurs. Otherwise, N.I is resolved as follows:
    • If N is a namespace and I is the name of an accessible member of that namespace, then N.I refers to that member and, depending on the member, is classified as a namespace or a type.
    • If N is a class or struct type and I is the name of an accessible type in N, then N.I refers to that type.
    • Otherwise, N.I is an invalid namespace-or-type-name, and an error occurs.


3.6.1 Fully qualified names

Every namespace and type has a fully qualified name which uniquely identifies the namespace or type amongst all others. The fully qualified name of a namespace or type N is determined as follows:

  • If N is a member of the global namespace, its fully qualified name is N.
  • Otherwise, its fully qualified name is S.N, where S is the fully qualified name of the namespace or type in which N is declared.

In other words, the fully qualified name of N is the complete hierarchical path of identifiers that lead to N, starting from the global namespace. Because every member of a namespace or type must have a unique name, it follows that the fully qualified name of a namespace or type is always unique.

The example below shows several namespace and type declarations along with their associated fully qualified names.

class A {}            // A
namespace X            // X
{
   class B            // X.B
   {
      class C {}      // X.B.C
   }
   namespace Y         // X.Y
   {
      class D {}      // X.Y.D
   }
}
namespace X.Y         // X.Y
{
   class E {}         // X.Y.E
}


4. Types

The types of the C# language are divided into three categories: Value types, reference types, and pointer types.

type:
value-type
reference-type
pointer-type

Pointer types can be used only in unsafe code, and are discussed further in §19.2.

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 their data, the latter known as 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.

C#’s type system is unified such that a value of any type can be treated as an object. Every type in C# directly or indirectly derives from the object class type, and object is the ultimate base class of all types. Values of reference types are treated as objects simply by viewing the values as type object. Values of value types are treated as objects by performing boxing and unboxing operations (§4.3).


4.1 Value types

A value type is either a struct type or an enumeration type. C# provides a set of predefined struct types called the simple types. The simple types are identified through reserved words, and are further subdivided into numeric types, integral types, and floating point types.

value-type:
struct-type
enum-type
struct-type:
type-name
simple-type
simple-type:
numeric-type
bool
numeric-type:
integral-type
floating-point-type
decimal
integral-type:
sbyte
byte
short
ushort
int
uint
long
ulong
char
floating-point-type:
float
double
enum-type:
type-name

All value types implicitly inherit from class object. It is not possible for any type to derive from a value type, and value types are thus implicitly sealed.

A variable of a value type always contains a value of that type. Unlike reference types, it is not possible for a value of a value type to be null or to reference an object of a more derived type.

Assignment to a variable of a value type creates a copy of the value being assigned. This differs from assignment to a variable of a reference type, which copies the reference but not the object identified by the reference.


4.1.1 Default constructors

All value types implicitly declare a public parameterless constructor called the default constructor. The default constructor returns a zero-initialized instance known as the default value for the value type:

  • For all simple-types, the default value is the value produced by a bit pattern of all zeros:
    • For sbyte, byte, short, ushort, int, uint, long, and ulong, the default value is 0.
    • For char, the default value is '\x0000'.
    • For float, the default value is 0.0f.
    • For double, the default value is 0.0d.
    • For decimal, the default value is 0.0m.
    • For bool, the default value is false.
  • For an enum-type E, the default value is 0.
  • For a struct-type, the default value is the value produced by setting all value type fields to their default value and all reference type fields to null.

Like any other constructor, the default constructor of a value type is invoked using the new operator. In the example below, the i and j variables are both initialized to zero.

class A
{
   void F() {
      int i = 0;
      int j = new int();
   }
}

Because every value type implicitly has a public parameterless constructor, it is not possible for a struct type to contain an explicit declaration of a parameterless constructor. A struct type is however permitted to declare parameterized constructors. For example

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

Given the above declaration, the statements

Point p1 = new Point();
Point p2 = new Point(0, 0);

both create a Point with x and y initialized to zero.


4.1.2 Struct types

A struct type is a value type that can declare constructors, constants, fields, methods, properties, indexers, operators, and nested types. Struct types are described in §11.


4.1.3 Simple types

C# provides a set of predefined struct types called the simple types. The simple types are identified through reserved words, but these reserved words are simply aliases for predefined struct types in the System namespace, as described in the table below.

Reserved word | Aliased type
sbyte | System.SByte
byte | System.Byte
short | System.Int16
ushort | System.UInt16
int | System.Int32
uint | System.UInt32
long | System.Int64
ulong | System.UInt64
char | System.Char
float | System.Single
double | System.Double
bool | System.Boolean
decimal | System.Decimal

A simple type and the struct type it aliases are completely indistinguishable. In other words, writing the reserved word byte is exactly the same as writing System.Byte, and writing System.Int32 is exactly the same as writing the reserved word int.

Because a simple type aliases a struct type, every simple type has members. For example, int has the members declared in System.Int32 and the members inherited from System.Object, and the following statements are permitted:

int i = int.MaxValue;         // System.Int32.MaxValue constant
string s = i.ToString();      // System.Int32.ToString() instance method
string t = 123.ToString();      // System.Int32.ToString() instance method

Notice in particular that integer literals are values of type int, and therefore also values of the System.Int32 struct type.

The simple types differ from other struct types in that they permit certain additional operations:

  • Most simple types permit values to be created by writing literals (§2.5.3). For example, 123 is a literal of type int and 'a' is a literal of type char. C# makes no provision for literals of other struct types, and values of other struct types are ultimately always created through constructors of those struct types.
  • When the operands of an expression are all simple type constants, it is possible for the compiler to evaluate the expression at compile time. Such an expression is known as a constant-expression (§7.15). Expressions involving operators defined by other struct types always imply run time evaluation.
  • Through const declarations it is possible to declare constants of the simple types (§10.3). It is not possible to have constants of other struct types, but a similar effect is provided by static readonly fields.
  • Conversions involving simple types can participate in evaluation of conversion operators defined by other struct types, but a user-defined conversion operator can never participate in evaluation of another user-defined operator (§6.4.2).


4.1.4 Integral types

C# supports nine integral types: sbyte, byte, short, ushort, int, uint, long, ulong, and char. The integral types have the following sizes and ranges of values:

  • The sbyte type represents signed 8-bit integers with values between –128 and 127.
  • The byte type represents unsigned 8-bit integers with values between 0 and 255.
  • The short type represents signed 16-bit integers with values between –32768 and 32767.
  • The ushort type represents unsigned 16-bit integers with values between 0 and 65535.
  • The int type represents signed 32-bit integers with values between –2147483648 and 2147483647.
  • The uint type represents unsigned 32-bit integers with values between 0 and 4294967295.
  • The long type represents signed 64-bit integers with values between –9223372036854775808 and 9223372036854775807.
  • The ulong type represents unsigned 64-bit integers with values between 0 and 18446744073709551615.
  • The char type represents unsigned 16-bit integers with values between 0 to 65535. The set of possible values for the char type corresponds to the Unicode character set.

The integral-type unary and binary operators always operate with signed 32-bit precision, unsigned 32-bit precision, signed 64-bit precision, or unsigned 64-bit precision:

  • For the unary + and ~ operators, the operand is converted to type T, where T is the first of int, uint, long, and ulong that can fully represent all possible values of the operand. The operation is then performed using the precision of type T, and the type of the result T.
  • For the unary – operator, the operand is converted to type T, where T is the first of int and long that can fully represent all possible values of the operand. The operation is then performed using the precision of type T, and the type of the result is T. The unary – operator cannot be applied to operands of type ulong.
  • For the binary +, –, *, /, %, &, ^, |, ==, !=, >, <, >=, and <= operators, the operands are converted to type T, where T is the first of int, uint, long, and ulong that can fully represent all possible values of each operand. The operation is then performed using the precision of type T, and the type of the result is T (or bool for the relational operators).
  • For the binary << and >> operators, the left operand is converted to type T, where T is the first of int, uint, long, and ulong that can fully represent all possible values of the operand. The operation is then performed using the precision of type T, and the type of the result T.

The char type is classified as an integral type, but it differs from the other integral types in two ways:

  • There are no implicit conversions from other types to the char type. In particular, even though the sbyte, byte, and ushort types have ranges of values that are fully representable using the char type, implicit conversions from sbyte, byte, or ushort to char do not exist.
  • Constants of the char type must be written as character-literals. Character constants can only be written as integer-literals in combination with a cast. For example, (char)10 is the same as '\x000A'.

The checked and unchecked operators and statements are used to control overflow checking for integral-type arithmetic operations and conversions (§7.5.13). In a checked context, an overflow produces a compile-time error or causes an OverflowException to be thrown. In an unchecked context, overflows are ignored and any high-order bits that do not fit in the destination type are discarded.


4.1.5 Floating point types

C# supports two floating point types: float and double. The float and double types are represented using the 32-bit single-precision and 64-bit double-precision IEEE 754 formats, which provide the following sets of values:

  • Positive zero and negative zero. In most situations, positive zero and negative zero behave identically as the simple value zero, but certain operations distinguish between the two.
  • Positive infinity and negative infinity. Infinities are produced by such operations as dividing a non-zero number by zero. For example 1.0 / 0.0 yields positive infinity, and –1.0 / 0.0 yields negative infinity.
  • The Not-a-Number value, often abbreviated NaN. NaN’s are produced by invalid floating-point operations, such as dividing zero by zero.
  • The finite set of non-zero values of the form s × m × 2e, where s is 1 or -1, and m and e are determined by the particular floating-point type: For float, 0 < m < 224 and -149 = e = 104, and for double, 0 < m < 253 and -1075 = e = 970.

The float type can represent values ranging from approximately 1.5 × 10-45 to 3.4 × 1038 with a precision of 7 digits.

The double type can represent values ranging from approximately 5.0 × 10-324 to 1.7 × 10308 with a precision of 15-16 digits.

If one of the operands of a binary operator is of a floating-point type, then the other operand must be of an integral type or a floating-point type, and the operation is evaluated as follows:

  • If one of the operands of is of an integral type, then that operand is converted to the floating-point type of the other operand.
  • Then, if either of the operands is of type double, the other operand is converted to double, the operation is performed using at least double range and precision, and the type of the result is double (or bool for the relational operators).
  • Otherwise, the operation is performed using at least float range and precision, and the type of the result is float (or bool for the relational operators).

The floating-point operators, including the assignment operators, never produce exceptions. Instead, in exceptional situations, floating-point operations produce zero, infinity, or NaN, as described below:

  • If the result of a floating-point operation is too small for the destination format, the result of the operation becomes positive zero or negative zero.
  • If the result of a floating-point operation is too large for the destination format, the result of the operation becomes positive infinity or negative infinity.
  • If a floating-point operation is invalid, the result of the operation becomes NaN.
  • If one or both operands of a floating-point operation is NaN, the result of the operation becomes NaN.

Floating-point operations may be performed with higher precision than the result type of the operation. For example, some hardware architectures support an "extended" or "long double" floating-point type with greater range and precision than the double type, and implicitly perform all floating-point operations using this higher precision type. Only at excessive cost in performance can such hardware architectures be made to perform floating-point operations with less precision, and rather than require an implementation to forfeit both performance and precision, C# allows a higher precision type to be used for all floating-point operations. Other than delivering more precise results, this rarely has any measurable effects. However, in expressions of the form x * y / z, where the multiplication produces a result that is outside the double range, but the subsequent division brings the temporary result back into the double range, the fact that the expression is evaluated in a higher range format may cause a finite result to be produced instead of an infinity.


4.1.6 The decimal type

The decimal type is a 128-bit data type suitable for financial and monetary calculations. The decimal type can represent values ranging from 1.0 × 10-28 to approximately 7.9 × 1028 with 28-29 significant digits.

The finite set of values of type decimal are of the form s × m × 10e, where s is 1 or –1, 0 = m < 296, and -28 = e = 0. The decimal type does not support signed zeros, infinities, and NaN's.

A decimal is represented as a 96-bit integer scaled by a power of ten. For decimals with an absolute value less than 1.0m, the value is exact to the 28th decimal place, but no further. For decimals with an absolute value greater than or equal to 1.0m, the value is exact to 28 or 29 digits. Contrary to the float and double data types, decimal fractional numbers such as 0.1 can be represented exactly in the decimal representation. In the float and double representations, such numbers are often infinite fractions, making those representations more prone to round-off errors.

If one of the operands of a binary operator is of type decimal, then the other operand must be of an integral type or of type decimal. If an integral type operand is present, it is converted to decimal before the operation is performed.

Operations on values of type decimal are exact to 28 or 29 digits, but to no more than 28 decimal places. Results are rounded to the nearest representable value, and, when a result is equally close to two representable values, to the value that has an even number in the least significant digit position.

If a decimal arithmetic operation produces a value that is too small for the decimal format after rounding, the result of the operation becomes zero. If a decimal arithmetic operation produces a result that is too large for the decimal format, an OverflowException is thrown.

The decimal type has greater precision but smaller range than the floating-point types. Thus, conversions from the floating-point types to decimal might produce overflow exceptions, and conversions from decimal to the floating-point types might cause loss of precision. For these reasons, no implicit conversions exist between the floating-point types and decimal, and without explicit casts, it is not possible to mix floating-point and decimal operands in the same expression.


4.1.7 The bool type

The bool type represents boolean logical quantities. The possible values of type bool are true and false.

No standard conversions exist between bool and other types. In particular, the bool type is distinct and separate from the integral types, and a bool value cannot be used in place of an integral value, nor vice versa.

In the C and C++ languages, a zero integral value or a null pointer can be converted to the boolean value false, and a non-zero integral value or a non-null pointer can be converted to the boolean value true. In C#, such conversions are accomplished by explicitly comparing an integral value to zero or explicitly comparing an object reference to null.


4.1.8 Enumeration types

An enumeration type is a distinct type with named constants. Every enumeration type has an underlying type, which can be either byte, short, int, or long. Enumeration types are defined through enumeration declarations (§14.1).


4.2 Reference types

A reference type is a class type, an interface type, an array type, or a delegate type.

reference-type:
class-type
interface-type
array-type
delegate-type
class-type:
type-name
object
string
interface-type:
type-name
array-type:
non-array-type rank-specifiers
non-array-type:
type
rank-specifiers:
rank-specifier
rank-specifiers rank-specifier
rank-specifier:
[ dim-separatorsopt ]
dim-separators:
,
dim-separators ,
delegate-type:
type-name

A reference type value is a reference to an instance of the type, the latter known as an object. The special value null is compatible with all reference types and indicates the absence of an instance.


4.2.1 Class types

A class type defines a data structure that contains data members (constants, fields, and events), function members (methods, properties, indexers, operators, constructors, and destructors), and nested types. Class types support inheritance, a mechanism whereby derived classes can extend and specialize base classes. Instances of class types are created using object-creation-expressions (§7.5.10.1).

Class types are described in §10.


4.2.2 The object type

The object class type is the ultimate base class of all other types. Every type in C# directly or indirectly derives from the object class type.

The object keyword is simply an alias for the predefined System.Object class. Writing the keyword object is exactly the same as writing System.Object, and vice versa.


4.2.3 The string type

The string type is a sealed class type that inherits directly from object. Instances of the string class represent Unicode character strings.

Values of the string type can be written as string literals (§2.5.3.5).

The string keyword is simply an alias for the predefined System.String class. Writing the keyword string is exactly the same as writing System.String, and vice versa.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

4.2.4 Interface types

© 2000 Microsoft Corporation. All rights reserved.


4.2.5 Array types

An array is a data structure that contains a number of variables which are accessed through computed indices. The variables contained in an array, also called the elements of the array, are all of the same type, and this type is called the element type of the array.

Array types are described in §12.


4.2.6 Delegate types

A delegate is a data structure that refers to a static method or to an object instance and an instance method of that object.

The closest equivalent of a delegate in C or C++ is a function pointer, but whereas a function pointer can only reference static functions, a delegate can reference both static and instance methods. In the latter case, the delegate stores not only a reference to the method’s entry point, but also a reference to the object instance for which to invoke the method.

Delegate types are described in §15.


4.3 Boxing and unboxing

Boxing and unboxing is a central concept in C#’s type system. It provides a binding link between value-types and reference-types by permitting any value of a value-type to be converted to and from type object. Boxing and unboxing enables a unified view of the type system wherein a value of any type can ultimately be treated as an object.


4.3.1 Boxing conversions

A boxing conversion permits any value-type to be implicitly converted to the type object or to any interface-type implemented by the value-type. Boxing a value of a value-type consists of allocating an object instance and copying the value-type value into that instance.

The actual process of boxing a value of a value-type is best explained by imagining the existence of a boxing class for that type. For any value-type T, the boxing class would be declared as follows:

class T_Box
{
   T value;
   T_Box(T t) {
      value = t;
   }
}

Boxing of a value v of type T now consists of executing the expression new T_Box(v), and returning the resulting instance as a value of type object. Thus, the statements

int i = 123;
object box = i;

conceptually correspond to

int i = 123;
object box = new int_Box(i);

Boxing classes like T_Box and int_Box above don’t actually exist and the dynamic type of a boxed value isn’t actually a class type. Instead, a boxed value of type T has the dynamic type T, and a dynamic type check using the is operator can simply reference type T. For example,

int i = 123;
object box = i;
if (box is int) {
   Console.Write("Box contains an int");
}

will output the string "Box contains an int" on the console.

A boxing conversion implies making a copy of the value being boxed. This is different from a conversion of a reference-type to type object, in which the value continues to reference the same instance and simply is regarded as the less derived type object. For example, given the declaration

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

the following statements

Point p = new Point(10, 10);
object box = p;
p.x = 20;
Console.Write(((Point)box).x);

will output the value 10 on the console because the implicit boxing operation that occurs in the assignment of p to box causes the value of p to be copied. Had Point instead been declared a class, the value 20 would be output because p and box would reference the same instance.


4.3.2 Unboxing conversions

An unboxing conversion permits an explicit conversion from type object to any value-type or from any interface-type to any value-type that implements the interface-type. An unboxing operation consists of first checking that the object instance is a boxed value of the given value-type, and then copying the value out of the instance.

Referring to the imaginary boxing class described in the previous section, an unboxing conversion of an object box to a value-type T consists of executing the expression ((T_Box)box).value. Thus, the statements

object box = 123;
int i = (int)box;

conceptually correspond to

object box = new int_Box(123);
int i = ((int_Box)box).value;

For an unboxing conversion to a given value-type to succeed at run-time, the value of the source argument must be a reference to an object that was previously created by boxing a value of that value-type. If the source argument is null or a reference to an incompatible object, an InvalidCastException is thrown.


5. Variables

Variables represent storage locations. Every variable has a type that determines what values can be stored in the variable. C# is a type-safe language, and the C# compiler guarantees that values stored in variables are always of the appropriate type. The value of a variable can be changed through assignment or through use of the ++ and -- operators.

A variable must be definitely assigned (§5.3) before its value can be obtained.

As described in the following sections, variables are either initially assigned or initially unassigned. An initially assigned variable has a well defined initial value and is always considered definitely assigned. An initially unassigned variable has no initial value. For an initially unassigned variable to be considered definitely assigned at a certain location, an assignment to the variable must occur in every possible execution path leading to that location.


5.1 Variable categories

C# defines seven categories of variables: Static variables, instance variables, array elements, value parameters, reference parameters, output parameters, and local variables. The sections that follow describe each of these categories.

In the example

class A
{
   static int x;
   int y;
   void F(int[] v, int a, ref int b, out int c) {
      int i = 1;
   }
}

x is a static variable, y is an instance variable, v[0] is an array element, a is a value parameter, b is a reference parameter, c is an output parameter, and i is a local variable.


5.1.1 Static variables

A field declared with the static modifier is called a static variable. A static variable comes into existence when the type in which it is declared is loaded, and ceases to exist when the type in which it is declared is unloaded.

The initial value of a static variable is the default value (§5.2) of the variable’s type.

For purposes of definite assignment checking, a static variable is considered initially assigned.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

5.1.2 Instance variables

A field declared without the static modifier is called an instance variable.


5.1.2.1 Instance variables in classes

An instance variable of a class comes into existence when a new instance of that class is created, and ceases to exist when there are no references to that instance and the destructor of the instance has executed.

The initial value of an instance variable of a class is the default value (§5.2) of the variable’s type.

For purposes of definite assignment checking, an instance variable of a class is considered initially assigned.


5.1.2.2 Instance variables in structs

An instance variable of a struct has exactly the same lifetime as the struct variable to which it belongs. In other words, when a variable of a struct type comes into existence or ceases to exist, so do the instance variables of the struct.

The initial assignment state of an instance variable of a struct in the same as that of the containing struct variable. In other words, when a struct variable is considered initially assigned, so are its instance variables, and when a struct variable is considered initially unassigned, its instance variables are likewise unassigned.


5.1.3 Array elements

The elements of an array come into existence when an array instance is created, and cease to exist when there are no references to that array instance.

The initial value of each of the elements of an array is the default value (§5.2) of the type of the array elements.

For purposes of definite assignment checking, an array element is considered initially assigned.


5.1.4 Value parameters

A parameter declared without a ref or out modifier is a value parameter.

A value parameter comes into existence upon invocation of the function member (method, constructor, accessor, or operator) to which the parameter belongs, and is initialized with the value of the argument given in the invocation. A value parameter ceases to exist upon return of the function member.

For purposes of definite assignment checking, a value parameter is considered initially assigned.


5.1.5 Reference parameters

A parameter declared with a ref modifier is a reference parameter.

A reference parameter does not create a new storage location. Instead, a reference parameter represents the same storage location as the variable given as the argument in the function member invocation. Thus, the value of a reference parameter is always the same as the underlying variable.

The following definite assignment rules apply to reference parameters. Note the different rules for output parameters described in §5.1.6.

  • A variable must be definitely assigned (§5.3) before it can be passed as a reference parameter in a function member invocation.
  • Within a function member, a reference parameter is considered initially assigned.

Within an instance method or instance accessor of a struct type, the this keyword behaves exactly as a reference parameter of the struct type (§7.5.7).


5.1.6 Output parameters

A parameter declared with an out modifier is an output parameter.

An output parameter does not create a new storage location. Instead, an output parameter represents the same storage location as the variable given as the argument in the function member invocation. Thus, the value of an output parameter is always the same as the underlying variable.

The following definite assignment rules apply to output parameters. Note the different rules for reference parameters described in §5.1.5.

  • A variable need not be definitely assigned before it can be passed as an output parameter in a function member invocation.
  • Following a function member invocation, each variable that was passed as an output parameter is considered assigned in that execution path.
  • Within a function member, an output parameter is considered initially unassigned.
  • Every output parameter of a function member must be definitely assigned (§5.3) before the function member returns.

Within a constructor of a struct type, the this keyword behaves exactly as an output parameter of the struct type (§7.5.7).


5.1.7 Local variables

A local variable is declared by a local-variable-declaration, which may occur in a block, a for-statement, or a switch-statement. A local variable comes into existence when control enters the block, for-statement, or switch-statement that immediately contains the local variable declaration. A local variable ceases to exist when control leaves its immediately containing block, for-statement, or switch-statement.

A local variable is not automatically initialized and thus has no default value. For purposes of definite assignment checking, a local variable is considered initially unassigned. A local-variable-declaration may include a variable-initializer, in which case the variable is considered definitely assigned in its entire scope, except within the expression provided in the variable-initializer.

Within the scope of a local variable, it is an error to refer to the local variable in a textual position that precedes its variable-declarator.


5.2 Default values

The following categories of variables are automatically initialized to their default values:

  • Static variables.
  • Instance variables of class instances.
  • Array elements.

The default value of a variable depends on the type of the variable and is determined as follows:

  • For a variable of a value-type, the default value is the same as the value computed by the value-type’s default constructor (§4.1.1).
  • For a variable of a reference-type, the default value is null.


5.3 Definite assignment

At a given location in the executable code of a function member, a variable is said to be definitely assigned if the compiler can prove, by static flow analysis, that the variable has been automatically initialized or has been the target of at least one assignment. The rules of definite assignment are:

  • An initially assigned variable (§5.3.1) is always considered definitely assigned.
  • An initially unassigned variable (§5.3.2) is considered definitely assigned at a given location if all possible execution paths leading to that location contain at least one of the following:
    • A simple assignment (§7.13.1) in which the variable is the left operand.
    • An invocation expression (§7.5.5) or object creation expression (§7.5.10.1) that passes the variable as an output parameter.
    • For a local variable, a local variable declaration (§8.5) that includes a variable initializer.

The definite assignment state of instance variables of a struct-type variable are tracked individually as well as collectively. In additional to the rules above, the following rules apply to struct-type variables and their instance variables:

  • An instance variable is considered definitely assigned if its containing struct-type variable is considered definitely assigned.
  • A struct-type variable is considered definitely assigned if each of its instance variables are considered definitely assigned.

Definite assignment is a requirement in the following contexts:

  • A variable must be definitely assigned at each location where its value is obtained. This ensures that undefined values never occur. The occurrence of a variable in an expression is considered to obtain the value of the variable, except when
    • the variable is the left operand of a simple assignment,
    • the variable is passed as an output parameter, or
    • the variable is a struct-type variable and occurs as the left operand of a member access.
  • A variable must be definitely assigned at each location where it is passed as a reference parameter. This ensures that the function member being invoked can consider the reference parameter initially assigned.
  • All output parameters of a function member must be definitely assigned at each location where the function member returns (through a return statement or through execution reaching the end of the function member body). This ensures that function members do no return undefined values in output parameters, thus enabling the compiler to consider a function member invocation that takes a variable as an output parameter equivalent to an assignment to the variable.
  • The this variable of a struct-type constructor must be definitely assigned at each location where the constructor returns.

The following example demonstrates how the different blocks of a try statement affect definite assignment.

class A
{
   static void F() {
      int i, j;
      try {
         // neither i nor j definitely assigned
         i = 1;
         // i definitely assigned
         j = 2;
         // i and j definitely assigned
      }
      catch {
         // neither i nor j definitely assigned
         i = 3;
         // i definitely assigned
      }
      finally {
         // neither i nor j definitely assigned
         i = 4;
         // i definitely assigned
         j = 5;
         // i and j definitely assigned
      }
      // i and j definitely assigned
   }
}

The static flow analysis performed to determine the definite assignment state of a variable takes into account the special behavior of the &&, ||, and ?: operators. In each of the methods in the example

class A
{
   static void F(int x, int y) {
      int i;
      if (x >= 0 && (i = y) >= 0) {
         // i definitely assigned
      }
      else {
         // i not definitely assigned
      }
      // i not definitely assigned
   }
   static void G(int x, int y) {
      int i;
      if (x >= 0 || (i = y) >= 0) {
         // i not definitely assigned
      }
      else {
         // i definitely assigned
      }
      // i not definitely assigned
   }
}

the variable i is considered definitely assigned in one of the embedded statements of an if statement but not in the other. In the if statement in the F method, the variable i is definitely assigned in the first embedded statement because execution of the expression (i = y) always precedes execution of this embedded statement. In contrast, the variable i is not definitely assigned in the second embedded statement since the variable i may be unassigned. Specifically, the variable i is unassigned if the value of the variable x is negative. Similarly, in the G method, the variable i is definitely assigned in the second embedded statement but not in the first embedded statement.


5.3.1 Initially assigned variables

The following categories of variables are classified as initially assigned:

  • Static variables.
  • Instance variables of class instances.
  • Instance variables of initially assigned struct variables.
  • Array elements.
  • Value parameters.
  • Reference parameters.


5.3.2 Initially unassigned variables

The following categories of variables are classified as initially unassigned:

  • Instance variables of initially unassigned struct variables.
  • Output parameters, including the this variable of struct constructors.
  • Local variables.


5.4 Variable references

A variable-reference is an expression that is classified as a variable. A variable-reference denotes a storage location that can be accessed both to fetch the current value and to store a new value. In C and C++, a variable-reference is known as an lvalue.

variable-reference:
expression

The following constructs require an expression to be a variable-reference:

  • The left hand side of an assignment (which may also be a property access or an indexer access).
  • An argument passed as a ref or out parameter in a method or constructor invocation.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

6. Conversions

© 2000 Microsoft Corporation. All rights reserved.


6.1 Implicit conversions

The following conversions are classified as implicit conversions:

  • Identity conversions
  • Implicit numeric conversions
  • Implicit enumeration conversions.
  • Implicit reference conversions
  • Boxing conversions
  • Implicit constant expression conversions
  • User-defined implicit conversions

Implicit conversions can occur in a variety of situations, including function member invocations (§7.4.3), cast expressions (§7.6.8), and assignments (§7.13).

The pre-defined implicit conversions always succeed and never cause exceptions to be thrown. Properly designed user-defined implicit conversions should exhibit these characteristics as well.


6.1.1 Identity conversion

An identity conversion converts from any type to the same type. This conversion exists only such that an entity that already has a required type can be said to be convertible to that type.


6.1.2 Implicit numeric conversions

The implicit numeric conversions are:

  • From sbyte to short, int, long, float, double, or decimal.
  • From byte to short, ushort, int, uint, long, ulong, float, double, or decimal.
  • From short to int, long, float, double, or decimal.
  • From ushort to int, uint, long, ulong, float, double, or decimal.
  • From int to long, float, double, or decimal.
  • From uint to long, ulong, float, double, or decimal.
  • From long to float, double, or decimal.
  • From ulong to float, double, or decimal.
  • From char to ushort, int, uint, long, ulong, float, double, or decimal.
  • From float to double.

Conversions from int, uint, or long to float and from long to double may cause a loss of precision, but will never cause a loss of magnitude. The other implicit numeric conversions never lose any information.

There are no implicit conversions to the char type. This in particular means that values of the other integral types do not automatically convert to the char type.


6.1.3 Implicit enumeration conversions

An implicit enumeration conversion permits the decimal-integer-literal 0 to be converted to any enum-type.


6.1.4 Implicit reference conversions

The implicit reference conversions are:

  • From any reference-type to object.
  • From any class-type S to any class-type T, provided S is derived from T.
  • From any class-type S to any interface-type T, provided S implements T.
  • From any interface-type S to any interface-type T, provided S is derived from T.
  • From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true:
    • S and T differ only in element type. In other words, S and T are both statically ranked arrays with the same number of dimensions, or S and T are both dynamically ranked arrays.
    • Both SE and TE are reference-types.
    • An implicit reference conversion exists from SE to TE.
  • From a statically ranked array-type S to a dynamically ranked array-type T, provided S and T have the same element type.
  • From any array-type to System.Array.
  • From any delegate-type to System.Delegate.
  • From any array-type or delegate-type to System.ICloneable.
  • From the null type to any reference-type.

The implicit reference conversions are those conversions between reference-types that can be proven to always succeed, and therefore require no checks at run-time.

Reference conversions, implicit or explicit, never change the referential identity of the object being converted. In other words, while a reference conversion may change the type of a value, it never changes the value itself.


6.1.5 Boxing conversions

A boxing conversion permits any value-type to be implicitly converted to the type object or to any interface-type implemented by the value-type. Boxing a value of a value-type consists of allocating an object instance and copying the value-type value into that instance.

Boxing conversions are further described in §4.3.1.


6.1.6 Implicit constant expression conversions

An implicit constant expression conversion permits the following conversions:

  • A constant-expression (§7.15) of type int can be converted to type sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant-expression is within the range of the destination type.
  • A constant-expression of type long can be converted to type ulong, provided the value of the constant-expression is not negative.


6.1.7 User-defined implicit conversions

A user-defined implicit conversion consists of an optional standard implicit conversion, followed by execution of a user-defined implicit conversion operator, followed by another optional standard implicit conversion. The exact rules for evaluating user-defined conversions are described in §6.4.3.


6.2 Explicit conversions

The following conversions are classified as explicit conversions:

  • All implicit conversions.
  • Explicit numeric conversions.
  • Explicit enumeration conversions.
  • Explicit reference conversions.
  • Explicit interface conversions.
  • Unboxing conversions.
  • User-defined explicit conversions.

Explicit conversions can occur in cast expressions (§7.6.8).

The explicit conversions are conversions that cannot be proved to always succeed, conversions that are known to possibly lose information, and conversions across domains of types sufficiently different to merit explicit notation.

The set explicit conversions includes all implicit conversions. This in particular means that redundant cast expressions are allowed.


6.2.1 Explicit numeric conversions

The explicit numeric conversions are the conversions from a numeric-type to another numeric-type for which an implicit numeric conversion (§6.1.2) does not already exist:

  • From sbyte to byte, ushort, uint, ulong, or char.
  • From byte to sbyte and char.
  • From short to sbyte, byte, ushort, uint, ulong, or char.
  • From ushort to sbyte, byte, short, or char.
  • From int to sbyte, byte, short, ushort, uint, ulong, or char.
  • From uint to sbyte, byte, short, ushort, int, or char.
  • From long to sbyte, byte, short, ushort, int, uint, ulong, or char.
  • From ulong to sbyte, byte, short, ushort, int, uint, long, or char.
  • From char to sbyte, byte, or short.
  • From float to sbyte, byte, short, ushort, int, uint, long, ulong, char, or decimal.
  • From double to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or decimal.
  • From decimal to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or double.

Because the explicit conversions include all implicit and explicit numeric conversions, it is always possible to convert from any numeric-type to any other numeric-type using a cast expression (§7.6.8).

The explicit numeric conversions possibly lose information or possibly cause exceptions to be thrown. An explicit numeric conversion is processed as follows:

  • For a conversion from an integral type to another integral type, the processing depends on the overflow checking context (§7.5.13) in which the conversion takes place:
    • In a checked context, the conversion succeeds if the source argument is within the range of the destination type, but throws an OverflowException if the source argument is outside the range of the destination type.
    • In an unchecked context, the conversion always succeeds, and simply consists of discarding the most significant bits of the source value.
  • For a conversion from float, double, or decimal to an integral type, the source value is rounded towards zero to the nearest integral value, and this integral value becomes the result of the conversion. If the resulting integral value is outside the range of the destination type, an OverflowException is thrown.
  • For a conversion from double to float, the double value is rounded to the nearest float value. If the double value is too small to represent as a float, the result becomes positive zero or negative zero. If the double value is too large to represent as a float, the result becomes positive infinity or negative infinity. If the double value is NaN, the result is also NaN.
  • For a conversion from float or double to decimal, the source value is converted to decimal representation and rounded to the nearest number after the 28th decimal place if required (§4.1.6). If the source value is too small to represent as a decimal, the result becomes zero. If the source value is NaN, infinity, or too large to represent as a decimal, an InvalidCastException is thrown.
  • For a conversion from decimal to float or double, the decimal value is rounded to the nearest double or float value. While this conversion may lose precision, it never causes an exception to be thrown.


6.2.2 Explicit enumeration conversions

The explicit enumeration conversions are:

  • From sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or decimal to any enum-type.
  • From any enum-type to sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, or decimal.
  • From any enum-type to any other enum-type.

An explicit enumeration conversion between two types is processed by treating any participating enum-type as the underlying type of that enum-type, and then performing an implicit or explicit numeric conversion between the resulting types. For example, given an enum-type E with and underlying type of int, a conversion from E to byte is processed as an explicit numeric conversion (§6.2.1) from int to byte, and a conversion from byte to E is processed as an implicit numeric conversion (§6.1.2) from byte to int.


6.2.3 Explicit reference conversions

The explicit reference conversions are:

  • From object to any reference-type.
  • From any class-type S to any class-type T, provided S is a base class of T.
  • From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T.
  • From any interface-type S to any class-type T, provided T is not sealed or provided T implements S.
  • From any interface-type S to any interface-type T, provided S is not derived from T.
  • From an array-type S with an element type SE to an array-type T with an element type TE, provided all of the following are true:
    • S and T differ only in element type. In other words, S and T have the same number of dimensions.
    • Both SE and TE are reference-types.
    • An explicit reference conversion exists from SE to TE.
  • From a dynamically ranked array-type S to a statically ranked array-type T, provided S and T have the same element type.
  • From System.Array to any array-type.
  • From System.Delegate to any delegate-type.
  • From System.ICloneable to any array-type or delegate-type.

The explicit reference conversions are those conversions between reference-types that require run-time checks to ensure they are correct.

For an explicit reference conversion to succeed at run-time, the value of the source argument must be null or the actual type of the object referenced by the source argument must be a type that can be converted to the destination type by an implicit reference conversion (§6.1.4). If an explicit reference conversion fails, an InvalidCastException is thrown.

Reference conversions, implicit or explicit, never change the referential identity of the object being converted. In other words, while a reference conversion may change the type of a value, it never changes the value itself.


6.2.4 Unboxing conversions

An unboxing conversion permits an explicit conversion from type object to any value-type or from any interface-type to any value-type that implements the interface-type. An unboxing operation consists of first checking that the object instance is a boxed value of the given value-type, and then copying the value out of the instance.

Unboxing conversions are further described in §4.3.2.


6.2.5 User-defined explicit conversions

A user-defined explicit conversion consists of an optional standard explicit conversion, followed by execution of a user-defined implicit or explicit conversion operator, followed by another optional standard explicit conversion. The exact rules for evaluating user-defined conversions are described in §6.4.4.


6.3 Standard conversions

The standard conversions are those pre-defined conversions that can occur as part of a user-defined conversion.


6.3.1 Standard implicit conversions

The following implicit conversions are classified as standard implicit conversions:

  • Identity conversions (§6.1.1)
  • Implicit numeric conversions (§6.1.2)
  • Implicit reference conversions (§6.1.4)
  • Boxing conversions (§6.1.5)
  • Implicit constant expression conversions (§6.1.6)

The standard implicit conversions specifically exclude user-defined implicit conversions.


6.3.2 Standard explicit conversions

The standard explicit conversions are all standard implicit conversions plus the subset of the explicit conversions for which an opposite standard implicit conversion exists. In other words, if a standard implicit conversion exists from a type A to a type B, then a standard explicit conversion exists from type A to type B and from type B to type A.


6.4 User-defined conversions

C# allows the pre-defined implicit and explicit conversions to be augmented by user-defined conversions. User-defined conversions are introduced by declaring conversion operators (§10.9.3) in class and struct types.


6.4.1 Permitted user-defined conversions

C# permits only certain user-defined conversions to be declared. In particular, it is not possible to redefine an already existing implicit or explicit conversion. A class or struct is permitted to declare a conversion from a source type S to a target type T only if all of the following are true:

  • S and T are different types.
  • Either S or T is the class or struct type in which the operator declaration takes place.
  • Neither S nor T is object or an interface-type.
  • T is not a base class of S, and S is not a base class of T.

The restrictions that apply to user-defined conversions are discussed further in §10.9.3.


6.4.2 Evaluation of user-defined conversions

A user-defined conversion converts a value from its type, called the source type, to another type, called the target type. Evaluation of a user-defined conversion centers on finding the most specific user-defined conversion operator for the particular source and target types. This determination is broken into several steps:

  • Finding the set of classes and structs from which user-defined conversion operators will be considered. This set consists of the source type and its base classes and the target type and its base classes (with the implicit assumptions that only classes and structs can declare user-defined operators, and that non-class types have no base classes).
  • From that set of types, determining which user-defined conversion operators are applicable. For a conversion operator to be applicable, it must be possible to perform a standard conversion (§6.3) from the source type to the argument type of the operator, and it must be possible to perform a standard conversion from the result type of the operator to the target type.
  • From the set of applicable user-defined operators, determining which operator is unambiguously the most specific. In general terms, the most specific operator is the operator whose argument type is "closest" to the source type and whose result type is "closest" to the target type. The exact rules for establishing the most specific user-defined conversion operator are defined in the following sections.

Once a most specific user-defined conversion operator has been identified, the actual execution of the user-defined conversion involves up to three steps:

  • First, if required, performing a standard conversion from the source type to the argument type of the user-defined conversion operator.
  • Next, invoking the user-defined conversion operator to perform the conversion.
  • Finally, if required, performing a standard conversion from the result type of the user-defined conversion operator to the target type.

Evaluation of a user-defined conversion never involves more than one user-defined conversion operator. In other words, a conversion from type S to type T will never first execute a user-defined conversion from S to X and then execute a user-defined conversion from X to T.

Exact definitions of evaluation of user-defined implicit or explicit conversions are given in the following sections. The definitions make use of the following terms:

  • If a standard implicit conversion (§6.3.1) exists from a type A to a type B, and if neither A nor B are interface-types, then A is said to be encompassed by B, and B is said to encompass A.
  • The most encompassing type in a set of types is the one type that encompasses all other types in the set. If no single type encompasses all other types, then the set has no most encompassing type. In more intuitive terms, the most encompassing type is the "largest" type in the set—the one type to which each of the other types can be implicitly converted.
  • The most encompassed type in a set of types is the one type that is encompassed by all other types in the set. If no single type is encompassed by all other types, then the set has no most encompassed type. In more intuitive terms, the most encompassed type is the "smallest" type in the set—the one type that can be implicitly converted to each of the other types.


6.4.3 User-defined implicit conversions

A user-defined implicit conversion from type S to type T is processed as follows:

  • Find the set of types, D, from which user-defined conversion operators will be considered. This set consists of S (if S is a class or struct), the base classes of S (if S is a class), T (if T is a class or struct), and the base classes of T (if T is a class).
  • Find the set of applicable user-defined conversion operators, U. This set consists of the user-defined implicit conversion operators declared by the classes or structs in D that convert from a type encompassing S to a type encompassed by T. If U is empty, the conversion is undefined and an error occurs.
  • Find the most specific source type, SX, of the operators in U:
    • If any of the operators in U convert from S, then SX is S.
    • Otherwise, SX is the most encompassed type in the combined set of source types of the operators in U. If no most encompassed type can be found, then the conversion is ambiguous and an error occurs.
  • Find the most specific target type, TX, of the operators in U:
    • If any of the operators in U convert to T, then TX is T.
    • Otherwise, TX is the most encompassing type in the combined set of target types of the operators in U. If no most encompassing type can be found, then the conversion is ambiguous and an error occurs.
  • If U contains exactly one user-defined conversion operator that converts from SX to TX, then this is the most specific conversion operator. If no such operator exists, or if more than one such operator exists, then the conversion is ambiguous and an error occurs. Otherwise, the user-defined conversion is applied:
    • If S is not SX, then a standard implicit conversion from S to SX is performed.
    • The most specific user-defined conversion operator is invoked to convert from SX to TX.
    • If TX is not T, then a standard implicit conversion from TX to T is performed.


6.4.4 User-defined explicit conversions

A user-defined explicit conversion from type S to type T is processed as follows:

  • Find the set of types, D, from which user-defined conversion operators will be considered. This set consists of S (if S is a class or struct), the base classes of S (if S is a class), T (if T is a class or struct), and the base classes of T (if T is a class).
  • Find the set of applicable user-defined conversion operators, U. This set consists of the user-defined implicit or explicit conversion operators declared by the classes or structs in D that convert from a type encompassing or encompassed by S to a type encompassing or encompassed by T. If U is empty, the conversion is undefined and an error occurs.
  • Find the most specific source type, SX, of the operators in U:
    • If any of the operators in U convert from S, then SX is S.
    • Otherwise, if any of the operators in U convert from types that encompass S, then SX is the most encompassed type in the combined set of source types of those operators. If no most encompassed type can be found, then the conversion is ambiguous and an error occurs.
    • Otherwise, SX is the most encompassing type in the combined set of source types of the operators in U. If no most encompassing type can be found, then the conversion is ambiguous and an error occurs.
  • Find the most specific target type, TX, of the operators in U:
    • If any of the operators in U convert to T, then TX is T.
    • Otherwise, if any of the operators in U convert to types that are encompassed by T, then TX is the most encompassing type in the combined set of source types of those operators. If no most encompassing type can be found, then the conversion is ambiguous and an error occurs.
    • Otherwise, TX is the most encompassed type in the combined set of target types of the operators in U. If no most encompassed type can be found, then the conversion is ambiguous and an error occurs.
  • If U contains exactly one user-defined conversion operator that converts from SX to TX, then this is the most specific conversion operator. If no such operator exists, or if more than one such operator exists, then the conversion is ambiguous and an error occurs. Otherwise, the user-defined conversion is applied:
    • If S is not SX, then a standard explicit conversion from S to SX is performed.
    • The most specific user-defined conversion operator is invoked to convert from SX to TX.
    • If TX is not T, then a standard explicit conversion from TX to T is performed.


7. Expressions

An expression is a sequence of operators and operands that specifies a computation. This chapter defines the syntax, order of evaluation, and meaning of expressions.


7.1 Expression classifications

An expression is classified as one of the following:

  • A value. Every value has an associated type.
  • A variable. Every variable has an associated type, namely the declared type of the variable.
  • A namespace. An expression with this classification can only appear as the left hand side of a member-access (§7.5.4). In any other context, an expression classified as a namespace causes an error.
  • A type. An expression with this classification can only appear as the left hand side of a member-access (§7.5.4). In any other context, an expression classified as a type causes an error.
  • A method group, which is a set of overloaded methods resulting from a member lookup (§7.3). A method group may have associated instance expression. When an instance method is invoked, the result of evaluating the instance expression becomes the instance represented by this (§7.5.7). A method group is only permitted in an invocation-expression (§7.5.5) or a delegate-creation-expression (§7.5.10.3). In any other context, an expression classified as a method group causes an error.
  • A property access. Every property access has an associated type, namely the type of the property. A property access may furthermore have an associated instance expression. When an accessor (the get or set block) of an instance property access is invoked, the result of evaluating the instance expression becomes the instance represented by this (§7.5.7).
  • An event access. Every event access has an associated type, namely the type of the event. An event access may furthermore have an associated instance expression. An event access may appear as the left hand operand of the += and -= operators (§7.13.3). In any other context, an expression classified as an event access causes an error.
  • An indexer access. Every indexer access has an associated type, namely the element type of the indexer. Furthermore, an indexer access has an associated instance expression and an associated argument list. When an accessor (the get or set block) of an indexer access is invoked, the result of evaluating the instance expression becomes the instance represented by this (§7.5.7), and the result of evaluating the argument list becomes the parameter list of the invocation.
  • Nothing. This occurs when the expression is an invocation of a method with a return type of void. An expression classified as nothing is only valid in the context of a statement-expression (§8.6).

The final result of an expression is never a namespace, type, method group, or event access. Rather, as noted above, these categories of expressions are intermediate constructs that are only permitted in certain contexts.

A property access or indexer access is always reclassified as a value by performing an invocation of the get-accessor or the set-accessor. The particular accessor is determined by the context of the property or indexer access: If the access is the target of an assignment, the set-accessor is invoked to assign a new value (§7.13.1). Otherwise, the get-accessor is invoked to obtain the current value (§7.1.1).


7.1.1 Values of expressions

Most of the constructs that involve an expression ultimately require the expression to denote a value. In such cases, if the actual expression denotes a namespace, a type, a method group, or nothing, an error occurs. However, if the expression denotes a property access, an indexer access, or a variable, the value of the property, indexer, or variable is implicitly substituted:

  • The value of a variable is simply the value currently stored in the storage location identified by the variable. A variable must be considered definitely assigned (§5.3) before its value can be obtained, or otherwise a compile-time error occurs.
  • The value of a property access expression is obtained by invoking the get-accessor of the property. If the property has no get-accessor, an error occurs. Otherwise, a function member invocation (§7.4.3) is performed, and the result of the invocation becomes the value of the property access expression.
  • The value of an indexer access expression is obtained by invoking the get-accessor of the indexer. If the indexer has no get-accessor, an error occurs. Otherwise, a function member invocation (§7.4.3) is performed with the argument list associated with the indexer access expression, and the result of the invocation becomes the value of the indexer access expression.


7.2 Operators

Expressions are constructed from operands and operators. The operators of an expression indicate which operations to apply to the operands. Examples of operators include +, -, *, /, and new. Examples of operands include literals, fields, local variables, and expressions.

There are three types of operators:

  • Unary operators. The unary operators take one operand and use either prefix notation (such as –x) or postfix notation (such as x++).
  • Binary operators. The binary operators take two operands and all use infix notation (such as x + y).
  • Ternary operator. Only one ternary operator, ?:, exists. The ternary operator takes three operands and uses infix notation (c? x: y).

The order of evaluation of operators in an expression is determined by the precedence and associativity of the operators (§7.2.1).

Certain operators can be overloaded. Operator overloading permits user-defined operator implementations to be specified for operations where one or both of the operands are of a user-defined class or struct type (§7.2.2).


7.2.1 Operator precedence and associativity

When an expression contains multiple operators, the precedence of the operators control 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. The precedence of an operator is established by the definition of its associated grammar production. For example, an additive-expression consists of a sequence of multiplicative-expressions separated by + or - operators, thus giving the + and - operators lower precedence than the *, /, and % operators.

The following table summarizes all operators in order of precedence from highest to lowest:

Section | Category | Operators
7.5 | Primary | (x) x.y f(x) a[x] x++ x-- new typeof sizeof checked unchecked
7.6 | Unary | + - ! ~ ++x --x (T)x
7.7 | Multiplicative | * / %
7.7 | Additive | + -
7.8 | Shift | << >>
7.9 | Relational | < > <= >= is
7.9 | Equality | == !=
7.10 | Logical AND | &
7.10 | Logical XOR | ^
7.10 | Logical OR | |
7.11 | Conditional AND | &&
7.11 | Conditional OR | ||
7.12 | Conditional | ?:
7.13 | Assignment | = *= /= %= += -= <<= >>= &= ^= |=

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.


7.2.2 Operator overloading

All unary and binary operators have predefined implementations that are automatically available in any expression. In addition to the predefined implementations, user-defined implementations can be introduced by including operator declarations in classes and structs (§10.9). User-defined operator implementations always take precedence over predefined operator implementations: Only when no applicable user-defined operator implementations exist will the predefined operator implementations be considered.

The overloadable unary operators are:

+   -   !   ~   ++   --   true   false

The overloadable binary operators are:

+   -   *   /   %   &   |   ^   <<   >>   ==   !=   >   <   >=   <=

Only the operators listed above can be overloaded. In particular, it is not possible to overload member access, method invocation, or the =, &&, ||, ?:, new, typeof, sizeof, and is operators.

When an binary operator is overloaded, the corresponding assignment operator is also implicitly overloaded. For example, an overload of operator * is also an overload of operator *=. This is described further in §7.13. Note that the assignment operator itself (=) cannot be overloaded. An assignment always performs a simple bit-wise copy of a value into a variable.

Cast operations, such as (T)x, are overloaded by providing user-defined conversions (§6.4).

Element access, such as a[x], is not considered an overloadable operator. Instead, user-defined indexing is supported through indexers (§10.8).

In expressions, operators are referenced using operator notation, and in declarations, operators are referenced using functional notation. The following table shows the relationship between operator and functional notations for unary and binary operators. In the first entry, op denotes any overloadable unary operator. In the second entry, op denotes the unary ++ and -- operators. In the third entry, op denotes any overloadable binary operator.

Operator notation | Functional notation
op x | operator op(x)
x op | operator op(x)
x op y | operator op(x, y)

User-defined operator declarations always require at least one of the parameters to be of the class or struct type that contains the operator declaration. Thus, it is not possible for a user-defined operator to have the same signature as a predefined operator.

User-defined operator declarations cannot modify the syntax, precedence, or associativity of an operator. For example, the * operator is always a binary operator, always has the precedence level specified in §7.2.1, and is always left-associative.

While it is possible for a user-defined operator to perform any computation it pleases, implementations that produce results other than those that are intuitively expected are strongly discouraged. For example, an implementation of operator == should compare the two operands for equality and return an appropriate result.

The descriptions of individual operators in §7.5 through §7.13 specify the predefined implementations of the operators and any additional rules that apply to each operator. The descriptions make use of the terms unary operator overload resolution, binary operator overload resolution, and numeric promotion, definitions of which are found in the following sections.


7.2.3 Unary operator overload resolution

An operation of the form op x or x op, where op is an overloadable unary operator, and x is an expression of type X, is processed as follows:

  • The set of candidate user-defined operators provided by X for the operation operator op(x) is determined using the rules of §7.2.5.
  • If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined unary operator op implementations become the set of candidate operators for the operation. The predefined implementations of a given operator are specified in the description of the operator (§7.5 and §7.6).
  • The overload resolution rules of §7.4.2 are applied to the set of candidate operators to select the best operator with respect to the argument list (x), and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, an error occurs.


7.2.4 Binary operator overload resolution

An operation of the form x op y, where op is an overloadable binary operator, x is an expression of type X, and y is an expression of type Y, is processed as follows:

  • The set of candidate user-defined operators provided by X and Y for the operation operator op(x, y) is determined. The set consists of the union of the candidate operators provided by X and the candidate operators provided by Y, each determined using the rules of §7.2.5. If X and Y are the same type, or if X and Y are derived from a common base type, then shared candidate operators only occur in the combined set once.
  • If the set of candidate user-defined operators is not empty, then this becomes the set of candidate operators for the operation. Otherwise, the predefined binary operator op implementations become the set of candidate operators for the operation. The predefined implementations of a given operator are specified in the description of the operator (§7.7 through §7.13).
  • The overload resolution rules of §7.4.2 are applied to the set of candidate operators to select the best operator with respect to the argument list (x, y), and this operator becomes the result of the overload resolution process. If overload resolution fails to select a single best operator, an error occurs.


7.2.5 Candidate user-defined operators

Given a type T and an operation operator op(A), where op is an overloadable operator and A is an argument list, the set of candidate user-defined operators provided by T for operator op(A) is determined as follows:

  • For all operator op declarations in T, if at least one operator is applicable (§7.4.2.1) with respect to the argument list A, then the set of candidate operators consists of all applicable operator op declarations in T.
  • Otherwise, if T is object, the set of candidate operators is empty.
  • Otherwise, the set of candidate operators provided by T is the set of candidate operators provided by the direct base class of T.


7.2.6 Numeric promotions

Numeric promotion consists of automatically performing certain implicit conversions of the operands of the predefined unary and binary numeric operators. Numeric promotion is not a distinct mechanism, but rather an effect of applying overload resolution to the predefined operators. Numeric promotion specifically does not affect evaluation of user-defined operators, although user-defined operators can be implemented to exhibit similar effects.

As an example of numeric promotion, consider the predefined implementations of the binary * operator:

int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);

When overload resolution rules (§7.4.2) are applied to this set of operators, the effect is to select the first of the operators for which implicit conversions exist from the operand types. For example, for the operation b * s, where b is a byte and s is a short, overload resolution selects operator *(int, int) as the best operator. Thus, the effect is that b and s are converted to int, and the type of the result is int. Likewise, for the operation i * d, where i is an int and d is a double, overload resolution selects operator *(double, double) as the best operator.


7.2.6.1 Unary numeric promotions

Unary numeric promotion occurs for the operands of the predefined +, –, and ~ unary operators. Unary numeric promotion simply consists of converting operands of type sbyte, byte, short, ushort, or char to type int. Additionally, for the unary – operator, unary numeric promotion converts operands of type uint to type long.


7.2.6.2 Binary numeric promotions

Binary numeric promotion occurs for the operands of the predefined +, –, *, /, %, &, |, ^, ==, !=, >, <, >=, and <= binary operators. Binary numeric promotion implicitly converts both operands to a common type which, in case of the non-relational operators, also becomes the result type of the operation. Binary numeric promotion consists of applying the following rules, in the order they appear here:

  • If either operand is of type decimal, the other operand is converted to type decimal, or an error occurs if the other operand is of type float or double.
  • Otherwise, if either operand is of type double, the other operand is converted to type double.
  • Otherwise, if either operand is of type float, the other operand is converted to type float.
  • Otherwise, if either operand is of type ulong, the other operand is converted to type ulong, or an error occurs if the other operand is of type sbyte, short, int, or long.
  • Otherwise, if either operand is of type long, the other operand is converted to type long.
  • Otherwise, if either operand is of type uint and the other operand is of type sbyte, short, or int, both operands are converted to type long.
  • Otherwise, if either operand is of type uint, the other operand is converted to type uint.
  • Otherwise, both operands are converted to type int.

Note that the first rule disallows any operations that mix the decimal type with the double and float types. The rule follows from the fact that there are no implicit conversions between the decimal type and the double and float types.

Also note that it is not possible for an operand to be of type ulong when the other operand is of a signed integral type. The reason is that no integral type exists that can represent the full range of ulong as well as the signed integral types.

In both of the above cases, a cast expression can be used to explicitly convert one operand to a type that is compatible with the other operand.

In the example

decimal AddPercent(decimal x, double percent) {
   return x * (1.0 + percent / 100.0);
}

a compile-time error occurs because a decimal cannot be multiplied by a double. The error is resolved by explicitly converting the second operand to decimal:

decimal AddPercent(decimal x, double percent) {
   return x * (decimal)(1.0 + percent / 100.0);
}


7.3 Member lookup

A member lookup is the process whereby the meaning of a name in the context of a type is determined. A member lookup may occur as part of evaluating a simple-name (§7.5.2) or a member-access (§7.5.4) in an expression.

A member lookup of a name N in a type T is processed as follows:

  • First, the set of all accessible (§3.3) members named N declared in T and the base types (§7.3.1) of T is constructed. Declarations that include an override modifier are excluded from the set. If no members named N exist and are accessible, then the lookup produces no match, and the following steps are not evaluated.
  • Next, members that are hidden by other members are removed from the set. For every member S.M in the set, where S in the type in which the member M is declared, the following rules are applied:
    • If M is a constant, field, property, event, type, or enumeration member, then all members declared in a base type of S are removed from the set.
    • If M is a method, then all non-method members declared in a base type of S are removed from the set, and all methods with the same signature as M declared in a base type of S are removed from the set.
  • Finally, having removed hidden members, the result of the lookup is determined:
    • If the set consists of a single non-method member, then this member is the result of the lookup.
    • Otherwise, if the set contains only methods, then this group of methods is the result of the lookup.
    • Otherwise, the lookup is ambiguous, and a compile-time error occurs (this situation can only occur for a member lookup in an interface that has multiple direct base interfaces).

For member lookups in types other than interfaces, and member lookups in interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effect of the lookup rules is simply that derived members hide base members with the same name or signature. Such single-inheritance lookups are never ambiguous. The ambiguities that can possibly arise from member lookups in multiple-inheritance interfaces are described in §13.2.5.


7.3.1 Base types

For purposes of member lookup, a type T is considered to have the following base types:

  • If T is object, then T has no base type.
  • If T is a value-type, the base type of T is the class type object.
  • If T is a class-type, the base types of T are the base classes of T, including the class type object.
  • If T is an interface-type, the base types of T are the base interfaces of T and the class type object.
  • If T is an array-type, the base types of T are the class types System.Array and object.
  • If T is a delegate-type, the base types of T are the class types System.Delegate and object.


7.4 Function members

Function members are members that contain executable statements. Function members are always members of types and cannot be members of namespaces. C# defines the following five categories of function members:

  • Constructors
  • Methods
  • Properties
  • Indexers
  • User-defined operators

The statements contained in function members are executed through function member invocations. The actual syntax for writing a function member invocation depends on the particular function member category. However, all function member invocations are expressions, allow arguments to be passed to the function member, and allow the function member to compute and return a result.

The argument list (§7.4.1) of a function member invocation provides actual values or variable references for the parameters of the function member.

Invocations of constructors, methods, indexers, and operators employ overload resolution to determine which of a candidate set of function members to invoke. This process is described in §7.4.2.

Once a particular function member has been identified at compile-time, possibly through overload resolution, the actual run-time process of invoking the function member is described in §7.4.3.

The following table summarizes the processing that takes place in constructs involving the five categories of function members. In the table, e, x, y, and value indicate expressions classified as variables or values, T indicates an expression classified as a type, F is the simple name of a method, and P is the simple name of a property.

Construct | Example | Description
Constructor invocation | new T(x, y) | Overload resolution is applied to select the best constructor in the class or struct T. The constructor is invoked with the argument list (x, y).
Method invocation | F(x, y) | Overload resolution is applied to select the best method F in the containing class or struct. The method is invoked with the argument list (x, y). If the method is not static, the instance expression is this.
 | T.F(x, y) | Overload resolution is applied to select the best method F in the class or struct T. An error occurs if the method is not static. The method is invoked with the argument list (x, y).
 | e.F(x, y) | Overload resolution is applied to select the best method F in the class, struct, or interface given by the type of e. An error occurs if the method is static. The method is invoked with the instance expression e and the argument list (x, y).
Property access | P | The get accessor of the property P in the containing class or struct is invoked. An error occurs if P is write-only. If P is not static, the instance expression is this.
 | P = value | The set accessor of the property P in the containing class or struct is invoked with the argument list (value). An error occurs if P is read-only. If P is not static, the instance expression is this.
 | T.P | The get accessor of the property P in the class or struct T is invoked. An error occurs if P is not static or if P is write-only.
 | T.P = value | The set accessor of the property P in the class or struct T is invoked with the argument list (value). An error occurs if P is not static or if P is read-only.
 | e.P | The get accessor of the property P in the class, struct, or interface given by the type of e is invoked with the instance expression e. An error occurs if P is static or if P is write-only.
 | e.P = value | The set accessor of the property P in the class, struct, or interface given by the type of e is invoked with the instance expression e and the argument list (value). An error occurs if P is static or if P is read-only.
Indexer access | e[x, y] | Overload resolution is applied to select the best indexer in the class, struct, or interface given by the type of e. The get accessor of the indexer is invoked with the instance expression e and the argument list (x, y). An error occurs if the indexer is write-only.
 | e[x, y] = value | Overload resolution is applied to select the best indexer in the class, struct, or interface given by the type of e. The set accessor of the indexer is invoked with the instance expression e and the argument list (x, y, value). An error occurs if the indexer is read-only.
Operator invocation | -x | Overload resolution is applied to select the best unary operator in the class or struct given by the type of x. The selected operator is invoked with the argument list (x).
 | x + y | Overload resolution is applied to select the best binary operator in the classes or structs given by the types of x and y. The selected operator is invoked with the argument list (x, y).


7.4.1 Argument lists

Every function member invocation includes an argument list which provides actual values or variable references for the parameters of the function member. The syntax for specifying the argument list of a function member invocation depends on the function member category:

  • For constructors, methods, and delegates, the arguments are specified as an argument-list, as described below.
  • For properties, the argument list is empty when invoking the get accessor, and consists of the expression specified as the right operand of the assignment operator when invoking the set accessor.
  • For indexers, the argument list consists of the expressions specified between the square brackets in the indexer access. When invoking the set accessor, the argument list additionally includes the expression specified as the right operand of the assignment operator.
  • For user-defined operators, the argument list consists of the single operand of the unary operator or the two operands of the binary operator.

The arguments of properties, indexers, and user-defined operators are always passed as value parameters (§10.5.1.1). Reference and output parameters are not supported for these categories of function members.

The arguments of a constructor, method, or delegate invocation are specified as an argument-list:

argument-list:
argument
argument-list , argument
argument:
expression
ref variable-reference
out variable-reference

An argument-list consists of zero or more arguments, separated by commas. Each argument can take one of the following forms:

  • An expression, indicating that the argument is passed as a value parameter (§10.5.1.1).
  • The keyword ref followed by a variable-reference (§5.4), indicating that the argument is passed as a reference parameter (§10.5.1.2). A variable must be definitely assigned (§5.3) before it can be passed as a reference parameter.
  • The keyword out followed by a variable-reference (§5.4), indicating that the argument is passed as an output parameter (§10.5.1.3). A variable is considered definitely assigned (§5.3) following a function member invocation in which the variable is passed as an output parameter.

During the run-time processing of a function member invocation (§7.4.3), the expressions or variable references of an argument list are evaluated in order, from left to right, as follows:

  • For a value parameter, the argument expression is evaluated and an implicit conversion (§6.1) to the corresponding parameter type is performed. The resulting value becomes the initial value of the value parameter in the function member invocation.
  • For a reference or output parameter, the variable reference is evaluated and the resulting storage location becomes the storage location represented by the parameter in the function member invocation. If the variable reference given as a reference or output parameter is an array element of a reference-type, a run-time check is performed to ensure that element type of the array is identical to the type of the parameter. If this check fails, an ArrayTypeMismatchException is thrown.

The expressions of an argument list are always evaluated in the order they are written. Thus, the example

class Test
{
   static void F(int x, int y, int z) {
      Console.WriteLine("x = {0}, y = {1}, z = {2}", x, y, z);
   }
   static void Main() {
      int i = 0;
      F(i++, i++, i++);
   }
}

produces the output

x = 0, y = 1, z = 2

The array co-variance rules (§12.5) permit a value of an array type A[] to be a reference to an instance of an array type B[], provided an implicit reference conversion exists from B to A. Because of these rules, when an array element of a reference-type is passed as a reference or output parameter, a run-time check is required to ensure that the actual element type of the array is identical to that of the parameter. In the example

class Test
{
   static void F(ref object x) {...}
   static void Main() {
      object[] a = new object[10];
      object[] b = new string[10];
      F(ref a[0]);      // Ok
      F(ref b[1]);      // ArrayTypeMismatchException
   }
}

the second invocation of F causes an ArrayTypeMismatchException to be thrown because the actual element type of b is string and not object.


7.4.2 Overload resolution

Overload resolution is a mechanism for selecting the best function member to invoke given an argument list and a set of candidate function members. Overload resolution selects the function member to invoke in the following distinct contexts within C#:

  • Invocation of a method named in an invocation-expression (§7.5.5).
  • Invocation of a constructor named in an object-creation-expression (§7.5.10.1).
  • Invocation of an indexer accessor through an element-access (§7.5.6).
  • Invocation of a predefined or user-defined operator referenced in an expression (§7.2.3 and §7.2.4).

Each of these contexts defines the set of candidate function members and the list of arguments in its own unique way. However, once the candidate function members and the argument list have been identified, the selection of the best function member is the same in all cases:

  • First, the set of candidate function members is reduced to those function members that are applicable with respect to the given argument list (§7.4.2.1). If this reduced set is empty, an error occurs.
  • Then, given the set of applicable candidate function members, the best function member in that set is located. If the set contains only one function member, then that function member is the best function member. Otherwise, the best function member is the one function member that is better than all other function members with respect to the given argument list, provided that each function member is compared to all other function members using the rules in §7.4.2.2. If there is not exactly one function member that is better than all other function members, then the function member invocation is ambiguous and an error occurs.

The following sections define the exact meanings of the terms applicable function member and better function member.


7.4.2.1 Applicable function member

A function member is said to be an applicable function member with respect to an argument list A when all of the following are true:

  • The number of arguments in A is identical to the number of parameters in the function member declaration.
  • For each argument in A, the parameter passing mode of the argument is identical to the parameter passing mode of the corresponding parameter, and
    • for an input parameter, an implicit conversion (§6.1) exists from the type of the argument to the type of the corresponding parameter, or
    • for a ref or out parameter, the type of the argument is identical to the type of the corresponding parameter.


7.4.2.2 Better function member

Given an argument list A with a set of argument types A1, A2, ..., AN and two applicable function members MP and MQ with parameter types P1, P2, ..., PN and Q1, Q2, ..., QN, MP is defined to be a better function member than MQ if

  • for each argument, the implicit conversion from AX to PX is not worse than the implicit conversion from AX to QX, and
  • for at least one argument, the conversion from AX to PX is better than the conversion from AX to QX.


7.4.2.3 Better conversion

Given an implicit conversion C1 that converts from a type S to a type T1, and an implicit conversion C2 that converts from a type S to a type T2, the better conversion of the two conversions is determined as follows:

  • If T1 and T2 are the same type, neither conversion is better.
  • If S is T1, C1 is the better conversion.
  • If S is T2, C2 is the better conversion.
  • If an implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 exists, C1 is the better conversion.
  • If an implicit conversion from T2 to T1 exists, and no implicit conversion from T1 to T2 exists, C2 is the better conversion.
  • If T1 is sbyte and T2 is byte, ushort, uint, or ulong, C1 is the better conversion.
  • If T2 is sbyte and T1 is byte, ushort, uint, or ulong, C2 is the better conversion.
  • If T1 is short and T2 is ushort, uint, or ulong, C1 is the better conversion.
  • If T2 is short and T1 is ushort, uint, or ulong, C2 is the better conversion.
  • If T1 is int and T2 is uint, or ulong, C1 is the better conversion.
  • If T2 is int and T1 is uint, or ulong, C2 is the better conversion.
  • If T1 is long and T2 is ulong, C1 is the better conversion.
  • If T2 is long and T1 is ulong, C2 is the better conversion.
  • Otherwise, neither conversion is better.

If an implicit conversion C1 is defined by these rules to be a better conversion than an implicit conversion C2, then it is also the case that C2 is a worse conversion than C1.


7.4.3 Function member invocation

This section describes the process that takes place at run-time to invoke a particular function member. It is assumed that a compile-time process has already determined the particular member to invoke, possibly by applying overload resolution to a set of candidate function members.

For purposes of describing the invocation process, function members are divided into two categories:

  • Static function members. These are static methods, constructors, static property accessors, and user-defined operators. Static function members are always non-virtual.
  • Instance function members. These are instance methods, instance property accessors, and indexer accessors. Instance function members are either non-virtual or virtual, and are always invoked on a particular instance. The instance is computed by an instance expression, and it becomes accessible within the function member as this (§7.5.7).

The run-time processing of a function member invocation consists of the following steps, where M is the function member and, if M is an instance member, E is the instance expression:

  • If M is a static function member:
    • The argument list is evaluated as described in §7.4.1.
    • M is invoked.
  • If M is an instance function member declared in a value-type:
    • E is evaluated. If this evaluation causes an exception, then no further steps are executed.
    • If E is not classified as a variable, then a temporary local variable of E’s type is created and the value of E is assigned to that variable. E is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as this within M, but not in any other way. Thus, only when E is a true variable is it possible for the caller to observe the changes that M makes to this.
    • The argument list is evaluated as described in §7.4.1.
    • M is invoked. The variable referenced by E becomes the variable referenced by this.
  • If M is an instance function member declared in a reference-type:
    • E is evaluated. If this evaluation causes an exception, then no further steps are executed.
    • The argument list is evaluated as described in §7.4.1.
    • If the type of E is a value-type, a boxing conversion (§4.3.1) is performed to convert E to type object, and E is considered to be of type object in the following steps.
    • The value of E is checked to be valid. If the value of E is null, a NullReferenceException is thrown and no further steps are executed.
    • The function member implementation to invoke is determined: If M is a non-virtual function member, then M is the function member implementation to invoke. Otherwise, M is a virtual function member and the function member implementation to invoke is determined through virtual function member lookup (§7.4.4) or interface function member lookup (§7.4.5).
    • The function member implementation determined in the step above is invoked. The object referenced by E becomes the object referenced by this.


7.4.3.1 Invocations on boxed instances

A function member implemented in a value-type can be invoked through a boxed instance of that value-type in the following situations:

  • When the function member is an override of a method inherited from type object and is invoked through an instance expression of type object.
  • When the function member is an implementation of an interface function member and is invoked through an instance expression of an interface-type.
  • When the function member is invoked through a delegate.

In these situations, the boxed instance is considered to contain a variable of the value-type, and this variable becomes the variable referenced by this within the function member invocation. This in particular means that when a function member is invoked on a boxed instance, it is possible for the function member to modify the value contained in the boxed instance.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

7.4.4 Virtual function member lookup

© 2000 Microsoft Corporation. All rights reserved.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

7.4.5 Interface function member lookup

© 2000 Microsoft Corporation. All rights reserved.


7.5 Primary expressions

primary-expression:
literal
simple-name
parenthesized-expression
member-access
invocation-expression
element-access
this-access
base-access
post-increment-expression
post-decrement-expression
new-expression
typeof-expression
sizeof-expression
checked-expression
unchecked-expression


7.5.1 Literals

A primary-expression that consists of a literal (§2.5.3) is classified as a value. The type of the value depends on the literal as follows:

  • A boolean-literal is of type bool. There are two possible boolean-literals, true and false.
  • An integer-literal is of type int, uint, long, or ulong, as determined by the value of the literal and by the presence or absence of a type suffix (§2.5.3.2).
  • A real-literal is of type float, double, or decimal, as determined by the presence or absence of a type suffix (§2.5.3.3).
  • A character-literal is of type char.
  • A string-literal is of type string.
  • The null-literal is of the null type.


7.5.2 Simple names

An simple-name consists of a single identifier.

simple-name:
identifier

A simple-name is evaluated and classified as follows:

  • If the simple-name appears within a block and if the block contains a local variable or parameter with the given name, then the simple-name refers to that local variable or parameter and is classified as a variable.
  • Otherwise, for each type T, starting with the immediately enclosing class, struct, or enumeration declaration and continuing with each enclosing outer class or struct declaration (if any), if a member lookup of the simple-name in T produces a match:
    • If T is the immediately enclosing class or struct type and the lookup identifies one or more methods, the result is a method group with an associated instance expression of this.
    • If T is the immediately enclosing class or struct type, if the lookup identifies an instance member, and if the reference occurs within the block of a constructor, an instance method, or an instance accessor, the result is exactly the same as a member access (§7.5.4) of the form this.E, where E is the simple-name.
    • Otherwise, the result is exactly the same as a member access (§7.5.4) of the form T.E, where E is the simple-name. In this case, it is an error for the simple-name to refer to an instance member.
  • Otherwise, starting with the namespace declaration in which the simple-name occurs (if any), continuing with each enclosing namespace declaration (if any), and ending with the global namespace, the following steps are evaluated until an entity is located:
    • If the namespace contains a namespace member with the given name, then the simple-name refers to that member and, depending on the member, is classified as a namespace or a type.
    • Otherwise, if the namespace declaration contains a using-alias-directive that associates the given name with an imported namespace or type, then the simple-name refers to that namespace or type.
    • Otherwise, if the namespaces imported by the using-namespace-directives of the namespace declaration contain exactly one type with the given name, then the simple-name refers to that type.
    • Otherwise, if the namespaces imported by the using-namespace-directives of the namespace declaration contain more than one type with the given name, then the simple-name is ambiguous and an error occurs.
  • Otherwise, the name given by the simple-name is undefined and an error occurs.


7.5.2.1 Invariant meaning in blocks

For each occurrence of a given identifier as a simple-name in an expression, every other occurrence of the same identifier as a simple-name in an expression within the immediately enclosing block (§8.2) or switch-block (§8.7.2) must refer to the same entity. This rule ensures that the meaning of an name in the context of an expression is always the same within a block.

The example

class Test
{
   double x;
   void F(bool b) {
      x = 1.0;
      if (b) {
         int x = 1;
      }
   }
}

is in error because x refers to different entities within the outer block (the extent of which includes the nested block in the if statement). In contrast, the example

class Test
{
   double x;
   void F(bool b) {
      if (b) {
         x = 1.0;
      }
      else {
         int x = 1;
      }
   }
}

is permitted because the name x is never used in the outer block.

Note that the rule of invariant meaning applies only to simple names. It is perfectly valid for the same identifier to have one meaning as a simple name and another meaning as right operand of a member access (§7.5.4). For example:

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

The example above illustrates a common pattern of using the names of fields as parameter names in a constructor. In the example, the simple names x and y refer to the parameters, but that does not prevent the member access expressions this.x and this.y from accessing the fields.


7.5.3 Parenthesized expressions

A parenthesized-expression consists of an expression enclosed in parentheses.

parenthesized-expression:
( expression )

A parenthesized-expression is evaluated by evaluating the expression within the parentheses. If the expression within the parentheses denotes a namespace, type, or method group, an error occurs. Otherwise, the result of the parenthesized-expression is the result of the evaluation of the contained expression.


7.5.4 Member access

A member-access consists of a primary-expression or a predefined-type, followed by a "." token, followed by an identifier.

member-access:
primary-expression . identifier
predefined-type . identifier
predefined-type: one of
bool byte char decimal double float int long
object sbyte short string uint ulong ushort

A member-access of the form E.I, where E is a primary-expression or a predefined-type and I is an identifier, is evaluated and classified as follows:

  • If E is a namespace and I is the name of an accessible member of that namespace, then the result is that member and, depending on the member, is classified as a namespace or a type.
  • If E is a predefined-type or a primary-expression classified as a type, and a member lookup (§7.3) of I in E produces a match, then E.I is evaluated and classified as follows:
    • If I identifies a type, then the result is that type.
    • If I identifies one or more methods, then the result is a method group with no associated instance expression.
    • If I identifies a static property, then the result is a property access with no associated instance expression.
    • If I identifies a static field:
      • If the field is readonly and the reference occurs outside the static constructor of the class or struct in which the field is declared, then the result is a value, namely the value of the static field I in E.
      • Otherwise, the result is a variable, namely the static field I in E.
    • If I identifies a static event:
      • If the reference occurs within the class or struct in which the event is declared, then E.I is processed exactly as if I was a static field or property.
      • Otherwise, the result is an event access with no associated instance expression.
    • If I identifies a constant, then the result is a value, namely the value of that constant.
    • If I identifies an enumeration member, then the result is a value, namely the value of that enumeration member.
    • Otherwise, E.I is an invalid member reference, and an error occurs.
  • If E is a property access, indexer access, variable, or value, the type of which is T, and a member lookup (§7.3) of I in T produces a match, then E.I is evaluated and classified as follows:
    • First, if E is a property or indexer access, then the value of the property or indexer access is obtained (§7.1.1) and E is reclassified as a value.
    • If I identifies one or more methods, then the result is a method group with an associated instance expression of E.
    • If I identifies an instance property, then the result is a property access with an associated instance expression of E.
    • If T is a class-type and I identifies an instance field of that class-type:
      • If the value of E is null, then a NullReferenceException is thrown.
      • Otherwise, if the field is readonly and the reference occurs outside an instance constructor of the class in which the field is declared, then the result is a value, namely the value of the field I in the object referenced by E.
      • Otherwise, the result is a variable, namely the field I in the object referenced by E.
    • If T is a struct-type and I identifies an instance field of that struct-type:
      • If E is a value, or if the field is readonly and the reference occurs outside an instance constructor of the struct in which the field is declared, then the result is a value, namely the value of the field I in the struct instance given by E.
      • Otherwise, the result is a variable, namely the field I in the struct instance given by E.
    • If I identifies an instance event:
      • If the reference occurs within the class or struct in which the event is declared, then E.I is processed exactly as if I was an instance field or property.
      • Otherwise, the result is an event access with an associated instance expression of E.
  • Otherwise, E.I is an invalid member reference, and an error occurs.


7.5.4.1 Identical simple names and type names

In a member access of the form E.I, if E is a single identifier, and if the meaning of E as a simple-name (§7.5.2) is a constant, field, property, local variable, or parameter with the same type as the meaning of E as a type-name (§3.6), then both possible meanings of E are permitted. The two possible meanings of E.I are never ambiguous, since I must necessarily be a member of the type E in both cases. In other words, the rule simply permits access to the static members of E where an error would have otherwise occurred. For example:

struct Color
{
   public static readonly Color White = new Color(...);
   public static readonly Color Black = new Color(...);
   public Color Complement() {...}
}
class A
{
   public Color Color;               // Field Color of type Color
   void F() {
      Color = Color.Black;          // References Color.Black static member
      Color = Color.Complement();   // Invokes Complement() on Color field
   }
   static void G() {
      Color c = Color.White;         // References Color.White static member
   }
}

Within the A class, those occurrences of the Color identifier that reference the Color type are underlined, and those that reference the Color field are not underlined.


7.5.5 Invocation expressions

An invocation-expression is used to invoke a method.

invocation-expression:
primary-expression ( argument-listopt )

The primary-expression of an invocation-expression must be a method group or a value of a delegate-type. If the primary-expression is a method group, the invocation-expression is a method invocation (§7.5.5.1). If the primary-expression is a value of a delegate-type, the invocation-expression is a delegate invocation (§7.5.5.2). If the primary-expression is neither a method group nor a value of a delegate-type, an error occurs.

The optional argument-list (§7.4.1) provides values or variable references for the parameters of the method.

The result of evaluating an invocation-expression is classified as follows:

  • If the invocation-expression invokes a method or delegate that returns void, the result is nothing. An expression that is classified as nothing cannot be an operand of any operator, and is permitted only in the context of a statement-expression (§8.6).
  • Otherwise, the result is a value of the type returned by the method or delegate.


7.5.5.1 Method invocations

For a method invocation, the primary-expression of the invocation-expression must be a method group. The method group identifies the one method to invoke or the set of overloaded methods from which to choose a specific method to invoke. In the latter case, determination of the specific method to invoke is based on the context provided by the types of the arguments in the argument-list.

The compile-time processing of a method invocation of the form M(A), where M is a method group and A is an optional argument-list, consists of the following steps:

  • The set of candidate methods for the method invocation is constructed. Starting with the set of methods associated with M, which were found by a previous member lookup (§7.3), the set is reduced to those methods that are applicable with respect to the argument list A. The set reduction consists of applying the following rules to each method T.N in the set, where T is the type in which the method N is declared:
    • If N is not applicable with respect to A (§7.4.2.1), then N is removed from the set.
    • If N is applicable with respect to A (§7.4.2.1), then all methods declared in a base type of T are removed from the set.
  • If the resulting set of candidate methods is empty, then no applicable methods exist, and an error occurs. If the candidate methods are not all declared in the same type, the method invocation is ambiguous, and an error occurs (this latter situation can only occur for an invocation of a method in an interface that has multiple direct base interfaces, as described in §13.2.5).
  • The best method of the set of candidate methods is identified using the overload resolution rules of §7.4.2. If a single best method cannot be identified, the method invocation is ambiguous, and an error occurs.
  • Given a best method, the invocation of the method is validated in the context of the method group: If the best method is a static method, the method group must have resulted from a simple-name or a member-access through a type. If the best method is an instance method, the method group must have resulted from a simple-name, a member-access through a variable or value, or a base-access. If neither of these requirements are true, a compile-time error occurs.

Once a method has been selected and validated at compile-time by the above steps, the actual run-time invocation is processed according to the rules of function member invocation described in §7.4.3.

The intuitive effect of the resolution rules described above is as follows: To locate the particular method invoked by a method invocation, start with the type indicated by the method invocation and proceed up the inheritance chain until at least one applicable, accessible, non-override method declaration is found. Then perform overload resolution on the set of applicable, accessible, non-override methods declared in that type and invoke the method thus selected.


7.5.5.2 Delegate invocations

For a delegate invocation, the primary-expression of the invocation-expression must be a value of a delegate-type. Furthermore, considering the delegate-type to be a function member with the same parameter list as the delegate-type, the delegate-type must be applicable (§7.4.2.1) with respect to the argument-list of the invocation-expression.

The run-time processing of a delegate invocation of the form D(A), where D is a primary-expression of a delegate-type and A is an optional argument-list, consists of the following steps:

  • D is evaluated. If this evaluation causes an exception, no further steps are executed.
  • The value of D is checked to be valid. If the value of D is null, a NullReferenceException is thrown and no further steps are executed.
  • Otherwise, D is reference to a delegate instance. A function member invocation (§7.4.3) is performed on the method referenced by the delegate. If the method is an instance method, the instance of the invocation becomes the instance referenced by the delegate.


7.5.6 Element access

An element-access consists of a primary-expression, followed by a "[" token, followed by an expression-list, followed by a "]" token. The expression-list consists of one or more expressions, separated by commas.

element-access:
primary-expression [ expression-list ]
expression-list:
expression
expression-list , expression

If the primary-expression of an element-access is a value of an array-type, the element-access is an array access (§7.5.6.1). Otherwise, the primary-expression must be a variable or value of a class, struct, or interface type that has one or more indexer members, and the element-access is then an indexer access (§7.5.6.2).


7.5.6.1 Array access

For an array access, the primary-expression of the element-access must be a value of an array-type. The number of expressions in the expression-list must be the same as the rank of the array-type, and each expression must be of type int or of a type that can be implicitly converted to int.

The result of evaluating an array access is a variable of the element type of the array, namely the array element selected by the value(s) of the expression(s) in the expression-list.

The run-time processing of an array access of the form P[A], where P is a primary-expression of an array-type and A is an expression-list, consists of the following steps:

  • P is evaluated. If this evaluation causes an exception, no further steps are executed.
  • The index expressions of the expression-list are evaluated in order, from left to right. Following evaluation of each index expression, an implicit conversion (§6.1) to type int is performed. If evaluation of an index expression or the subsequent implicit conversion causes an exception, then no further index expressions are evaluated and no further steps are executed.
  • The value of P is checked to be valid. If the value of P is null, a NullReferenceException is thrown and no further steps are executed.
  • The value of each expression in the expression-list is checked against the actual bounds of each dimension of the array instance referenced by P. If one or more values are out of range, an IndexOutOfRangeException is thrown and no further steps are executed.
  • The location of the array element given by the index expression(s) is computed, and this location becomes the result of the array access.


7.5.6.2 Indexer access

For an indexer access, the primary-expression of the element-access must be a variable or value of a class, struct, or interface type, and this type must implement one or more indexers that are applicable with respect to the expression-list of the element-access.

The compile-time processing of an indexer access of the form P[A], where P is a primary-expression of a class, struct, or interface type T, and A is an expression-list, consists of the following steps:

  • The set of indexers provided by T is constructed. The set consists of all indexers declared in T or a base type of T that are not override declarations and are accessible in the current context (§3.3).
  • The set is reduced to those indexers that are applicable and not hidden by other indexers. The following rules are applied to each indexer S.I in the set, where S is the type in which the indexer I is declared:
    • If I is not applicable with respect to A (§7.4.2.1), then I is removed from the set.
    • If I is applicable with respect to A (§7.4.2.1), then all indexers declared in a base type of S are removed from the set.
  • If the resulting set of candidate indexers is empty, then no applicable indexers exist, and an error occurs. If the candidate indexers are not all declared in the same type, the indexer access is ambiguous, and an error occurs (this latter situation can only occur for an indexer access on an instance of an interface that has multiple direct base interfaces).
  • The best indexer of the set of candidate indexers is identified using the overload resolution rules of §7.4.2. If a single best indexer cannot be identified, the indexer access is ambiguous, and an error occurs.
  • The result of processing the indexer access is an expression classified as an indexer access. The indexer access expression references the indexer determined in the step above, and has an associated instance expression of P and an associated argument list of A.

Depending on the context in which it is used, an indexer access causes invocation of either the get-accessor or the set-accessor of the indexer. If the indexer access is the target of an assignment, the set-accessor is invoked to assign a new value (§7.13.1). In all other cases, the get-accessor is invoked to obtain the current value (§7.1.1).


7.5.6.3 String indexing

The string class implements an indexer that allows the individual characters of a string to be accessed. The indexer of the string class has the following declaration:

public char this[int index] { get; }

In other words, a read-only indexer that takes a single argument of type int and returns an element of type char. Values passed for the index argument must be greater than or equal to zero and less than the length of the string.


7.5.7 This access

A this-access consists of the reserved word this.

this-access:
this

A this-access is permitted only in the block of a constructor, an instance method, or an instance accessor. It has one of the following meanings:

  • When this is used in a primary-expression within a constructor of a class, it is classified as a value. The type of the value is the class within which the reference occurs, and the value is a reference to the object being constructed.
  • When this is used in a primary-expression within an instance method or instance accessor of a class, it is classified as a value. The type of the value is the class within which the reference occurs, and the value is a reference to the object for which the method or accessor was invoked.
  • When this is used in a primary-expression within a constructor of a struct, it is classified as a variable. The type of the variable is the struct within which the reference occurs, and the variable represents the struct being constructed. The this variable of a constructor of a struct behaves exactly the same as an out parameter of the struct type—this in particular means that the variable must be definitely assigned in every execution path of the constructor.
  • When this is used in a primary-expression within an instance method or instance accessor of a struct, it is classified as a variable. The type of the variable is the struct within which the reference occurs, and the variable represents the struct for which the method or accessor was invoked. The this variable of an instance method of a struct behaves exactly the same as a ref parameter of the struct type.

Use of this in a primary-expression in a context other than the ones listed above is an error. In particular, it is not possible to refer to this in a static method, a static property accessor, or in a variable-initializer of a field declaration.


7.5.8 Base access

A base-access consists of the reserved word base followed by either a "." token and an identifier or an expression-list enclosed in square brackets:

base-access:
base . identifier
base [ expression-list ]

A base-access is used to access base class members that are hidden by similarly named members in the current class or struct. A base-access is permitted only in the block of a constructor, an instance method, or an instance accessor. When base.I occurs in a class or struct, I must denote a member of the base class of that class or struct. Likewise, when base[E] occurs in a class, an applicable indexer must exist in the base class.

At compile-time, base-access expressions of the form base.I and base[E] are evaluated exactly as if they were written ((B)this).I and ((B)this)[E], where B is the base class of the class or struct in which the construct occurs. Thus, base.I and base[E] correspond to this.I and this[E], except this is viewed as an instance of the base class.

When a base-access references a function member (a method, property, or indexer), the function member is considered non-virtual for purposes of function member invocation (§7.4.3). Thus, within an override of a virtual function member, a base-access can be used to invoke the inherited implementation of the function member. If the function member referenced by a base-access is abstract, an error occurs.


7.5.9 Postfix increment and decrement operators

post-increment-expression:
primary-expression ++
post-decrement-expression:
primary-expression --

The operand of a postfix increment or decrement operation must be an expression classified as a variable, a property access, or an indexer access. The result of the operation is a value of the same type as the operand.

If the operand of a postfix increment or decrement operation is a property or indexer access, the property or indexer must have both a get and a set accessor. If this is not the case, a compile-time error occurs.

Unary operator overload resolution (§7.2.3) is applied to select a specific operator implementation. Predefined ++ and -- operators exist for the following types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, and any enum type. The predefined ++ operators return the value produced by adding 1 to the argument, and the predefined -- operators return the value produced by subtracting 1 from the argument.

The run-time processing of a postfix increment or decrement operation of the form x++ or x-- consists of the following steps:

  • If x is classified as a variable:
    • x is evaluated to produce the variable.
    • The value of x is saved.
    • The selected operator is invoked with the saved value of x as its argument.
    • The value returned by the operator is stored in the location given by the evaluation of x.
    • The saved value of x becomes the result of the operation.
  • If x is classified as a property or indexer access:
    • The instance expression (if x is not static) and the argument list (if x is an indexer access) associated with x are evaluated, and the results are used in the subsequent get and set accessor invocations.
    • The get accessor of x is invoked and the returned value is saved.
    • The selected operator is invoked with the saved value of x as its argument.
    • The set accessor of x is invoked with the value returned by the operator as its value argument.
    • The saved value of x becomes the result of the operation.

The ++ and -- operators also support prefix notation, as described in §7.6.7. The result of x++ or x-- is the value of x before the operation, whereas the result of ++x or --x is the value of x after the operation. In either case, x itself has the same value after the operation.

An operator ++ or operator -- implementation can be invoked using either postfix and prefix notation. It is not possible to have separate operator implementations for the two notations.


7.5.10 new operator

The new operator is used to create new instances of types.

new-expression:
object-creation-expression
array-creation-expression
delegate-creation-expression

There are three forms of new expressions:

  • Object creation expressions are used to create a new instances of class types and value types.
  • Array creation expressions are used to create new instances of array types.
  • Delegate creation expressions are used to create new instances of delegate types.

The new operator implies creation of an instance of a type, but does not necessarily imply dynamic allocation of memory. In particular, instances of value types require no additional memory beyond the variables in which they reside, and no dynamic allocations occur when new is used to create instances of value types.


7.5.10.1 Object creation expressions

An object-creation-expression is used to create a new instance of a class-type or a value-type.

object-creation-expression:
new type ( argument-listopt )

The type of an object-creation-expression must be a class-type or a value-type. The type cannot be an abstract class-type.

The optional argument-list (§7.4.1) is permitted only if the type is a class-type or a struct-type.

The compile-time processing of an object-creation-expression of the form new T(A), where T is a class-type or a value-type and A is an optional argument-list, consists of the following steps:

  • If T is a value-type and A is not present:
    • The object-creation-expression is a default constructor invocation. The result of the object-creation-expression is a value of type T, namely the default value for T as defined in §4.1.1.
  • Otherwise, if T is a class-type or a struct-type:
    • If T is an abstract class-type, an error occurs.
    • The constructor to invoke is determined using the overload resolution rules of §7.4.2. The set of candidate constructors consists of all accessible constructors declared in T. If the set of candidate constructors is empty, or if a single best constructor cannot be identified, an error occurs.
    • The result of the object-creation-expression is a value of type T, namely the value produced by invoking the constructor determined in the step above.
  • Otherwise, the object-creation-expression is invalid, and an error occurs.

The run-time processing of an object-creation-expression of the form new T(A), where T is class-type or a struct-type and A is an optional argument-list, consists of the following steps:

  • If T is a class-type:
    • A new instance of class T is allocated. If there is not enough memory available to allocate the new instance, an OutOfMemoryException is thrown and no further steps are executed.
    • All fields of the new instance are initialized to their default values (§5.2).
    • The constructor is invoked according to the rules of function member invocation (§7.4.3). A reference to the newly allocated instance is automatically passed to the constructor and the instance can be accessed from within the constructor as this.
  • If T is a struct-type:
    • An instance of type T is created by allocating a temporary local variable. Since a constructor of a struct-type is required to definitely assign a value to each field of the instance being created, no initialization of the temporary variable is necessary.
    • The constructor is invoked according to the rules of function member invocation (§7.4.3). A reference to the newly allocated instance is automatically passed to the constructor and the instance can be accessed from within the constructor as this.


7.5.10.2 Array creation expressions

An array-creation-expression is used to create a new instance of an array-type.

array-creation-expression:
new non-array-type [ expression-list ] rank-specifiersopt array-initializeropt
new array-type array-initializer

An array creation expression of first form allocates an array instance of the type that results from deleting each of the individual expressions from the expression list. For example, the array creation expression new int[10, 20] produces an array instance of type int[,], and the array creation expression new int[10][,] produces an array of type int[][,]. Each expression in the expression list must be of type int or of a type that can be implicitly converted to int. The value of each expression determines the length of the corresponding dimension in the newly allocated array instance.

If an array creation expression of the first form includes an array initializer, each expression in the expression list must be a constant and the rank and dimension lengths specified by the expression list must match those of the array initializer.

In an array creation expression of the second form, the rank of the specified array type must match that of the array initializer. The individual dimension lengths are inferred from the number of elements in each of the corresponding nesting levels of the array initializer. Thus, the expression

new int[,] {{0, 1}, {2, 3}, {4, 5}};

exactly corresponds to

new int[3, 2] {{0, 1}, {2, 3}, {4, 5}};

Array initializers are further described in §12.6.

The result of evaluating an array creation expression is classified as a value, namely a reference to the newly allocated array instance.

The run-time processing of an array creation expression consists of the following steps:

  • The dimension length expressions of the expression-list are evaluated in order, from left to right. Following evaluation of each expression, an implicit conversion (§6.1) to type int is performed. If evaluation of an expression or the subsequent implicit conversion causes an exception, then no further expressions are evaluated and no further steps are executed.
  • The computed values for the dimension lengths are validated. If one or more of the values are less than zero, an IndexOutOfRangeException is thrown and no further steps are executed.
  • An array instance with the given dimension lengths is allocated. If there is not enough memory available to allocate the new instance, an OutOfMemoryException is thrown and no further steps are executed.
  • All elements of the new array instance are initialized to their default values (§5.2).
  • If the array creation expression contains an array initializer, then each expression in the array initializer is evaluated and assigned to its corresponding array element. The evaluations and assignments are performed in the order the expressions are written in the array initializer—in other words, elements are initialized in increasing index order, with the rightmost dimension increasing first. If evaluation of a given expression or the subsequent assignment to the corresponding array element causes an exception, then no further elements are initialized (and the remaining elements will thus have their default values).

An array creation expression permits instantiation of an array with elements of an array type, but the elements of such an array must be manually initialized. For example, the statement

int[][] a = new int[100][];

creates a single-dimensional array with 100 elements of type int[]. The initial value of each element is null. It is not possible for the same array creation expression to also instantiate the sub-arrays, and the statement

int[][] a = new int[100][5];      // Error

is an error. Instantiation of the sub-arrays must instead be performed manually, as in

int[][] a = new int[100][];
for (int i = 0; i < 100; i++) a[i] = new int[5];

When an array of arrays has a "rectangular" shape, that is when the sub-arrays are all of the same length, it is more efficient to use a multi-dimensional array. In the example above, instantiation of the array of arrays creates 101 objects—one outer array and 100 sub-arrays. In contrast,

int[,] = new int[100, 5];

creates only a single object, a two-dimensional array, and accomplishes the allocation in a single statement.


7.5.10.3 Delegate creation expressions

A delegate-creation-expression is used to create a new instance of a delegate-type.

delegate-creation-expression:
new delegate-type ( expression )

The argument of a delegate creation expression must be a method group or a value of a delegate-type. If the argument is a method group, it identifies the method and, for an instance method, the object for which to create a delegate. If the argument is a value of a delegate-type, it identifies a delegate instance of which to create a copy.

The compile-time processing of a delegate-creation-expression of the form new D(E), where D is a delegate-type and E is an expression, consists of the following steps:

  • If E is a method group:
    • If the method group resulted from a base-access, an error occurs.
    • The set of methods identified by E must include exactly one method with precisely the same signature and return type as those of D, and this becomes the method to which the newly created delegate refers. If no matching method exists, or if more than one matching methods exists, an error occurs. If the selected method is an instance method, the instance expression associated with E determines the target object of the delegate.
    • As in a method invocation, the selected method must be compatible with the context of the method group: If the method is a static method, the method group must have resulted from a simple-name or a member-access through a type. If the method is an instance method, the method group must have resulted from a simple-name or a member-access through a variable or value. If the selected method does not match the context of the method group, an error occurs.
    • The result is a value of type D, namely a newly created delegate that refers to the selected method and target object.
  • Otherwise, if E is a value of a delegate-type:
    • The delegate-type of E must have the exact same signature and return type as D, or otherwise an error occurs.
    • The result is a value of type D, namely a newly created delegate that refers to the same method and target object as E.
  • Otherwise, the delegate creation expression is invalid, and an error occurs.

The run-time processing of a delegate-creation-expression of the form new D(E), where D is a delegate-type and E is an expression, consists of the following steps:

  • If E is a method group:
    • If the method selected at compile-time is a static method, the target object of the delegate is null. Otherwise, the selected method is an instance method, and the target object of the delegate is determined from the instance expression associated with E:
      • The instance expression is evaluated. If this evaluation causes an exception, no further steps are executed.
      • If the instance expression is of a reference-type, the value computed by the instance expression becomes the target object. If the target object is null, a NullReferenceException is thrown and no further steps are executed.
      • If the instance expression is of a value-type, a boxing operation (§4.3.1) is performed to convert the value to an object, and this object becomes the target object.
    • A new instance of the delegate type D is allocated. If there is not enough memory available to allocate the new instance, an OutOfMemoryException is thrown and no further steps are executed.
    • The new delegate instance is initialized with a reference to the method that was determined at compile-time and a reference to the target object computed above.
  • If E is a value of a delegate-type:
    • E is evaluated. If this evaluation causes an exception, no further steps are executed.
    • If the value of E is null, a NullReferenceException is thrown and no further steps are executed.
    • A new instance of the delegate type D is allocated. If there is not enough memory available to allocate the new instance, an OutOfMemoryException is thrown and no further steps are executed.
    • The new delegate instance is initialized with references to the same method and object as the delegate instance given by E.

The method and object to which a delegate refers are determined when the delegate is instantiated and then remain constant for the entire lifetime of the delegate. In other words, it is not possible to change the target method or object of a delegate once it has been created.

It is not possible to create a delegate that refers to a constructor, property, indexer, or user-defined operator.

As described above, when a delegate is created from a method group, the signature and return type of the delegate determine which of the overloaded methods to select. In the example

delegate double DoubleFunc(double x);
class A
{
   DoubleFunc f = new DoubleFunc(Square);
   static float Square(float x) {
      return x * x;
   }
   static double Square(double x) {
      return x * x;
   }
}

the A.f field is initialized with a delegate that refers to the second Square method because that method exactly matches the signature and return type of DoubleFunc. Had the second Square method not been present, a compile-time error would have occurred.


7.5.11 typeof operator

The typeof operator is used to obtain the System.Type object for a type.

typeof-expression:
typeof ( type )

The result of a typeof-expression is the System.Type object for the indicated type.

The example

class Test
{
   static void Main() {
      Type[] t = {
         typeof(int),
         typeof(System.Int32),
         typeof(string),
         typeof(double[])
      };
      for (int i = 0; i < t.Length; i++) {
         Console.WriteLine(t[i].Name);
      }
   }
}

produces the following output:

Int32
Int32
String
Double[]

Note that int and System.Int32 are the same type.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

7.5.12 sizeof operator

sizeof-expression:
sizeof ( type )


7.5.13 checked and unchecked operators

The checked and unchecked operators are used to control the overflow checking context for integral-type arithmetic operations and conversions.

checked-expression:
checked ( expression )
unchecked-expression:
unchecked ( expression )

The checked operator evaluates the contained expression in a checked context, and the unchecked operator evaluates the contained expression in an unchecked context. A checked-expression or unchecked-expression corresponds exactly to a parenthesized-expression (§7.5.3), except that the contained expression is evaluated in the given overflow checking context.

The overflow checking context can also be controlled through the checked and unchecked statements (§8.11).

The following operations are affected by the overflow checking context established by the checked and unchecked operators and statements:

  • The predefined ++ and -- unary operators (§7.5.9 and §7.6.7), when the operand is of an integral type.
  • The predefined - unary operator (§7.6.2), when the operand is of an integral type.
  • The predefined +, -, *, and / binary operators (§7.7), when both operands are of integral types.
  • Explicit numeric conversions (§6.2.1) from one integral type to another integral type.

When one of the above operations produce a result that is too large to represent in the destination type, the context in which the operation is performed controls the resulting behavior:

  • In a checked context, if the operation is a constant expression (§7.15), a compile-time error occurs. Otherwise, when the operation is performed at run-time, an OverflowException is thrown.
  • In an unchecked context, the result is truncated by discarding any high-order bits that do not fit in the destination type.

When a non-constant expression (an expression that is evaluated at run-time) is not enclosed by any checked or unchecked operators or statements, the effect of an overflow during the run-time evaluation of the expression depends on external factors (such as compiler switches and execution environment configuration). The effect is however guaranteed to be either that of a checked evaluation or that of an unchecked evaluation.

For constant expressions (expressions that can be fully evaluated at compile-time), the default overflow checking context is always checked. Unless a constant expression is explicitly placed in an unchecked context, overflows that occur during the compile-time evaluation of the expression always cause compile-time errors.

In the example

class Test
{
   static int x = 1000000;
   static int y = 1000000;
   static int F() {
      return checked(x * y);      // Throws OverflowException
   }
   static int G() {
      return unchecked(x * y);   // Returns -727379968
   }
   static int H() {
      return x * y;               // Depends on default
   }
}

no compile-time errors are reported since neither of the expressions can be evaluated at compile-time. At run-time, the F() method throws an OverflowException, and the G() method returns –727379968 (the lower 32 bits of the out-of-range result). The behavior of the H() method depends on the default overflow checking context for the compilation, but it is either the same as F() or the same as G().

In the example

class Test
{
   const int x = 1000000;
   const int y = 1000000;
   static int F() {
      return checked(x * y);      // Compile error, overflow
   }
   static int G() {
      return unchecked(x * y);   // Returns -727379968
   }
   static int H() {
      return x * y;               // Compile error, overflow
   }
}

the overflows that occur when evaluating the constant expressions in F() and H() cause compile-time errors to be reported because the expressions are evaluated in a checked context. An overflow also occurs when evaluating the constant expression in G(), but since the evaluation takes place in an unchecked context, the overflow is not reported.

The checked and unchecked operators only affect the overflow checking context for those operations that are textually contained within the "(" and ")" tokens. The operators have no effect on function members that are invoked as a result of evaluating the contained expression. In the example

class Test
{
   static int Multiply(int x, int y) {
      return x * y;
   }
   static int F() {
      return checked(Multiply(1000000, 1000000));
   }
}

the use of checked in F() does not affect the evaluation of x * y in Multiply(), and x * y is therefore evaluated in the default overflow checking context.

The unchecked operator is convenient when writing constants of the signed integral types in hexadecimal notation. For example:

class Test
{
   public const int AllBits = unchecked((int)0xFFFFFFFF);
   public const int HighBit = unchecked((int)0x80000000);
}

Both of the hexadecimal constants above are of type uint. Because the constants are outside the int range, without the unchecked operator, the casts to int would produce compile-time errors.


7.6 Unary expressions

unary-expression:
primary-expression
+ unary-expression
- unary-expression
! unary-expression
~ unary-expression
* unary-expression
& unary-expression
pre-increment-expression
pre-decrement-expression
cast-expression


7.6.1 Unary plus operator

For an operation of the form +x, unary operator overload resolution (§7.2.3) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined unary plus operators are:

int operator +(int x);
uint operator +(uint x);
long operator +(long x);
ulong operator +(ulong x);
float operator +(float x);
double operator +(double x);
decimal operator +(decimal x);

For each of these operators, the result is simply the value of the operand.


7.6.2 Unary minus operator

For an operation of the form –x, unary operator overload resolution (§7.2.3) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined negation operators are:

  • Integer negation:
int operator –(int x);
long operator –(long x);

The result is computed by subtracting x from zero. In a checked context, if the value of x is the maximum negative int or long, an OverflowException is thrown. In an unchecked context, if the value of x is the maximum negative int or long, the result is that same value and the overflow is not reported.

If the operand of the negation operator is of type uint, it is converted to type long, and the type of the result is long. An exception is the rule that permits the int value -2147483648 (-231) to be written as a decimal integer literal (§2.5.3.2).

If the operand of the negation operator is of type ulong, an error occurs. An exception is the rule that permits the long value -9223372036854775808 (-263) to be written as decimal integer literal (§2.5.3.2).

  • Floating-point negation:
float operator –(float x);
double operator –(double x);

The result is the value of x with its sign inverted. If x is NaN, the result is also NaN.

  • Decimal negation:
decimal operator –(decimal x);

The result is computed by subtracting x from zero.


7.6.3 Logical negation operator

For an operation of the form !x, unary operator overload resolution (§7.2.3) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. Only one predefined logical negation operator exists:

bool operator !(bool x);

This operator computes the logical negation of the operand: If the operand is true, the result is false. If the operand is false, the result is true.


7.6.4 Bitwise complement operator

For an operation of the form ~x, unary operator overload resolution (§7.2.3) is applied to select a specific operator implementation. The operand is converted to the parameter type of the selected operator, and the type of the result is the return type of the operator. The predefined bitwise complement operators are:

int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);

For each of these operators, the result of the operation is the bitwise complement of x.

Every enumeration type E implicitly provides the following bitwise complement operator:

E operator ~(E x);

The result of evaluating ~x, where x is an expression of an enumeration type E with an underlying type U, is exactly the same as evaluating (E)(~(U)x).


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

7.6.5 Indirection operator

© 2000 Microsoft Corporation. All rights reserved.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

7.6.6 Address operator

© 2000 Microsoft Corporation. All rights reserved.


7.6.7 Prefix increment and decrement operators

pre-increment-expression:
++ unary-expression
pre-decrement-expression:
-- unary-expression

The operand of a prefix increment or decrement operation must be an expression classified as a variable, a property access, or an indexer access. The result of the operation is a value of the same type as the operand.

If the operand of a prefix increment or decrement operation is a property or indexer access, the property or indexer must have both a get and a set accessor. If this is not the case, a compile-time error occurs.

Unary operator overload resolution (§7.2.3) is applied to select a specific operator implementation. Predefined ++ and -- operators exist for the following types: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, and any enum type. The predefined ++ operators return the value produced by adding 1 to the argument, and the predefined -- operators return the value produced by subtracting 1 from the argument.

The run-time processing of a prefix increment or decrement operation of the form ++x or --x consists of the following steps:

  • If x is classified as a variable:
    • x is evaluated to produce the variable.
    • The selected operator is invoked with the value of x as its argument.
    • The value returned by the operator is stored in the location given by the evaluation of x.
    • The value returned by the operator becomes the result of the operation.
  • If x is classified as a property or indexer access:
    • The instance expression (if x is not static) and the argument list (if x is an indexer access) associated with x are evaluated, and the results are used in the subsequent get and set accessor invocations.
    • The get accessor of x is invoked.
    • The selected operator is invoked with the value returned by the get accessor as its argument.
    • The set accessor of x is invoked with the value returned by the operator as its value argument.
    • The value returned by the operator becomes the result of the operation.

The ++ and -- operators also support postfix notation, as described in §7.5.9. The result of x++ or x-- is the value of x before the operation, whereas the result of ++x or --x is the value of x after the operation. In either case, x itself has the same value after the operation.

An operator ++ or operator -- implementation can be invoked using either postfix and prefix notation. It is not possible to have separate operator implementations for the two notations.


7.6.8 Cast expressions

A cast-expression is used to explicitly convert an expression to a given type.

cast-expression:
( type ) unary-expression

A cast-expression of the form (T)E, where T is a type and E is a unary-expression, performs an explicit conversion (§6.2) of the value of E to type T. If no explicit conversion exists from the type of E to T, an error occurs. Otherwise, the result is the value produced by the explicit conversion. The result is always classified as a value, even if E denotes a variable.

The grammar for a cast-expression leads to certain syntactic ambiguities. For example, the expression (x)–y could either be interpreted as a cast-expression (a cast of –y to type x) or as an additive-expression combined with a parenthesized-expression (which computes the value x – y).

To resolve cast-expression ambiguities, the following rule exists: A sequence of one or more tokens (§2.4.6) enclosed in parentheses is considered the start of a cast-expression only if at least one of the following are true:

  • The sequence of tokens is correct grammar for a type, but not for an expression.
  • The sequence of tokens is correct grammar for a type, and the token immediately following the closing parentheses is the token "~", the token "!", the token "(", an identifier (§2.5), a literal (§2.5.3), or any keyword (§2.5.2) except is.

The above rules mean that only if the construct is unambiguously a cast-expression is it considered a cast-expression.

The term "correct grammar" above means only that the sequence of tokens must conform to the particular grammatical production. It specifically does not consider the actual meaning of any constituent identifiers. For example, if x and y are identifiers, then x.y is correct grammar for a type, even if x.y doesn’t actually denote a type.

From the disambiguation rules it follows that, if x and y are identifiers, (x)y, (x)(y), and (x)(-y) are cast-expressions, but (x)-y is not, even if x identifies a type. However, if x is a keyword that identifies a predefined type (such as int), then all four forms are cast-expressions (because such a keyword could not possibly be an expression by itself).


7.7 Arithmetic operators

The *, /, %, +, and – operators are called the arithmetic operators.

multiplicative-expression:
unary-expression
multiplicative-expression * unary-expression
multiplicative-expression / unary-expression
multiplicative-expression % unary-expression
additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression – multiplicative-expression


7.7.1 Multiplication operator

For an operation of the form x * y, binary operator overload resolution (§7.2.4) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

The predefined multiplication operators are listed below. The operators all compute the product of x and y.

  • Integer multiplication:
int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);

In a checked context, if the product is outside the range of the result type, an OverflowException is thrown. In an unchecked context, overflows are not reported and any significant high-order bits of the result are discarded.

  • Floating-point multiplication:
float operator *(float x, float y);
double operator *(double x, double y);

The product is computed according to the rules of IEEE 754 arithmetic. The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN’s. In the table, x and y are positive finite values. z is the result of x * y. If the result is too large for the destination type, z is infinity. If the result is too small for the destination type, z is zero.

 | +y | –y | +0 | –0 | +8 | –8 | NaN
+x | z | –z | +0 | –0 | +8 | –8 | NaN
–x | –z | z | –0 | +0 | –8 | +8 | NaN
+0 | +0 | –0 | +0 | –0 | NaN | NaN | NaN
–0 | –0 | +0 | –0 | +0 | NaN | NaN | NaN
+8 | +8 | –8 | NaN | NaN | +8 | –8 | NaN
–8 | –8 | +8 | NaN | NaN | –8 | +8 | NaN
NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN
  • Decimal multiplication:
decimal operator *(decimal x, decimal y);

If the resulting value is too large to represent in the decimal format, an OverflowException is thrown. If the result value is too small to represent in the decimal format, the result is zero.


7.7.2 Division operator

For an operation of the form x / y, binary operator overload resolution (§7.2.4) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

The predefined division operators are listed below. The operators all compute the quotient of x and y.

  • Integer division:
int operator /(int x, int y);
uint operator /(uint x, uint y);
long operator /(long x, long y);
ulong operator /(ulong x, ulong y);

If the value of the right operand is zero, a DivideByZeroException is thrown.

The division rounds the result towards zero, and the absolute value of the result is the largest possible integer that is less than the absolute value of the quotient of the two operands. The result is zero or positive when the two operands have the same sign and zero or negative when the two operands have opposite signs.

If the left operand is the maximum negative int or long and the right operand is –1, an overflow occurs. In a checked context, this causes an OverflowException to be thrown. In an unchecked context, the overflow is not reported and the result is instead the value of the left operand.

  • Floating-point division:
float operator /(float x, float y);
double operator /(double x, double y);

The quotient is computed according to the rules of IEEE 754 arithmetic. The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN’s. In the table, x and y are positive finite values. z is the result of x / y. If the result is too large for the destination type, z is infinity. If the result is too small for the destination type, z is zero.

 | +y | –y | +0 | –0 | +8 | –8 | NaN
+x | z | –z | +8 | –8 | +0 | –0 | NaN
–x | –z | z | –8 | +8 | –0 | +0 | NaN
+0 | +0 | –0 | NaN | NaN | +0 | –0 | NaN
–0 | –0 | +0 | NaN | NaN | –0 | +0 | NaN
+8 | +8 | –8 | +8 | –8 | NaN | NaN | NaN
–8 | –8 | +8 | –8 | +8 | NaN | NaN | NaN
NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN
  • Decimal division:
decimal operator /(decimal x, decimal y);

If the value of the right operand is zero, a DivideByZeroException is thrown. If the resulting value is too large to represent in the decimal format, an OverflowException is thrown. If the result value is too small to represent in the decimal format, the result is zero.


7.7.3 Remainder operator

For an operation of the form x % y, binary operator overload resolution (§7.2.4) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

The predefined remainder operators are listed below. The operators all compute the remainder of the division between x and y.

  • Integer remainder:
int operator %(int x, int y);
int operator %(uint x, uint y);
long operator %(long x, long y);
ulong operator %(ulong x, ulong y);

The result of x % y is the value produced by x – (x / y) * y. If y is zero, a DivideByZeroException is thrown. The remainder operator never causes an overflow.

  • Floating-point remainder:
float operator %(float x, float y);
double operator %(double x, double y);

The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN’s. In the table, x and y are positive finite values. z is the result of x % y and is computed as x – n * y, where n is the largest possible integer that is less than or equal to x / y. This method of computing the remainder is analogous to that used for integer operands, but differs from the IEEE 754 definition (in which n is the integer closest to x / y).

 | +y | –y | +0 | –0 | +8 | –8 | NaN
+x | z | z | NaN | NaN | x | x | NaN
–x | –z | –z | NaN | NaN | –x | –x | NaN
+0 | +0 | +0 | NaN | NaN | +0 | +0 | NaN
–0 | –0 | –0 | NaN | NaN | –0 | –0 | NaN
+8 | NaN | NaN | NaN | NaN | NaN | NaN | NaN
–8 | NaN | NaN | NaN | NaN | NaN | NaN | NaN
NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN
  • Decimal remainder:
decimal operator %(decimal x, decimal y);

If the value of the right operand is zero, a DivideByZeroException is thrown. If the resulting value is too large to represent in the decimal format, an OverflowException is thrown. If the result value is too small to represent in the decimal format, the result is zero.


7.7.4 Addition operator

For an operation of the form x + y, binary operator overload resolution (§7.2.4) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

The predefined addition operators are listed below. For numeric and enumeration types, the predefined addition operators compute the sum of the two operands. When one or both operands are of type string, the predefined addition operators concatenate the string representation of the operands.

  • Integer addition:
int operator +(int x, int y);
uint operator +(uint x, uint y);
long operator +(long x, long y);
ulong operator +(ulong x, ulong y);

In a checked context, if the sum is outside the range of the result type, an OverflowException is thrown. In an unchecked context, overflows are not reported and any significant high-order bits of the result are discarded.

  • Floating-point addition:
float operator +(float x, float y);
double operator +(double x, double y);

The sum is computed according to the rules of IEEE 754 arithmetic. The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN’s. In the table, x and y are nonzero finite values, and z is the result of x + y. If x and y have the same magnitude but opposite signs, z is positive zero. If x + y is too large to represent in the destination type, z is an infinity with the same sign as x + y. If x + y is too small to represent in the destination type, z is a zero with the same sign as x + y.

 | y | +0 | –0 | +8 | –8 | NaN
x | z | x | x | +8 | –8 | NaN
+0 | y | +0 | +0 | +8 | –8 | NaN
–0 | y | +0 | –0 | +8 | –8 | NaN
+8 | +8 | +8 | +8 | +8 | NaN | NaN
–8 | –8 | –8 | –8 | NaN | –8 | NaN
NaN | NaN | NaN | NaN | NaN | NaN | NaN
  • Decimal addition:
decimal operator +(decimal x, decimal y);

If the resulting value is too large to represent in the decimal format, an OverflowException is thrown. If the result value is too small to represent in the decimal format, the result is zero.

  • Enumeration addition. Every enumeration type implicitly provides the following predefined operators, where E is the enum type, and U is the underlying type of E:
E operator +(E x, U y);
E operator +(U x, E y);

The operators are evaluated exactly as (E)((U)x + (U)y).

  • String concatenation:
string operator +(string x, string y);
string operator +(string x, object y);
string operator +(object x, string y);

The binary + operator performs string concatenation when one or both operands are of type string. If an operand of string concatenation is null, an empty string is substituted. Otherwise, any non-string argument is converted to its string representation by invoking the virtual ToString() method inherited from type object. If ToString() returns null, an empty string is substituted.

The result of the string concatenation operator is a string that consists of the characters of the left operand followed by the characters of the right operand. The string concatenation operator never returns a null value. An OutOfMemoryException may be thrown if there is not enough memory available to allocate the resulting string.

  • Delegate concatenation. Every delegate type implicitly provides the following predefined operator, where D is the delegate type:
D operator +(D x, D y);


7.7.5 Subtraction operator

For an operation of the form x – y, binary operator overload resolution (§7.2.4) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

The predefined subtraction operators are listed below. The operators all subtract y from x.

  • Integer subtraction:
int operator –(int x, int y);
uint operator –(uint x, uint y);
long operator –(long x, long y);
ulong operator –(ulong x, ulong y);

In a checked context, if the difference is outside the range of the result type, an OverflowException is thrown. In an unchecked context, overflows are not reported and any significant high-order bits of the result are discarded.

  • Floating-point subtraction:
float operator –(float x, float y);
double operator –(double x, double y);

The difference is computed according to the rules of IEEE 754 arithmetic. The following table lists the results of all possible combinations of nonzero finite values, zeros, infinities, and NaN’s. In the table, x and y are nonzero finite values, and z is the result of x – y. If x and y are equal, z is positive zero. If x – y is too large to represent in the destination type, z is an infinity with the same sign as x – y. If x – y is too small to represent in the destination type, z is a zero with the same sign as x – y.

 | y | +0 | –0 | +8 | –8 | NaN
x | z | x | x | –8 | +8 | NaN
+0 | –y | +0 | +0 | –8 | +8 | NaN
–0 | –y | –0 | +0 | –8 | +8 | NaN
+8 | +8 | +8 | +8 | NaN | +8 | NaN
–8 | –8 | –8 | –8 | –8 | NaN | NaN
NaN | NaN | NaN | NaN | NaN | NaN | NaN
  • Decimal subtraction:
decimal operator –(decimal x, decimal y);

If the resulting value is too large to represent in the decimal format, an OverflowException is thrown. If the result value is too small to represent in the decimal format, the result is zero.

  • Enumeration subtraction. Every enumeration type implicitly provides the following predefined operator, where E is the enum type, and U is the underlying type of E:
U operator –(E x, E y);

This operator is evaluated exactly as (U)((U)x – (U)y). In other words, the operator computes the difference between the ordinal values of x and y, and the type of the result is the underlying type of the enumeration.

E operator –(E x, U y);

This operator is evaluated exactly as (E)((U)x – y). In other words, the operator subtracts a value from the underlying type of the enumeration, yielding a value of the enumeration.

  • Delegate removal. Every delegate type implicitly provides the following predefined operator, where D is the delegate type:
D operator –(D x, D y);


7.8 Shift operators

The << and >> operators are used to perform bit shifting operations.

shift-expression:
additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression

For an operation of the form x << count or x >> count, binary operator overload resolution (§7.2.4) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

When declaring an overloaded shift operator, the type of the first operand must always be the class or struct containing the operator declaration, and the type of the second operand must always be int.

The predefined shift operators are listed below.

  • Shift left:
int operator <<(int x, int count);
uint operator <<(uint x, int count);
long operator <<(long x, int count);
ulong operator <<(ulong x, int count);

The << operator shifts x left by a number of bits computed as described below.

The high-order bits of x are discarded, the remaining bits are shifted left, and the low-order empty bit positions are set to zero.

  • Shift right:
int operator >>(int x, int count);
uint operator >>(uint x, int count);
long operator >>(long x, int count);
ulong operator >>(ulong x, int count);

The >> operator shifts x right by a number of bits computed as described below.

When x is of type int or long, the low-order bits of x are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero if x is non-negative and set to one if x is negative.

When x is of type uint or ulong, the low-order bits of x are discarded, the remaining bits are shifted right, and the high-order empty bit positions are set to zero.

For the predefined operators, the number of bits to shift is computed as follows:

  • When the type of x is int or uint, the shift count is given by the low-order five bits of count. In other words, the shift count is computed from count & 0x1F.
  • When the type of x is long or ulong, the shift count is given by the low-order six bits of count. In other words, the shift count is computed from count & 0x3F.

If the resulting shift count is zero, the shift operators is simply return the value of x.

Shift operations never cause overflows and produce the same results in checked and unchecked contexts.

When the left operand of the >> operator is of a signed integral type, the operator performs an arithmetic shift right wherein the value of the most significant bit (the sign bit) of the operand is propagated to the high-order empty bit positions. When the left operand of the >> operator is of an unsigned integral type, the operator performs a logical shift right wherein high-order empty bit positions are always set to zero. To perform the opposite operation of that inferred from the operand type, explicit casts can be used. For example, if x is a variable of type int, the operation (int)((uint)x >> y) performs a logical shift right of x.


7.9 Relational operators

The ==, !=, <, >, <=, >=, and is operators are called the relational operators.

relational-expression:
shift-expression
relational-expression < shift-expression
relational-expression > shift-expression
relational-expression <= shift-expression
relational-expression >= shift-expression
relational-expression is reference-type
equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression

The is operator is described in §7.9.9.

The ==, !=, <, >, <= and >= operators as a group are called the comparison operators. For an operation of the form x op y, where op is a comparison operator, overload resolution (§7.2.4) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

The predefined comparison operators are described in the following sections. All predefined comparison operators return a result of type bool, as described in the following table.

Operation | Result
x == y | true if x is equal to y, false otherwise
x != y | true if x is not equal to y, false otherwise
x < y | true if x is less than y, false otherwise
x > y | true if x is greater than y, false otherwise
x <= y | true if x is less than or equal to y, false otherwise
x >= y | true if x is greater than or equal to y, false otherwise


7.9.1 Integer comparison operators

The predefined integer comparison operators are:

bool operator ==(int x, int y);
bool operator ==(uint x, uint y);
bool operator ==(long x, long y);
bool operator ==(ulong x, ulong y);
bool operator !=(int x, int y);
bool operator !=(uint x, uint y);
bool operator !=(long x, long y);
bool operator !=(ulong x, ulong y);
bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);
bool operator >(int x, int y);
bool operator >(uint x, uint y);
bool operator >(long x, long y);
bool operator >(ulong x, ulong y);
bool operator <=(int x, int y);
bool operator <=(uint x, uint y);
bool operator <=(long x, long y);
bool operator <=(ulong x, ulong y);
bool operator >=(int x, int y);
bool operator >=(uint x, uint y);
bool operator >=(long x, long y);
bool operator >=(ulong x, ulong y);

Each of these operators compare the numeric values of the two integer operands and return a bool value that indicates whether the particular relation is true or false.


7.9.2 Floating-point comparison operators

The predefined floating-point comparison operators are:

bool operator ==(float x, float y);
bool operator ==(double x, double y);
bool operator !=(float x, float y);
bool operator !=(double x, double y);
bool operator <(float x, float y);
bool operator <(double x, double y);
bool operator >(float x, float y);
bool operator >(double x, double y);
bool operator <=(float x, float y);
bool operator <=(double x, double y);
bool operator >=(float x, float y);
bool operator >=(double x, double y);

The operators compare the operands according to the rules of the IEEE 754 standard:

  • If either operand is NaN, the result is false for all operators except !=, and true for the != operator. For any two operands, x != y always produces the same result as !(x == y). However, when one or both operands are NaN, the <, >, <=, and >= operators do not produce the same results as the logical negation of the opposite operator. For example, if either of x and y is NaN, then x < y is false, but !(x >= y) is true.
  • When neither operand is NaN, the operators compare the values of the two floating-point operands with respect to the ordering
–8 < –max < ... < –min < –0.0 == +0.0 < +min < ... < +max < +8

where min and max are the smallest and largest positive finite values that can be represented in the given floating-point format. Notable effects of this ordering are:

  • Negative and positive zero are considered equal.
  • A negative infinity is considered less than all other values, but equal to another negative infinity.
  • A positive infinity is considered greater than all other values, but equal to another positive infinity.


7.9.3 Decimal comparison operators

The predefined decimal comparison operators are:

bool operator ==(decimal x, decimal y);
bool operator !=(decimal x, decimal y);
bool operator <(decimal x, decimal y);
bool operator >(decimal x, decimal y);
bool operator <=(decimal x, decimal y);
bool operator >=(decimal x, decimal y);

Each of these operators compare the numeric values of the two decimal operands and return a bool value that indicates whether the particular relation is true or false.


7.9.4 Boolean equality operators

The predefined boolean equality operators are:

bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);

The result of == is true if both x and y are true or if both x and y are false. Otherwise, the result is false.

The result of != is false if both x and y are true or if both x and y are false. Otherwise, the result is true. When the operands are of type bool, the != operator produces the same result as the ^ operator.


7.9.5 Enumeration comparison operators

Every enumeration type implicitly provides the following predefined comparison operators:

bool operator ==(E x, E y);
bool operator !=(E x, E y);
bool operator <(E x, E y);
bool operator >(E x, E y);
bool operator <=(E x, E y);
bool operator >=(E x, E y);

The result of evaluating x op y, where x and y are expressions of an enumeration type E with an underlying type U, and op is one of the comparison operators, is exactly the same as evaluating ((U)x) op ((U)y). In other words, the enumeration type comparison operators simply compare the underlying integral values of the two operands.


7.9.6 Reference type equality operators

The predefined reference type equality operators are:

bool operator ==(object x, object y);
bool operator !=(object x, object y);

The operators return the result of comparing the two references for equality or non-equality.

Since the predefined reference type equality operators accept operands of type object, they apply to all types that do not declare applicable operator == and operator != members. Conversely, any applicable user-defined equality operators effectively hide the predefined reference type equality operators.

The predefined reference type equality operators require the operands to be reference-type values or the value null, and furthermore require that an implicit conversion exists from the type of either operand to the type of the other operand. Unless both of these conditions are true, a compile-time error occurs. Notable implications of these rules are:

  • It is an error to use the predefined reference type equality operators to compare two references that are known to be different at compile-time. For example, if the compile-time types of the operands are two class types A and B, and if neither A nor B derives from the other, then it would be impossible for the two operands to reference the same object. Thus, the operation is considered a compile-time error.
  • The predefined reference type equality operators do not permit value type operands to be compared. Therefore, unless a struct type declares its own equality operators, it is not possible to compare values of that struct type.
  • The predefined reference type equality operators never cause boxing operations to occur for their operands. It would be meaningless to perform such boxing operations, since references to the newly allocated boxed instances would necessarily differ from all other references.

For an operation of the form x == y or x != y, if any applicable operator == or operator != exists, the operator overload resolution (§7.2.4) rules will select that operator instead of the predefined reference type equality operator. However, it is always possible to select the reference type equality operator by explicitly casting one or both of the operands to type object. The example

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

produces the output

True
False
False
False

The s and t variables refer to two distinct string instances containing the same characters. The first comparison outputs True because the predefined string equality operator (§7.9.7) is selected when both operands are of type string. The remaining comparisons all output False because the predefined reference type equality operator is selected when one or both of the operands are of type object.

Note that the above technique is not meaningful for value types. The example

class Test
{
   static void Main() {
      int i = 123;
      int j = 123;
      Console.WriteLine((object)i == (object)j);
   }
}

outputs False because the casts create references to two separate instances of boxed int values.


7.9.7 String equality operators

The predefined string equality operators are:

bool operator ==(string x, string y);
bool operator !=(string x, string y);

Two string values are considered equal when one of the following is true:

  • Both values are null.
  • Both values are non-null references to string instances that have identical lengths and identical characters in each character position.

The string equality operators compare string values rather than string references. When two separate string instances contain the exact same sequence of characters, the values of the strings are equal, but the references are different. As described in §7.9.6, the reference type equality operators can be used to compare string references instead of string values.


7.9.8 Delegate equality operators

Every delegate type implicitly provides the following predefined comparison operators, where D is any delegate type:

bool operator ==(D x, D y);
bool operator !=(D x, D y);


7.9.9 The is operator

The is operator is used to check whether the run-time type of an object is compatible with a given type. In an operation of the form e is T, e must be an expression of a reference-type and T must be a reference-type. If this is not the case, a compile-time error occurs.

The operation e is T returns true if e is not null and if an implicit reference conversion (§6.1.4) from the run-time type of the instance referenced by e to the type given by T exists. In other words, e is T checks that e is not null and that a cast-expression (§7.6.8) of the form (T)(e) will complete without throwing an exception.

If e is T is known at compile-time to always be true or always be false, a compile-time error occurs. The operation is known to always be true if an implicit reference conversion exists from the compile-time type of e to T. The operation is known to always be false if no implicit or explicit reference conversion exists from the compile-time type of e to T.


7.10 Logical operators

The &, ^, and | operators are called the logical operators.

and-expression:
equality-expression
and-expression & equality-expression
exclusive-or-expression:
and-expression
exclusive-or-expression ^ and-expression
inclusive-or-expression:
exclusive-or-expression
inclusive-or-expression | exclusive-or-expression

For an operation of the form x op y, where op is one of the logical operators, overload resolution (§7.2.4) is applied to select a specific operator implementation. The operands are converted to the parameter types of the selected operator, and the type of the result is the return type of the operator.

The predefined logical operators are described in the following sections.


7.10.1 Integer logical operators

The predefined integer logical operators are:

int operator &(int x, int y);
uint operator &(uint x, uint y);
long operator &(long x, long y);
ulong operator &(ulong x, ulong y);
int operator |(int x, int y);
uint operator |(uint x, uint y);
long operator |(long x, long y);
ulong operator |(ulong x, ulong y);
int operator ^(int x, int y);
uint operator ^(uint x, uint y);
long operator ^(long x, long y);
ulong operator ^(ulong x, ulong y);

The & operator computes the bitwise logical AND of the two operands, the | operator computes the bitwise logical OR of the two operands, and the ^ operator computes the bitwise logical exclusive OR of the two operands. No overflows are possible from these operations.


7.10.2 Enumeration logical operators

Every enumeration type E implicitly provides the following predefined logical operators:

E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);

The result of evaluating x op y, where x and y are expressions of an enumeration type E with an underlying type U, and op is one of the logical operators, is exactly the same as evaluating (E)((U)x) op ((U)y). In other words, the enumeration type logical operators simply perform the logical operation on the underlying type of the two operands.


7.10.3 Boolean logical operators

The predefined boolean logical operators are:

bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);

The result of x & y is true if both x and y are true. Otherwise, the result is false.

The result of x | y is true if either x or y is true. Otherwise, the result is false.

The result of x ^ y is true if x is true and y is false, or x is false and y is true. Otherwise, the result is false. When the operands are of type bool, the ^ operator computes the same result as the != operator.


7.11 Conditional logical operators

The && and || operators are called the conditional logical operators. They are at times also called the "short-circuiting" logical operators.

conditional-and-expression:
inclusive-or-expression
conditional-and-expression && inclusive-or-expression
conditional-or-expression:
conditional-and-expression
conditional-or-expression || conditional-and-expression

The && and || operators are conditional versions of the & and | operators:

  • The operation x && y corresponds to the operation x & y, except that y is evaluated only if x is true.
  • The operation x || y corresponds to the operation x | y, except that y is evaluated only if x is false.

An operation of the form x && y or x || y is processed by applying overload resolution (§7.2.4) as if the operation was written x & y or x | y. Then,

  • If overload resolution fails to find a single best operator, or if overload resolution selects one of the predefined integer logical operators, an error occurs.
  • Otherwise, if the selected operator is one of the predefined boolean logical operators (§7.10.2), the operation is processed as described in §7.11.1.
  • Otherwise, the selected operator is a user-defined operator, and the operation is processed as described in §7.11.2.

It is not possible to directly overload the conditional logical operators. However, because the conditional logical operators are evaluated in terms of the regular logical operators, overloads of the regular logical operators are, with certain restrictions, also considered overloads of the conditional logical operators. This is described further in §7.11.2.


7.11.1 Boolean conditional logical operators

When the operands of && or || are of type bool, or when the operands are of types that do not define an applicable operator & or operator |, but do define implicit conversions to bool, the operation is processed as follows:

  • The operation x && y is evaluated as x? y: false. In other words, x is first evaluated and converted to type bool. Then, if x is true, y is evaluated and converted to type bool, and this becomes the result of the operation. Otherwise, the result of the operation is false.
  • The operation x || y is evaluated as x? true: y. In other words, x is first evaluated and converted to type bool. Then, if x is true, the result of the operation is true. Otherwise, y is evaluated and converted to type bool, and this becomes the result of the operation.


7.11.2 User-defined conditional logical operators

When the operands of && or || are of types that declare an applicable user-defined operator & or operator |, both of the following must be true, where T is the type in which the selected operator is declared:

  • The return type and the type of each parameter of the selected operator must be T. In other words, the operator must compute the logical AND or the logical OR of two operands of type T, and must return a result of type T.
  • T must contain declarations of operator true and operator false.

A compile-time error occurs if either of these requirements is not satisfied. Otherwise, the && or || operation is evaluated by combining the user-defined operator true or operator false with the selected user-defined operator:

  • The operation x && y is evaluated as T.false(x)? x: T.&(x, y), where T.false(x) is an invocation of the operator false declared in T, and T.&(x, y) is an invocation of the selected operator &. In other words, x is first evaluated and operator false is invoked on the result to determine if x is definitely false. Then, if x is definitely false, the result of the operation is the value previously computed for x. Otherwise, y is evaluated, and the selected operator & is invoked on the value previously computed for x and the value computed for y to produce the result of the operation.
  • The operation x || y is evaluated as T.true(x)? x: T.|(x, y), where T.true(x) is an invocation of the operator true declared in T, and T.|(x, y) is an invocation of the selected operator |. In other words, x is first evaluated and operator true is invoked on the result to determine if x is definitely true. Then, if x is definitely true, the result of the operation is the value previously computed for x. Otherwise, y is evaluated, and the selected operator | is invoked on the value previously computed for x and the value computed for y to produce the result of the operation.

In either of these operations, the expression given by x is only evaluated once, and the expression given by y is either not evaluated or evaluated exactly once.

For an example of a type that implements operator true and operator false, see §11.3.2.


7.12 Conditional operator

The ?: operator is called the conditional operator. It is at times also called the ternary operator.

conditional-expression:
conditional-or-expression
conditional-or-expression ? expression : expression

A conditional expression of the form b? x: y first evaluates the condition b. Then, if b is true, x is evaluated and becomes the result of the operation. Otherwise, y is evaluated and becomes the result of the operation. A conditional expression never evaluates both x and y.

The conditional operator is right-associative, meaning that operations are grouped from right to left. For example, an expression of the form a? b: c? d: e is evaluated as a? b: (c? d: e).

The first operand of the ?: operator must be an expression of a type that can be implicitly converted to bool, or an expression of a type that implements operator true. If neither of these requirements are satisfied, a compile-time error occurs.

The second and third operands of the ?: operator control the type of the conditional expression. Let X and Y be the types of the second and third operands. Then,

  • If X and Y are the same type, then this is the type of the conditional expression.
  • Otherwise, if an implicit conversion (§6.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
  • Otherwise, if an implicit conversion (§6.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
  • Otherwise, no expression type can be determined, and a compile-time error occurs.

The run-time processing of a conditional expression of the form b? x: y consists of the following steps:

  • First, b is evaluated, and the bool value of b is determined:
    • If an implicit conversion from the type of b to bool exists, then this implicit conversion is performed to produce a bool value.
    • Otherwise, the operator true defined by the type of b is invoked to produce a bool value.
  • If the bool value produced by the step above is true, then x is evaluated and converted to the type of the conditional expression, and this becomes the result of the conditional expression.
  • Otherwise, y is evaluated and converted to the type of the conditional expression, and this becomes the result of the conditional expression.


7.13 Assignment operators

The assignment operators assign a new value to a variable, a property, or an indexer element.

assignment:
unary-expression assignment-operator expression
assignment-operator: one of
= += -= *= /= %= &= |= ^= <<= >>=

The left operand of an assignment must be an expression classified as a variable, a property access, or an indexer access.

The = operator is called the simple assignment operator. It assigns the value of the right operand to the variable, property, or indexer element given by the left operand. The simple assignment operator is described in §7.13.1.

The operators formed by prefixing a binary operator with an = character are called the compound assignment operators. These operators perform the indicated operation on the two operands, and then assign the resulting value to the variable, property, or indexer element given by the left operand. The compound assignment operators are described in §7.13.2.

The assignment operators are right-associative, meaning that operations are grouped from right to left. For example, an expression of the form a = b = c is evaluated as a = (b = c).


7.13.1 Simple assignment

The = operator is called the simple assignment operator. In a simple assignment, the right operand must be an expression of a type that is implicitly convertible to the type of the left operand. The operation assigns the value of the right operand to the variable, property, or indexer element given by the left operand.

The result of a simple assignment expression is the value assigned to the left operand. The result has the same type as the left operand and is always classified as a value.

If the left operand is a property or indexer access, the property or indexer must have a set accessor. If this is not the case, a compile-time error occurs.

The run-time processing of a simple assignment of the form x = y consists of the following steps:

  • If x is classified as a variable:
    • x is evaluated to produce the variable.
    • y is evaluated and, if required, converted to the type of x through an implicit conversion (§6.1).
    • If the variable given by x is an array element of a reference-type, a run-time check is performed to ensure that the value computed for y is compatible with the array instance of which x is an element. The check succeeds if y is null, or if an implicit reference conversion (§6.1.4) exists from the actual type of the instance referenced by y to the actual element type of the array instance containing x. Otherwise, an ArrayTypeMismatchException is thrown.
    • The value resulting from the evaluation and conversion of y is stored into the location given by the evaluation of x.
  • If x is classified as a property or indexer access:
    • The instance expression (if x is not static) and the argument list (if x is an indexer access) associated with x are evaluated, and the results are used in the subsequent set accessor invocation.
    • y is evaluated and, if required, converted to the type of x through an implicit conversion (§6.1).
    • The set accessor of x is invoked with the value computed for y as its value argument.

The array co-variance rules (§12.5) permit a value of an array type A[] to be a reference to an instance of an array type B[], provided an implicit reference conversion exists from B to A. Because of these rules, assignment to an array element of a reference-type requires a run-time check to ensure that the value being assigned is compatible with the array instance. In the example

string[] sa = new string[10];
object[] oa = sa;
oa[0] = null;               // Ok
oa[1] = "Hello";            // Ok
oa[2] = new ArrayList();   // ArrayTypeMismatchException

the last assignment causes an ArrayTypeMismatchException to be thrown because an instance of ArrayList cannot be stored in an element of a string[].

When a property or indexer declared in a struct-type is the target of an assignment, the instance expression associated with the property or indexer access must be classified as a variable. If the instance expression is classified as a value, a compile-time error occurs.

Given the declarations:

struct Point
{
   int x, y;
   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
   public int X {
      get { return x; }
      set { x = value; }
   }
   public int Y {
      get { return y; }
      set { y = value; }
   }
}
struct Rectangle
{
   Point a, b;
   public Rectangle(Point a, Point b) {
      this.a = a;
      this.b = b;
   }
   public Point A {
      get { return a; }
      set { a = value; }
   }
   public Point B {
      get { return b; }
      set { b = value; }
   }
}

in the example

Point p = new Point();
p.X = 100;
p.Y = 100;
Rectangle r = new Rectangle();
r.A = new Point(10, 10);
r.B = p;

the assignments to p.X, p.Y, r.A, and r.B are permitted because p and r are variables. However, in the example

Rectangle r = new Rectangle();
r.A.X = 10;
r.A.Y = 10;
r.B.X = 100;
r.B.Y = 100;

the assignments are all invalid, since r.A and r.B are not variables.


7.13.2 Compound assignment

An operation of the form x op= y is processed by applying binary operator overload resolution (§7.2.4) as if the operation was written x op y. Then,

  • If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once.
  • Otherwise, if the selected operator is a predefined operator, if the return type of the selected operator is explicitly convertible to the type of x, and if y is implicitly convertible to the type of x, then the operation is evaluated as x = (T)(x op y), where T is the type of x, except that x is evaluated only once.
  • Otherwise, the compound assignment is invalid, and a compile-time error occurs.

The term "evaluated only once" means that in the evaluation of x op y, the results of any constituent expressions of x are temporarily saved and then reused when performing the assignment to x. For example, in the assignment A()[B()] += C(), where A is a method returning int[], and B and C are methods returning int, the methods are invoked only once, in the order A, B, C.

When the left operand of a compound assignment is a property access or indexer access, the property or indexer must have both a get accessor and a set accessor. If this is not the case, a compile-time error occurs.

The second rule above permits x op= y to be evaluated as x = (T)(x op y) in certain contexts. The rule exists such that the predefined operators can be used as compound operators when the left operand is of type sbyte, byte, short, ushort, or char. Even when both arguments are of one of those types, the predefined operators produce a result of type int, as described in §7.2.6.2. Thus, without a cast it would not be possible to assign the result to the left operand.

The intuitive effect of the rule for predefined operators is simply that x op= y is permitted if both of x op y and x = y are permitted. In the example

byte b = 0;
char ch = '\0';
int i = 0;
b += 1;            // Ok
b += 1000;         // Error, b = 1000 not permitted
b += i;            // Error, b = i not permitted
b += (byte)i;      // Ok
ch += 1;            // Error, ch = 1 not permitted
ch += (char)1;      // Ok

the intuitive reason for each error is that a corresponding simple assignment would also have been an error.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

7.13.3 Event assignment

© 2000 Microsoft Corporation. All rights reserved.


7.14 Expression

An expression is either a conditional-expression or an assignment.

expression:
conditional-expression
assignment


7.15 Constant expressions

A constant-expression is an expression that can be fully evaluated at compile-time.

constant-expression:
expression

The type of a constant expression can be one of the following: sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, any enumeration type, or the null type. The following constructs are permitted in constant expressions:

  • Literals (including the null literal).
  • References to const members of class and struct types.
  • References to members of enumeration types.
  • Parenthesized sub-expressions.
  • Cast expressions, provided the target type is one of the types listed above.
  • The predefined +, –, !, and ~ unary operators.
  • The predefined +, –, *, /, %, <<, >>, &, |, ^, &&, ||, ==, !=, <, >, <=, and => binary operators, provided each operand is of a type listed above.
  • The ?: conditional operator.

Whenever an expression is of one of the types listed above and contains only the constructs listed above, the expression is evaluated at compile-time. This is true even if the expression is a sub-expression of a larger expression that contains non-constant constructs.

The compile-time evaluation of constant expressions uses the same rules as run-time evaluation of non-constant expressions, except that where run-time evaluation would have thrown an exception, compile-time evaluation causes a compile-time error to occur.

Unless a constant expression is explicitly placed in an unchecked context, overflows that occur in integral-type arithmetic operations and conversions during the compile-time evaluation of the expression always cause compile-time errors (§7.5.13).

Constant expressions occur in the contexts listed below. In these contexts, an error occurs if an expression cannot be fully evaluated at compile-time.

  • Constant declarations (§10.3).
  • Enumeration member declarations (§14.2).
  • case labels of a switch statement (§8.7.2).
  • goto case statements (§8.9.3).
  • Attributes (§17).

An implicit constant expression conversion (§6.1.6) permits a constant expression of type int to be converted to sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant expression is within the range of the destination type.


7.16 Boolean expressions

A boolean-expression is an expression that yields a result of type bool.

boolean-expression:
expression

The controlling conditional expression of an if-statement (§8.7.1), while-statement (§8.8.1), do-statement (§8.8.2), or for-statement (§8.8.3) is a boolean-expression. The controlling conditional expression of the ?: operator (§7.12) follows the same rules as a boolean-expression, but for reasons of operator precedence is classified as a conditional-or-expression.

A boolean-expression is required to be of a type that can be implicitly converted to bool or of a type that implements operator true. If neither of these requirements are satisfied, a compile-time error occurs.

When a boolean expression is of a type that cannot be implicitly converted to bool but does implement operator true, then following evaluation of the expression, the operator true implementation provided by the type is invoked to produce a bool value.

The DBBool struct type in §11.3.2 provides an example of a type that implements operator true.


8. Statements

C# provides a variety of statements. Most of these statements will be familiar to developers who have programmed in C and C++.

statement:
labeled-statement
declaration-statement
embedded-statement
embedded-statement:
block
empty-statement
expression-statement
selection-statement
iteration-statement
jump-statement
try-statement
checked-statement
unchecked-statement
lock-statement

The embedded-statement nonterminal is used for statements that appear within other statements. The use of embedded-statement rather than statement excludes the use of declaration statements and labeled statements in these contexts. For example, the code

void F(bool b) {
   if (b)
      int i = 44;
}

is in error because an if statement requires an embedded-statement rather than a statement for its if branch. If this code were permitted, then the variable i would be declared, but it could never be used.


8.1 End points and reachability

Every statement has an end point. In intuitive terms, the end point of a statement is the location that immediately follows the statement. The execution rules for composite statements (statements that contain embedded statements) specify the action that is taken when control reaches the end point of an embedded statement. For example, when control reaches the end point of a statement in a block, control is transferred to the next statement in the block.

If a statement can possibly be reached by execution, the statement is said to be reachable. Conversely, if there is no possibility that a statement will be executed, the statement is said to be unreachable.

In the example

void F() {
   Console.WriteLine("reachable");
   goto Label;
   Console.WriteLine("unreachable");
   Label:
   Console.WriteLine("reachable");
}

the second Console.WriteLine invocation is unreachable because there is no possibility that the statement will be executed.

A warning is reported if the compiler determines that a statement is unreachable. It is specifically not an error for a statement to be unreachable.

To determine whether a particular statement or end point is reachable, the compiler performs flow analysis according to the reachability rules defined for each statement. The flow analysis takes into account the values of constant expressions (§7.15) that control the behavior of statements, but the possible values of non-constant expressions are not considered. In other words, for purposes of control flow analysis, a non-constant expression of a given type is considered to have any possible value of that type.

In the example

void F() {
   const int i = 1;
   if (i == 2) Console.WriteLine("unreachable");
}

the boolean expression of the if statement is a constant expression because both operands of the == operator are constants. The constant expression is evaluated at compile-time, producing the value false, and the Console.WriteLine invocation is therefore considered unreachable. However, if i is changed to be a local variable

void F() {
   int i = 1;
   if (i == 2) Console.WriteLine("reachable");
}

the Console.WriteLine invocation is considered reachable, even though it will in reality never be executed.

The block of a function member is always considered reachable. By successively evaluating the reachability rules of each statement in a block, the reachability of any given statement can be determined.

In the example

Void F(int x) {
   Console.WriteLine("start");
   if (x < 0) Console.WriteLine("negative");
}

the reachability of the second Console.WriteLine is determined as follows:

  • First, because the block of the F method is reachable, the first Console.WriteLine statement is reachable.
  • Next, because the first Console.WriteLine statement is reachable, its end point is reachable.
  • Next, because the end point of the first Console.WriteLine statement is reachable, the if statement is reachable.
  • Finally, because the boolean expression of the if statement does not have the constant value false, the second Console.WriteLine statement is reachable.

There are two situations in which it is an error for the end point of a statement to be reachable:

  • Because the switch statement does not permit a switch section to "fall through" to the next switch section, it is an error for the end point of the statement list of a switch section to be reachable. If this error occurs, it is typically an indication that a break statement is missing.
  • It is an error for the end point of the block of a function member that computes a value to be reachable. If this error occurs, it is typically an indication that a return statement is missing.


8.2 Blocks

A block permits multiple statements to be written in contexts where a single statement is expected.

block:
{ statement-listopt }

A block consists of an optional statement-list (§8.2.1), enclosed in braces. If the statement list is omitted, the block is said to be empty.

A block may contain declaration statements (§8.5). The scope of a local variable or constant declared in a block extends from the declaration to the end of the block.

Within a block, the meaning of a name used in an expression context must always be the same (§7.5.2.1).

A block is executed as follows:

  • If the block is empty, control is transferred to the end point of the block.
  • If the block is not empty, control is transferred to the statement list. When and if control reaches the end point of the statement list, control is transferred to the end point of the block.

The statement list of a block is reachable if the block itself is reachable.

The end point of a block is reachable if the block is empty or if the end point of the statement list is reachable.


8.2.1 Statement lists

A statement list consists of one or more statements written in sequence. Statement lists occur in blocks (§8.2) and in switch-blocks (§8.7.2).

statement-list:
statement
statement-list statement

A statement list is executed by transferring control to the first statement. When and if control reaches the end point of a statement, control is transferred to the next statement. When and if control reaches the end point of the last statement, control is transferred to the end point of the statement list.

A statement in a statement list is reachable if at least one of the following is true:

  • The statement is the first statement and the statement list itself is reachable.
  • The end point of the preceding statement is reachable.
  • The statement is a labeled statement and the label is referenced by a reachable goto statement.

The end point of a statement list is reachable if the end point of the last statement in the list is reachable.


8.3 The empty statement

An empty-statement does nothing.

empty-statement:
;

An empty statement is used when there are no operations to perform in a context where a statement is required.

Execution of an empty statement simply transfers control to the end point of the statement. Thus, the end point of an empty statement is reachable if the empty statement is reachable.

An empty statement can be used when writing a while statement with a null body:

bool ProcessMessage() {...}
void ProcessMessages() {
   while (ProcessMessage());
}

Also, an empty statement can be used to declare a label just before the closing "}" of a block:

void F() {
   ...
if (done) goto exit;
   ...
exit: ;
}


8.4 Labeled statements

A labeled-statement permits a statement to be prefixed by a label. Labeled statements are permitted blocks, but are not permitted as embedded statements.

labeled-statement:
identifier : statement

A labeled statement declares a label with the name given by the identifier. The scope of a label is the block in which the label is declared, including any nested blocks. It is an error for two labels with the same name to have overlapping scopes.

A label can be referenced from goto statements (§8.9.3) within the scope of the label. This means that goto statements can transfer control inside blocks and out of blocks, but never into blocks.

Labels have their own declaration space and do not interfere with other identifiers. The example

int F(int x) {
   if (x >= 0) goto x;
   x = -x;
   x: return x;
}

is valid and uses the name x as both a parameter and a label.

Execution of a labeled statement corresponds exactly to execution of the statement following the label.

In addition to the reachability provided by normal flow of control, a labeled statement is reachable if the label is referenced by a reachable goto statement.


8.5 Declaration statements

A declaration-statement declares a local variable or constant. Declaration statements are permitted in blocks, but are not permitted as embedded statements.

declaration-statement:
local-variable-declaration ;
local-constant-declaration ;


8.5.1 Local variable declarations

A local-variable-declaration declares one or more local variables.

local-variable-declaration:
type variable-declarators
variable-declarators:
variable-declarator
variable-declarators , variable-declarator
variable-declarator:
identifier
identifier = variable-initializer
variable-initializer:
expression
array-initializer

The type of a local-variable-declaration specifies the type of the variables introduced by the declaration. The type is followed by a list of variable-declarators, each of which introduces a new variable. A variable-declarator consists of an identifier that names the variable, optionally followed by an "=" token and a variable-initializer that gives the initial value of the variable.

The value of a local variable is obtained in an expression using a simple-name (§7.5.2), and the value of a local variable is modified using an assignment (§7.13). A local variable must be definitely assigned (§5.3) at each location where its value is obtained.

The scope of a local variable starts immediately after its identifier in the declaration and extends to the end of the block containing the declaration. Within the scope of a local variable, it is an error to declare another local variable or constant with the same name.

A local variable declaration that declares multiple variables is equivalent to multiple declarations of single variables with the same type. Furthermore, a variable initializer in a local variable declaration corresponds exactly to an assignment statement that is inserted immediately after the declaration.

The example

void F() {
   int x = 1, y, z = x * 2;
}

corresponds exactly to

void F() {
   int x; x = 1;
   int y;
   int z; z = x * 2;
}


8.5.2 Local constant declarations

A local-constant-declaration declares one or more local constants.

local-constant-declaration:
const type constant-declarators
constant-declarators:
constant-declarator
constant-declarators , constant-declarator
constant-declarator:
identifier = constant-expression

The type of a local-constant-declaration specifies the type of the constants introduced by the declaration. The type is followed by a list of constant-declarators, each of which introduces a new constant. A constant-declarator consists of an identifier that names the constant, followed by an "=" token, followed by a constant-expression (§7.15) that gives the value of the constant.

The type and constant-expression of a local constant declaration must follow the same rules as those of a constant member declaration (§10.3).

The value of a local constant is obtained in an expression using a simple-name (§7.5.2).

The scope of a local constant extends from its declaration to the end of the block containing the declaration. The scope of a local constant does not include the constant-expression that provides its value. Within the scope of a local constant, it is an error to declare another local variable or constant with the same name.


8.6 Expression statements

An expression-statement evaluates a given expression. The value computed by the expression, if any, is discarded.

expression-statement:
statement-expression ;
statement-expression:
invocation-expression
object-creation-expression
assignment
post-increment-expression
post-decrement-expression
pre-increment-expression
pre-decrement-expression

Not all expressions are permitted as statements. In particular, expressions such as x + y and x == 1 that have no side-effects, but merely compute a value (which will be discarded), are not permitted as statements.

Execution of an expression statement evaluates the contained expression and then transfers control to the end point of the expression statement.


8.7 Selection statements

Selection statements select one of a number of possible statements for execution based on the value of a controlling expression.

selection-statement:
if-statement
switch-statement


8.7.1 The if statement

The if statement selects a statement for execution based on the value of a boolean expression.

if-statement:
if ( boolean-expression ) embedded-statement
if ( boolean-expression ) embedded-statement else embedded-statement
boolean-expression:
expression

An else part is associated with the nearest preceding if statement that does not already have an else part. Thus, an if statement of the form

if (x) if (y) F(); else G();

is equivalent to

if (x) {
   if (y) {
      F();
   }
   else {
      G();
   }
}

An if statement is executed as follows:

  • The boolean-expression (§7.16) is evaluated.
  • If the boolean expression yields true, control is transferred to the first embedded statement. When and if control reaches the end point of that statement, control is transferred to the end point of the if statement.
  • If the boolean expression yields false and if an else part is present, control is transferred to the second embedded statement. When and if control reaches the end point of that statement, control is transferred to the end point of the if statement.
  • If the boolean expression yields false and if an else part is not present, control is transferred to the end point of the if statement.

The first embedded statement of an if statement is reachable if the if statement is reachable and the boolean expression does not have the constant value false.

The second embedded statement of an if statement, if present, is reachable if the if statement is reachable and the boolean expression does not have the constant value true.

The end point of an if statement is reachable if the end point of at least one of its embedded statements is reachable. In addition, the end point of an if statement with no else part is reachable if the if statement is reachable and the boolean expression does not have the constant value true.


8.7.2 The switch statement

The switch statement executes the statements that are associated with the value of the controlling expression.

switch-statement:
switch ( expression ) switch-block
switch-block:
{ switch-sectionsopt }
switch-sections:
switch-section
switch-sections switch-section
switch-section:
switch-labels statement-list
switch-labels:
switch-label
switch-labels switch-label
switch-label:
case constant-expression :
default :

A switch-statement consists of the keyword switch, followed by a parenthesized expression (called the switch expression), followed by a switch-block. The switch-block consists of zero or more switch-sections, enclosed in braces. Each switch-section consists of one or more switch-labels followed by a statement-list (§8.2.1)..

The governing type of a switch statement is established by the switch expression. If the type of the switch expression is sbyte, byte, short, ushort, int, uint, long, ulong, char, string, or an enum-type, then that is the governing type of the switch statement. Otherwise, exactly one user-defined implicit conversion (§6.4) must exist from the type of the switch expression to one of the following possible governing types: sbyte, byte, short, ushort, int, uint, long, ulong, char, string. If no such implicit conversion exists, or if more that one such implicit conversion exists, a compile-time error occurs.

The constant expression of each case label must denote a value of a type that is implicitly convertible (§6.1) to the governing type of the switch statement. A compile-time error occurs if an two or more case labels in the same switch statement specify the same constant value.

There can be at most one default label in a switch statement.

A switch statement is executed as follows:

  • The switch expression is evaluated and converted to the governing type.
  • If one of the constants specified in a case label is equal to the value of the switch expression, control is transferred to the statement list following the matched case label.
  • If no constant matches the value of the switch expression and if a default label is present, control is transferred to the statement list following the default label.
  • If no constant matches the value of the switch expression and if no default label is present, control is transferred to the end point of the switch statement.

If the end point of the statement list of a switch section is reachable, a compile-time error occurs. This is known as the "no fall through" rule. The example

switch (i) {
case 0:
   CaseZero();
   break;
case 1:
   CaseOne();
   break;
default:
   CaseOthers();
   break;
}

is valid because no switch section has a reachable end point. Unlike C and C++, execution of a switch section is not permitted to "fall through" to the next switch section, and the example

switch (i) {
case 0:
   CaseZero();
case 1:
   CaseZeroOrOne();
default:
   CaseAny();
}

is in error. When execution of a switch section is to be followed by execution of another switch section, an explicit goto case or goto default statement must be used:

switch (i) {
case 0:
   CaseZero();
   goto case 1;
case 1:
   CaseZeroOrOne();
   goto default;
default:
   CaseAny();
   break;
}

Multiple labels are permitted in a switch-section. The example

switch (i) {
case 0:
   CaseZero();
   break;
case 1:
   CaseOne();
   break;
case 2:
default:
   CaseTwo();
   break;
}

is legal. The example does not violate the "no fall through" rule because the labels case 2: and default: are part of the same switch-section.

The "no fall through" rule prevents a common class of bugs that occur in C and C++ when break statements are accidentally omitted. Also, because of this rule, the switch sections of a switch statement can be arbitrarily rearranged without affecting the behavior of the statement. For example, the sections of the switch statement above can be reversed without affecting the behavior of the statement:

switch (i) {
default:
   CaseAny();
   break;
case 1:
   CaseZeroOrOne();
   goto default;
case 0:
   CaseZero();
   goto case 1;
}

The statement list of a switch section typically ends in a break, goto case, or goto default statement, but any construct that renders the end point of the statement list unreachable is permitted. For example, a while statement controlled by the boolean expression true is known to never reach its end point. Likewise, a throw or return statement always transfer control elsewhere and never reaches its end point. Thus, the following example is valid:

switch (i) {
case 0:
   while (true) F();
case 1:
   throw new ArgumentException();
case 2:
   return;
}

The governing type of a switch statement may be the type string. For example:

void DoCommand(string command) {
   switch (command.ToLower()) {
   case "run":
      DoRun();
      break;
   case "save":
      DoSave();
      break;
   case "quit":
      DoQuit();
      break;
   default:
      InvalidCommand(command);
      break;
   }
}

Like the string equality operators (§7.9.7), the switch statement is case sensitive and will execute a given switch section only if the switch expression string exactly matches a case label constant. As illustrated by the example above, a switch statement can be made case insensitive by converting the switch expression string to lower case and writing all case label constants in lower case.

When the governing type of a switch statement is string, the value null is permitted as a case label constant.

A switch-block may contain declaration statements (§8.5). The scope of a local variable or constant declared in a switch block extends from the declaration to the end of the switch block.

Within a switch block, the meaning of a name used in an expression context must always be the same (§7.5.2.1).

The statement list of a given switch section is reachable if the switch statement is reachable and at least one of the following is true:

  • The switch expression is a non-constant value.
  • The switch expression is a constant value that matches a case label in the switch section.
  • The switch expression is a constant value that doesn’t match any case label, and the switch section contains the default label.
  • A switch label of the switch section is referenced by a reachable goto case or goto default statement.

The end point of a switch statement is reachable if at least one of the following is true:

  • The switch statement contains a reachable break statement that exits the switch statement.
  • The switch statement is reachable, the switch expression is a non-constant value, and no default label is present.
  • The switch statement is reachable, the switch expression is a constant value that doesn’t match any case label, and no default label is present.


8.8 Iteration statements

Iteration statements repeatedly execute an embedded statement.

iteration-statement:
while-statement
do-statement
for-statement
foreach-statement


8.8.1 The while statement

The while statement conditionally executes an embedded statement zero or more times.

while-statement:
while ( boolean-expression ) embedded-statement

A while statement is executed as follows:

  • The boolean-expression (§7.16) is evaluated.
  • If the boolean expression yields true, control is transferred to the embedded statement. When and if control reaches the end point of the embedded statement (possibly from execution of a continue statement), control is transferred to the beginning of the while statement.
  • If the boolean expression yields false, control is transferred to the end point of the while statement.

Within the embedded statement of a while statement, a break statement (§8.9.1) may be used to transfer control to the end point of the while statement (thus ending iteration of the embedded statement), and a continue statement (§?) may be used to transfer control to the end point of the embedded statement (thus performing another iteration of the while statement).

The embedded statement of a while statement is reachable if the while statement is reachable and the boolean expression does not have the constant value false.

The end point of a while statement is reachable if at least one of the following is true:

  • The while statement contains a reachable break statement that exits the while statement.
  • The while statement is reachable and the boolean expression does not have the constant value true.


8.8.2 The do statement

The do statement conditionally executes an embedded statement one or more times.

do-statement:
do embedded-statement while ( boolean-expression ) ;

A do statement is executed as follows:

  • Control is transferred to the embedded statement.
  • When and if control reaches the end point of the embedded statement (possibly from execution of a continue statement), the boolean-expression (§7.16) is evaluated. If the boolean expression yields true, control is transferred to the beginning of the do statement. Otherwise, control is transferred to the end point of the do statement.

Within the embedded statement of a do statement, a break statement (§8.9.1) may be used to transfer control to the end point of the do statement (thus ending iteration of the embedded statement), and a continue statement (§?) may be used to transfer control to the end point of the embedded statement (thus performing another iteration of the do statement).

The embedded statement of a do statement is reachable if the do statement is reachable.

The end point of a do statement is reachable if at least one of the following is true:

  • The do statement contains a reachable break statement that exits the do statement.
  • The end point of the embedded statement is reachable and the boolean expression does not have the constant value true.


8.8.3 The for statement

The for statement evaluates a sequence of initialization expressions and then, while a condition is true, repeatedly executes an embedded statement and evaluates a sequence of iteration expressions.

for-statement:
for ( for-initializeropt ; for-conditionopt ; for-iteratoropt ) embedded-statement
for-initializer:
local-variable-declaration
statement-expression-list
for-condition:
boolean-expression
for-iterator:
statement-expression-list
statement-expression-list:
statement-expression
statement-expression-list , statement-expression

The for-initializer, if present, consists of either a local-variable-declaration (§8.5.1) or a list of statement-expressions (§8.6) separated by commas. The scope of a local variable declared by a for-initializer starts at the variable-declarator for the variable and extends to the end of the embedded statement. The scope includes the for-condition and the for-iterator.

The for-condition, if present, must be a boolean-expression (§7.16).

The for-iterator, if present, consists of a list of statement-expressions (§8.6) separated by commas.

A for statement is executed as follows:

  • If a for-initializer is present, the variable initializers or statement expressions are executed in the order they are written. This step is only performed once.
  • If a for-condition is present, it is evaluated.
  • If the for-condition is not present or if the evaluation yields true, control is transferred to the embedded statement. When and if control reaches the end point of the embedded statement (possibly from execution of a continue statement), the expressions of the for-iterator, if any, are evaluated in sequence, and then another iteration is performed, starting with evaluation of the for-condition in the step above.
  • If the for-condition is present and the evaluation yields false, control is transferred to the end point of the for statement.

Within the embedded statement of a for statement, a break statement (§8.9.1) may be used to transfer control to the end point of the for statement (thus ending iteration of the embedded statement), and a continue statement (§8.9.2) may be used to transfer control to the end point of the embedded statement (thus executing another iteration of the for statement).

The embedded statement of a for statement is reachable if one of the following is true:

  • The for statement is reachable and no for-condition is present.
  • The for statement is reachable and a for-condition is present and does not have the constant value false.

The end point of a for statement is reachable if at least one of the following is true:

  • The for statement contains a reachable break statement that exits the for statement.
  • The for statement is reachable and a for-condition is present and does not have the constant value true.


8.8.4 The foreach statement

The foreach statement enumerates the elements of a collection, executing an embedded statement for each element of the collection.

foreach-statement:
foreach ( type identifier in expression ) embedded-statement

The type and identifier of a foreach statement declare the iteration variable of the statement. The iteration variable corresponds to a read-only local variable with a scope that extends over the embedded statement. During execution of a foreach statement, the iteration variable represents the collection element for which an iteration is currently being performed. A compile-time error occurs if the embedded statement attempts to assign to the iteration variable or pass the iteration variable as a ref or out parameter.

The type of the expression of a foreach statement must be a collection type (as defined below), and an explicit conversion (§6.2) must exist from the element type of the collection to the type of the iteration variable.

A type C is said to be a collection type if all of the following are true:

  • C contains a public instance method with the signature GetEnumerator() that returns a struct-type, class-type, or interface-type, in the following called E.
  • E contains a public instance method with the signature MoveNext() and the return type bool.
  • E contains a public instance property named Current that permits reading. The type of this property is said to be the element type of the collection type.

The System.Array type (§12.1.1) is a collection type, and since all array types derive from System.Array, any array type expression is permitted in a foreach statement. For single-dimensional arrays, the foreach statement enumerates the array elements in increasing index order, starting with index 0 and ending with index Length – 1. For multi-dimensional arrays, the indices of the rightmost dimension are increased first.

A foreach statement is executed as follows:

  • The collection expression is evaluated to produce an instance of the collection type. This instance is referred to as c in the following. If c is of a reference-type and has the value null, a NullReferenceException is thrown.
  • An enumerator instance is obtained by evaluating the method invocation c.GetEnumerator(). The returned enumerator is stored in a temporary local variable, in the following referred to as e. It is not possible for the embedded statement to access this temporary variable. If e is of a reference-type and has the value null, a NullReferenceException is thrown.
  • The enumerator is advanced to the next element by evaluating the method invocation e.MoveNext().
  • If the value returned by e.MoveNext() is true, the following steps are performed:
    • The current enumerator value is obtained by evaluating the property access e.Current, and the value is converted to the type of the iteration variable by an explicit conversion (§6.2). The resulting value is stored in the iteration variable such that it can be accessed in the embedded statement.
    • Control is transferred to the embedded statement. When and if control reaches the end point of the embedded statement (possibly from execution of a continue statement), another foreach iteration is performed, starting with the step above that advances the enumerator.
  • If the value returned by e.MoveNext() is false, control is transferred to the end point of the foreach statement.

Within the embedded statement of a foreach statement, a break statement (§8.9.1) may be used to transfer control to the end point of the foreach statement (thus ending iteration of the embedded statement), and a continue statement (§8.9.2) may be used to transfer control to the end point of the embedded statement (thus executing another iteration of the foreach statement).

The embedded statement of a foreach statement is reachable if the foreach statement is reachable. Likewise, the end point of a foreach statement is reachable if the foreach statement is reachable.


8.9 Jump statements

Jump statements unconditionally transfer control.

jump-statement:
break-statement
continue-statement
goto-statement
return-statement
throw-statement

The location to which a jump statement transfers control is called the target of the jump statement.

When a jump statement occurs within a block, and when the target of the jump statement is outside that block, the jump statement is said to exit the block. While a jump statement may transfer control out of a block, it can never transfer control into a block.

Execution of jump statements is complicated by the presence of intervening try statements. In the absence of such try statements, a jump statement unconditionally transfers control from the jump statement to its target. In the presence of such intervening try statements, execution is more complex. If the jump statement exits one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. This process is repeated until the finally blocks of all intervening try statements have been executed.

In the example

static void F() {
   while (true) {
      try {
         try {
            Console.WriteLine("Before break");
            break;
         }
         finally {
            Console.WriteLine("Innermost finally block");
         }
      }
      finally {
         Console.WriteLine("Outermost finally block");
      }
   }
   Console.WriteLine("After break");
}

the finally blocks associated with two try statements are executed before control is transferred to the target of the jump statement.


8.9.1 The break statement

The break statement exits the nearest enclosing switch, while, do, for, or foreach statement.

break-statement:
break ;

The target of a break statement is the end point of the nearest enclosing switch, while, do, for, or foreach statement. If a break statement is not enclosed by a switch, while, do, for, or foreach statement, a compile-time error occurs.

When multiple switch, while, do, for, or foreach statements are nested within each other, a break statement applies only to the innermost statement. To transfer control across multiple nesting levels, a goto statement (§8.9.3) must be used.

A break statement cannot exit a finally block (§8.10). When a break statement occurs within a finally block, the target of the break statement must be within the same finally block, or otherwise a compile-time error occurs.

A break statement is executed as follows:

  • If the break statement exits one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. This process is repeated until the finally blocks of all intervening try statements have been executed.
  • Control is transferred to the target of the break statement.

Because a break statement unconditionally transfers control elsewhere, the end point of a break statement is never reachable.


8.9.2 The continue statement

The continue statement starts a new iteration of the nearest enclosing while, do, for, or foreach statement.

continue-statement:
continue ;

The target of a continue statement is the end point of the embedded statement of the nearest enclosing while, do, for, or foreach statement. If a continue statement is not enclosed by a while, do, for, or foreach statement, a compile-time error occurs.

When multiple while, do, for, or foreach statements are nested within each other, a continue statement applies only to the innermost statement. To transfer control across multiple nesting levels, a goto statement (§8.9.3) must be used.

A continue statement cannot exit a finally block (§8.10). When a continue statement occurs within a finally block, the target of the continue statement must be within the same finally block, or otherwise a compile-time error occurs.

A continue statement is executed as follows:

  • If the continue statement exits one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. This process is repeated until the finally blocks of all intervening try statements have been executed.
  • Control is transferred to the target of the continue statement.

Because a continue statement unconditionally transfers control elsewhere, the end point of a continue statement is never reachable.


8.9.3 The goto statement

The goto statement transfers control to a statement that is marked by a label.

goto-statement:
goto identifier ;
goto case constant-expression ;
goto default ;

The target of a goto identifier statement is the labeled statement with the given label. If a label with the given name does not exist in the current function member, or if the goto statement is not within the scope of the label, a compile-time error occurs.

The target of a goto case statement is the statement list of the switch section in the nearest enclosing switch statement that contains a case label with the given constant value. If the goto case statement is not enclosed by a switch statement, if the constant-expression is not implicitly convertible (§6.1) to the governing type of the nearest enclosing switch statement, or if the nearest enclosing switch statement does not contain a case label with the given constant value, a compile-time error occurs.

The target of a goto default statement is the statement list of the switch section in the nearest enclosing switch statement (§8.7.2) that contains a default label. If the goto default statement is not enclosed by a switch statement, or if the nearest enclosing switch statement does not contain a default label, a compile-time error occurs.

A goto statement cannot exit a finally block (§8.10). When a goto statement occurs within a finally block, the target of the goto statement must be within the same finally block, or otherwise a compile-time error occurs.

A goto statement is executed as follows:

  • If the goto statement exits one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. This process is repeated until the finally blocks of all intervening try statements have been executed.
  • Control is transferred to the target of the goto statement.

Because a goto statement unconditionally transfers control elsewhere, the end point of a goto statement is never reachable.


8.9.4 The return statement

The return statement returns control to the caller of the function member in which the return statement appears.

return-statement:
return expressionopt ;

A return statement with no expression can be used only in a function member that does not compute a value, that is, a method with the return type void, the set accessor of a property or indexer, a constructor, or a destructor.

A return statement with an expression can only be used only in a function member that computes a value, that is, a method with a non-void return type, the get accessor of a property or indexer, or a user-defined operator. An implicit conversion (§6.1) must exist from the type of the expression to the return type of the containing function member.

It is an error for a return statement to appear in a finally block (§8.10).

A return statement is executed as follows:

  • If the return statement specifies an expression, the expression is evaluated and the resulting value is converted to the return type of the containing function member by an implicit conversion. The result of the conversion becomes the value returned to the caller.
  • If the return statement is enclosed by one or more try blocks with associated finally blocks, control is initially transferred to the finally block of the innermost try statement. When and if control reaches the end point of a finally block, control is transferred to the finally block of the next enclosing try statement. This process is repeated until the finally blocks of all enclosing try statements have been executed.
  • Control is returned to the caller of the containing function member.

Because a return statement unconditionally transfers control elsewhere, the end point of a return statement is never reachable.


8.9.5 The throw statement

The throw statement throws an exception.

throw-statement:
throw expressionopt ;

A throw statement with an expression throws the exception produced by evaluating the expression. The expression must denote a value of the class type System.Exception or of a class type that derives from System.Exception. If evaluation of the expression produces null, a NullReferenceException is thrown instead.

A throw statement with no expression can be used only in a catch block. It re-throws the exception that is currently being handled by the catch block.

Because a throw statement unconditionally transfers control elsewhere, the end point of a throw statement is never reachable.

When an exception is thrown, control is transferred to the first catch clause in a try statement that can handle the exception. The process that takes place from the point of the exception being thrown to the point of transferring control to a suitable exception handler is known as exception propagation. Propagation of an exception consists of repeatedly evaluating the following steps until a catch clause that matches the exception is found. In the descriptions, the throw point is initially the location at which the exception is thrown.

  • In the current function member, each try statement that encloses the throw point is examined. For each statement S, starting with the innermost try statement and ending with the outermost try statement, the following steps are evaluated:
    • If the try block of S encloses the throw point and if S has one or more catch clauses, the catch clauses are examined in order of appearance to locate a suitable handler for the exception. The first catch clause that specifies the exception type or a base type of the exception type is considered a match. A general catch clause is considered a match for any exception type. If a matching catch clause is located, the exception propagation is completed by transferring control to the block of that catch clause.
    • Otherwise, if the try block or a catch block of S encloses the throw point and if S has a finally block, control is transferred to the finally block. If the finally block throws another exception, processing of the current exception is terminated. Otherwise, when control reaches the end point of the finally block, processing of the current exception is continued.
  • If an exception handler was not located in the current function member invocation, the function member invocation is terminated. The steps above are then repeated for the caller of the function member with a throw point corresponding to the statement from which the function member was invoked.
  • If the exception processing ends up terminating all function member invocations in the current thread or process, indicating that the thread or process has no handler for the exception, then the tread or process is itself terminated in an implementation defined fashion.


8.10 The try statement

The try statement provides a mechanism for catching exceptions that occur during execution of a block. The try statement furthermore provides the ability to specify a block of code that is always executed when control leaves the try statement.

try-statement:
try block catch-clauses
try block finally-clause
try block catch-clauses finally-clause
catch-clauses:
specific-catch-clauses general-catch-clauseopt
specific-catch-clausesopt general-catch-clause
specific-catch-clauses:
specific-catch-clause
specific-catch-clauses specific-catch-clause
specific-catch-clause:
catch ( class-type identifieropt ) block
general-catch-clause:
catch block
finally-clause:
finally block

There are three possible forms of try statements:

  • A try block followed by one or more catch blocks.
  • A try block followed by a finally block.
  • A try block followed by one or more catch blocks followed by a finally block.

When a catch clause specifies a class-type, the type must be System.Exception or a type that derives from System.Exception.

When a catch clause specifies both a class-type and an identifier, an exception variable of the given name and type is declared. The exception variable corresponds to a read-only local variable with a scope that extends over the catch block. During execution of the catch block, the exception variable represents the exception currently being handled. A compile-time error occurs if a catch block attempts to assign to the exception variable or pass the exception variable as a ref or out parameter.

Unless a catch clause includes an exception variable name, it is impossible to access the exception object in the catch block.

A catch clause that specifies neither an exception type nor an exception variable name is called a general catch clause. A try statement can only have one general catch clause, and if one is present it must be the last catch clause. A general catch clause of the form

catch {...}

is precisely equivalent to

catch (System.Exception) {...}

An error occurs if a catch clause specifies a type that is equal to or derived from a type that was specified in an earlier catch clause. Because catch clauses are examined in order of appearance to locate a handler for an exception, without this restriction it would be possible to write unreachable catch clauses.

It is an error for a try statement to contain a general catch clause if the try statement also contains a catch clause for the System.Exception type.

Within a catch block, a throw statement (§8.9.5) with no expression can be used to re-throw the exception that is currently being handled by the catch block.

It is an error for a break, continue, or goto statement to transfer control out of a finally block. When a break, continue, or goto statement occurs in a finally block, the target of the statement must be within the same finally block, or otherwise a compile-time error occurs.

It is an error for a return statement to occur in a finally block.

A try statement is executed as follows:

  • Control is transferred to the try block.
  • When and if control reaches the end point of the try block:
    • If the try statement has a finally block, the finally block is executed.
    • Control is transferred to the end point of the try statement.
  • If an exception is propagated to the try statement during execution of the try block:
    • The catch clauses, if any, are examined in order of appearance to locate a suitable handler for the exception. The first catch clause that specifies the exception type or a base type of the exception type is considered a match. A general catch clause is considered a match for any exception type. If a matching catch clause is located:
      • If the matching catch clause declares an exception variable, the exception object is assigned to the exception variable.
      • Control is transferred to the matching catch block.
      • When and if control reaches the end point of the catch block:
        • If the try statement has a finally block, the finally block is executed.
        • Control is transferred to the end point of the try statement.
      • If an exception is propagated to the try statement during execution of the catch block:
        • If the try statement has a finally block, the finally block is executed.
        • The exception is propagated to the next enclosing try statement.
    • If the try statement has no catch clauses or if no catch clause matches the exception:
      • If the try statement has a finally block, the finally block is executed.
      • The exception is propagated to the next enclosing try statement.

The statements of a finally block are always executed when control leaves a try statement. This is true whether the control transfer occurs as a result of normal execution, as a result of executing a break, continue, goto, or return statement, or as a result of propagating an exception out of the try statement.

If an exception is thrown during execution of a finally block, the exception is propagated to the next enclosing try statement. If another exception was in the process of being propagated, that exception is lost. The process of propagating an exception is further discussed in the description of the throw statement (§8.9.5).

The try block of a try statement is reachable if the try statement is reachable.

A catch block of a try statement is reachable if the try statement is reachable.

The finally block of a try statement is reachable if the try statement is reachable.

The end point of a try statement is reachable both of the following are true:

  • The end point of the try block is reachable or the end point of at least one catch block is reachable.
  • If a finally block is present, the end point of the finally block is reachable.


8.11 The checked and unchecked statements

The checked and unchecked statements are used to control the overflow checking context for integral-type arithmetic operations and conversions.

checked-statement:
checked block
unchecked-statement:
unchecked block

The checked statement causes all expressions in the block to be evaluated in a checked context, and the unchecked statement causes all expressions in the block to be evaluated in an unchecked context.

The checked and unchecked statements are precisely equivalent to the checked and unchecked operators (§7.5.13), except that they operate on blocks instead of expressions.


8.12 The lock statement

The lock statement obtains the mutual-exclusion lock for a given object, executes a statement, and then releases the lock.

lock-statement:
lock ( expression ) embedded-statement

The expression of a lock statement must denote a value of a reference-type. An implicit boxing conversion (§6.1.5) is never performed for the expression of a lock statement, and thus it is an error for the expression to denote a value of a value-type.

A lock statement of the form

lock (x) ...

where x is an expression of a reference-type, is precisely equivalent to

System.CriticalSection.Enter(x);
try {
   ...
}
finally {
   System.CriticalSection.Exit(x);
}

except that x is only evaluated once. The exact behavior of the Enter and Exit methods of the System.CriticalSection class is implementation defined.

The System.Type object of a class can conveniently be used as the mutual-exclusion lock for static methods of the class. For example:

class Cache
{
   public static void Add(object x) {
      lock (typeof(Cache)) {
         ...
      }
   }
   public static void Remove(object x) {
      lock (typeof(Cache)) {
         ...
      }
   }
}


9. Namespaces

C# programs are organized using namespaces. 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.

Using directives are provided to facilitate the use of namespaces.


9.1 Compilation units

A compilation-unit defines the overall structure of a source file. A compilation unit consists of zero or more using-directives followed by zero or more namespace-member-declarations.

compilation-unit:
using-directivesopt namespace-member-declarationsopt

A C# program consists of one or more compilation units, each contained in a separate source file. When a C# program is compiled, all of the compilation units are processed together. Thus, compilation units can depend on each other, possibly in a circular fashion.

The using-directives of a compilation unit affect the namespace-member-declarations of that compilation unit, but have no effect on other compilation units.

The namespace-member-declarations of each compilation unit of a program contribute members to a single declaration space called the global namespace. For example:

File A.cs:

class A {}

File B.cs:

class B {}

The two compilation units contribute to the single global namespace, in this case declaring two classes with the fully qualified names A and B. Because the two compilation units contribute to the same declaration space, it would have been an error if each contained a declaration of a member with the same name.


9.2 Namespace declarations

A namespace-declaration consists of the keyword namespace, followed by a namespace name and body, optionally followed by a semicolon.

namespace-declaration:
namespace qualified-identifier namespace-body ;opt
qualified-identifier:
identifier
qualified-identifier . identifier
namespace-body:
{ using-directivesopt namespace-member-declarationsopt }

A namespace-declaration may occur as a top-level declaration in a compilation-unit or as a member declaration within another namespace-declaration. When a namespace-declaration occurs as a top-level declaration in a compilation-unit, the namespace becomes a member of the global namespace. When a namespace-declaration occurs within another namespace-declaration, the inner namespace becomes a member of the outer namespace. In either case, the name of a namespace must be unique within the containing namespace.

Namespaces are implicitly public and the declaration of a namespace cannot include any access modifiers.

Within a namespace-body, the optional using-directives import the names of other namespaces and types, allowing them to be referenced directly instead of through qualified names. The optional namespace-member-declarations contribute members to the declaration space of the namespace. Note that all using-directives must appear before any member declarations.

The qualified-identifier of a namespace-declaration may be single identifier or a sequence of identifiers separated by "." tokens. The latter form permits a program to define a nested namespace without lexically nesting several namespace declarations. For example,

namespace N1.N2
{
   class A {}
   class B {}
}

is semantically equivalent to

namespace N1
{
   namespace N2
   {
      class A {}
      class B {}
   }
}

Namespaces are open-ended, and two namespace declarations with the same fully qualified name contribute to the same declaration space (§3.1). In the example

namespace N1.N2
{
   class A {}
}
namespace N1.N2
{
   class B {}
}

the two namespace declarations above contribute to the same declaration space, in this case declaring two classes with the fully qualified names N1.N2.A and N1.N2.B. Because the two declarations contribute to the same declaration space, it would have been an error if each contained a declaration of a member with the same name.


9.3 Using directives

Using directives facilitate the use of namespaces and types defined in other namespaces. Using directives impact the name resolution process of namespace-or-type-names (§3.6) and simple-names (§7.5.2), but unlike declarations, using directives do not contribute new members to the underlying declaration spaces of the compilation units or namespaces within which they are used.

using-directives:
using-directive
using-directives using-directive
using-directive:
using-alias-directive
using-namespace-directive

A using-alias-directive (§9.3.1) introduces an alias for a namespace or type.

A using-namespace-directive (§9.3.2) imports the type members of a namespace.

The scope of a using-directive extends over the namespace-member-declarations of its immediately containing compilation unit or namespace body. The scope of a using-directive specifically does not include its peer using-directives. Thus, peer using-directives do not affect each other, and the order in which they are written is insignificant.


9.3.1 Using alias directives

A using-alias-directive introduces an identifier that serves as an alias for a namespace or type within the immediately enclosing compilation unit or namespace body.

using-alias-directive:
using identifier = namespace-or-type-name ;

Within member declarations in a compilation unit or namespace body that contains a using-alias-directive, the identifier introduced by the using-alias-directive can be used to reference the given namespace or type. For example:

namespace N1.N2
{
   class A {}
}
namespace N3
{
   using A = N1.N2.A;
   class B: A {}
}

Here, within member declarations in the N3 namespace, A is an alias for N1.N2.A, and thus class N3.B derives from class N1.N2.A. The same effect can be obtained by creating an alias R for N1.N2 and then referencing R.A:

namespace N3
{
   using R = N1.N2;
   class B: R.A {}
}

The identifier of a using-alias-directive must be unique within the declaration space of the compilation unit or namespace that immediately contains the using-alias-directive. For example:

namespace N3
{
   class A {}
}
namespace N3
{
   using A = N1.N2.A;      // Error, A already exists
}

Here, N3 already contains a member A, so it is an error for a using-alias-directive to use that identifier. It is likewise an error for two or more using-alias-directives in the same compilation unit or namespace body to declare aliases by the same name.

A using-alias-directive makes an alias available within a particular compilation unit or namespace body, but it does not contribute any new members to the underlying declaration space. In other words, a using-alias-directive is not transitive but rather affects only the compilation unit or namespace body in which it occurs. In the example

namespace N3
{
   using R = N1.N2;
}
namespace N3
{
   class B: R.A {}         // Error, R unknown
}

the scope of the using-alias-directive that introduces R only extends to member declarations in the namespace body in which it is contained, and R is thus unknown in the second namespace declaration. However, placing the using-alias-directive in the containing compilation unit causes the alias to become available within both namespace declarations:

using R = N1.N2;
namespace N3
{
   class B: R.A {}
}
namespace N3
{
   class C: R.A {}
}

Just like regular members, names introduced by using-alias-directives are hidden by similarly named members in nested scopes. In the example

using R = N1.N2;
namespace N3
{
   class R {}
   class B: R.A {}      // Error, R has no member A
}

the reference to R.A in the declaration of B causes an error because R refers to N3.F, not N1.N2.

The order in which using-alias-directives are written has no significance, and resolution of the namespace-or-type-name referenced by a using-alias-directive is neither affected by the using-alias-directive itself nor by other using-directives in the immediately containing compilation unit or namespace body. In other words, the namespace-or-type-name of a using-alias-directive is resolved as if the immediately containing compilation unit or namespace body had no using-directives. In the example

namespace N1.N2 {}
namespace N3
{
   using R1 = N1;         // OK
   using R2 = N1.N2;      // OK
   using R3 = R1.N2;      // Error, R1 unknown
}

the last using-alias-directive is in error because it is not affected by the first using-alias-directive.

A using-alias-directive can create an alias for any namespace or type, including the namespace within which it appears and any namespace or type nested within that namespace.

Accessing a namespace or type through an alias yields exactly the same result as accessing the namespace or type through its declared name. In other words, given

namespace N1.N2
{
   class A {}
}
namespace N3
{
   using R1 = N1;
   using R2 = N1.N2;
   class B
   {
      N1.N2.A a;         // refers to N1.N2.A
      R1.N2.A b;         // refers to N1.N2.A
      R2.A c;            // refers to N1.N2.A
   }
}

the names N1.N2.A, R1.N2.A, and R2.A are completely equivalent and all refer to the class whose fully qualified name is N1.N2.A.


9.3.2 Using namespace directives

A using-namespace-directive imports the types contained in a namespace into the immediately enclosing compilation unit or namespace body, enabling the identifier of each type to be used without qualification.

using-namespace-directive:
using namespace-name ;

Within member declarations in compilation unit or namespace body that contains a using-namespace-directive, the types contained in the given namespace can be referenced directly. For example:

namespace N1.N2
{
   class A {}
}
namespace N3
{
   using N1.N2;
   class B: A {}
}

Here, within member declarations in the N3 namespace, the type members of N1.N2 are directly available, and thus class N3.B derives from class N1.N2.A.

A using-namespace-directive imports the types contained in the given namespace, but specifically does not import nested namespaces. In the example

namespace N1.N2
{
   class A {}
}
namespace N3
{
   using N1;
   class B: N2.A {}      // Error, N2 unknown
}

the using-namespace-directive imports the types contained in N1, but not the namespaces nested in N1. Thus, the reference to N2.A in the declaration of B is in error because no members named N2 are in scope.

Unlike a using-alias-directive, a using-namespace-directive may import types whose identifiers are already defined within the enclosing compilation unit or namespace body. In effect, names imported by a using-namespace-directive are hidden by similarly named members in the enclosing compilation unit or namespace body. For example:

namespace N1.N2
{
   class A {}
   class B {}
}
namespace N3
{
   using N1.N2;
   class A {}
}

Here, within member declarations in the N3 namespace, A refers to N3.A rather than N1.N2.A.

When more than one namespace imported by using-namespace-directives in the same compilation unit or namespace body contain types by the same name, references to that name are considered ambiguous. In the example

namespace N1
{
   class A {}
}
namespace N2
{
   class A {}
}
namespace N3
{
   using N1;
   using N2;
   class B: A {}            // Error, A is ambiguous
}

both N1 and N2 contain a member A, and because N3 imports both, referencing A in N3 is an error. In this situation, the conflict can be resolved either through qualification of references to A, or by introducing a using-alias-directive that picks a particular A. For example:

namespace N3
{
   using N1;
   using N2;
   using A = N1.A;
   class B: A {}            // A means N1.A
}

Like a using-alias-directive, a using-namespace-directive does not contribute any new members to the underlying declaration space of the compilation unit or namespace, but rather affects only the compilation unit or namespace body in which it appears.

The namespace-name referenced by a using-namespace-directive is resolved in the same way as the namespace-or-type-name referenced by a using-alias-directive. Thus, using-namespace-directives in the same compilation unit or namespace body do not affect each other and can be written in any order.


9.4 Namespace members

A namespace-member-declaration is either a namespace-declaration (§9.2) or a type-declaration (§9.5).

namespace-member-declarations:
namespace-member-declaration
namespace-member-declarations namespace-member-declaration
namespace-member-declaration:
namespace-declaration
type-declaration

A compilation unit or a namespace body can contain namespace-member-declarations, and such declarations contribute new members to the underlying declaration space of the containing compilation unit or namespace body.


9.5 Type declarations

A type-declaration is either a class-declaration (§10.1), a struct-declaration (§11.1), an interface-declaration (§13.1), an enum-declaration (§14.1), or a delegate-declaration (§15.1).

type-declaration:
class-declaration
struct-declaration
interface-declaration
enum-declaration
delegate-declaration

A type-declaration can occur as a top-level declaration in a compilation unit or as a member declaration within a namespace, class, or struct.

When a type declaration for a type T occurs as a top-level declaration in a compilation unit, the fully qualified name of the newly declared type is simply T. When a type declaration for a type T occurs within a namespace, class, or struct, the fully qualified name of the newly declared type is N.T, where N is the fully qualified name of the containing namespace, class, or struct.

A type declared within a class or struct is called a nested type (§10.2.6).

The permitted access modifiers and the default access for a type declaration depend on the context in which the declaration takes place (§3.3.1):

  • Types declared in compilation units or namespaces can have public or internal access. The default is internal access.
  • Types declared in classes can have public, protected internal, protected, internal, or private access. The default is private access.
  • Types declared in structs can have public, internal, or private access. The default is private access.


10. Classes

A class is a data structure that contains data members (constants, fields, and events), function members (methods, properties, indexers, operators, constructors, and destructors), and nested types. Class types support inheritance, a mechanism whereby derived classes can extend and specialize base classes.


10.1 Class declarations

A class-declaration is a type-declaration (§9.5) that declares a new class.

class-declaration:
attributesopt class-modifiersopt class identifier class-baseopt class-body ;opt

A class-declaration consists of an optional set of attributes (§17), followed by an optional set of class-modifiers (§10.1.1), followed by the keyword class and an identifier that names the class, followed by an optional class-base specification (§10.1.2), followed by a class-body (§10.1.3), optionally followed by a semicolon.


10.1.1 Class modifiers

A class-declaration may optionally include a sequence of class modifiers:

class-modifiers:
class-modifier
class-modifiers class-modifier
class-modifier:
new
public
protected
internal
private
abstract
sealed

It is an error for the same modifier to appear multiple times in a class declaration.

The new modifier is only permitted on nested classes. It specifies that the class hides an inherited member by the same name, as described in §10.2.2.

The public, protected, internal, and private modifiers control the accessibility of the class. Depending on the context in which the class declaration occurs, some of these modifiers may not be permitted (§3.3.1).

The abstract and sealed modifiers are discussed in the following sections.


10.1.1.1 Abstract classes

The abstract modifier is used to indicate that a class is incomplete and intended only to be a base class of other classes. An abstract class differs from a non-abstract class is the following ways:

  • An abstract class cannot be instantiated, and it is an error to use the new operator on an abstract class. While it is possible to have variables and values whose compile-time types are abstract, such variables and values will necessarily either be null or contain references to instances of non-abstract classes derived from the abstract types.
  • An abstract class is permitted (but not required) to contain abstract methods and accessors.
  • An abstract class cannot be sealed.

When a non-abstract class is derived from an abstract class, the non-abstract class must include actual implementations of all inherited abstract methods and accessors. Such implementations are provided by overriding the abstract methods and accessors. In the example

abstract class A
{
   public abstract void F();
}
abstract class B: A
{
   public void G() {}
}
class C: B
{
   public override void F() {
      // actual implementation of F
   }
}

the abstract class A introduces an abstract method F. Class B introduces an additional method G, but doesn’t provide an implementation of F. B must therefore also be declared abstract. Class C overrides F and provides an actual implementation. Since there are no outstanding abstract methods or accessors in C, C is permitted (but not required) to be non-abstract.


10.1.1.2 Sealed classes

The sealed modifier is used to prevent derivation from a class. An error occurs if a sealed class is specified as the base class of another class.

A sealed class cannot also be an abstract class.

The sealed modifier is primarily used to prevent unintended derivation, but it also enables certain run-time optimizations. In particular, because a sealed class is known to never have any derived classes, it is possible to transform virtual function member invocations on sealed class instances into non-virtual invocations.


10.1.2 Class base specification

A class declaration may include a class-base specification which defines the direct base class of the class and the interfaces implemented by the class.

class-base:
: class-type
: interface-type-list
: class-type , interface-type-list
interface-type-list:
interface-type
interface-type-list , interface-type


10.1.2.1 Base classes

When a class-type is included in the class-base, it specifies the direct base class of the class being declared. If a class declaration has no class-base, or if the class-base lists only interface types, the direct base class is assumed to be object. A class inherits members from its direct base class, as described in §10.2.1.

In the example

class A {}
class B: A {}

class A is said to be the direct base class of B, and B is said to be derived from A. Since A does not explicitly specify a direct base class, its direct base class is implicitly object.

The direct base class of a class type must be at least as accessible as the class type itself (§3.3.4). For example, it is an error for a public class to derive from a private or internal class.

The base classes of a class are the direct base class and its base classes. In other words, the set of base classes is the transitive closure of the direct base class relationship. Referring to the example above, the base classes of B are A and object.

Except for class object, every class has exactly one direct base class. The object class has no direct base class and is the ultimate base class of all other classes.

When a class B derives from a class A, it is an error for A to depend on B. A class directly depends on its direct base class (if any) and directly depends on the class within which it is immediately nested (if any). Given this definition, the complete set of classes upon which a class depends is the transitive closure of the directly depends on relationship.

The example

class A: B {}
class B: C {}
class C: A {}

is in error because the classes circularly depend on themselves. Likewise, the example

class A: B.C {}
class B: A
{
   public class C {}
}

is in error because A depends on B.C (its direct base class), which depends on B (its immediately enclosing class), which circularly depends on A.

Note that a class does not depend on the classes that are nested within it. In the example

class A
{
   class B: A {}
}

B depends on A (because A is both its direct base class and its immediately enclosing class), but A does not depend on B (since B is neither a base class nor an enclosing class of A). Thus, the example is valid.

It is not possible to derive from a sealed class. In the example

sealed class A {}
class B: A {}         // Error, cannot derive from a sealed class

class B is in error because it attempts to derive from the sealed class A.


10.1.2.2 Interface implementations

A class-base specification may include a list of interface types, in which case the class is said to implement the given interface types. Interface implementations are discussed further in §13.4.


10.1.3 Class body

The class-body of a class defines the members of the class.

class-body:
{ class-member-declarationsopt }


10.2 Class members

The members of a class consist of the members introduced by its class-member-declarations and the members inherited from the direct base class.

class-member-declarations:
class-member-declaration
class-member-declarations class-member-declaration
class-member-declaration:
constant-declaration
field-declaration
method-declaration
property-declaration
event-declaration
indexer-declaration
operator-declaration
constructor-declaration
destructor-declaration
static-constructor-declaration
type-declaration

The members of a class are divided into the following categories:

  • Constants, which represent constant values associated with the class (§10.3).
  • Fields, which are the variables of the class (§10.4).
  • Methods, which implement the computations and actions that can be performed by the class (§10.5).
  • Properties, which define named attributes and the actions associated with reading and writing those attributes (§10.6).
  • Events, which define notifications that are generated by the class (§10.7).
  • Indexers, which permit instances of the class to be indexed in the same way as arrays (§10.8).
  • Operators, which define the expression operators that can be applied to instances of the class (§10.9).
  • Instance constructors, which implement the actions required to initialize instances of the class (§10.10)
  • Destructors, which implement the actions to perform before instances of the class are permanently discarded (§10.11).
  • Static constructors, which implement the actions required to initialize the class itself (§10.12).
  • Types, which represent the types that are local to the class (§9.5).

Members that contain executable code are collectively known as the function members of the class. The function members of a class are the methods, properties, indexers, operators, constructors, and destructors of the class.

A class-declaration creates a new declaration space (§3.1), and the class-member-declarations immediately contained by the class-declaration introduce new members into this declaration space. The following rules apply to class-member-declarations:

  • Constructors and destructors must have the same name as the immediately enclosing class. All other members must have names that differ from the name of the immediately enclosing class.
  • The name of a constant, field, property, event, or type must differ from the names of all other members declared in the same class.
  • The name of a method must differ from the names of all other non-methods declared in the same class. In addition, the signature (§3.4) of a method must differ from the signatures of all other methods declared in the same class.
  • The signature of an indexer must differ from the signatures of all other indexers declared in the same class.
  • The signature of an operator must differ from the signatures of all other operators declared in the same class.

The inherited members of a class (§10.2.1) are specifically not part of the declaration space of a class. Thus, a derived class is allowed to declare a member with the same name or signature as an inherited member (which in effect hides the inherited member).


10.2.1 Inheritance

A class inherits the members of its direct base class. Inheritance means that a class implicitly contains all members of its direct base class, except for the constructors and destructors of the base class. Some important aspects of inheritance are:

  • Inheritance is transitive. If C is derived from B, and B is derived from A, then C inherits the members declared in B as well as the members declared in A.
  • A derived class extends its direct base class. A derived class can add new members to those it inherits, but it cannot remove the definition of an inherited member.
  • Constructors and destructors are not inherited, but all other members are, regardless of their declared accessibility (§3.3). However, depending on their declared accessibility, inherited members may not be accessible in a derived class.
  • A derived class can hide (§3.5.1.2) inherited members by declaring new members with the same name or signature. Note however that hiding an inherited member does not remove the member—it merely makes the member inaccessible in the derived class.
  • An instance of a class contains a copy of all instance fields declared in the class and its base classes, and an implicit conversion (§6.1.4) exists from a derived class type to any of its base class types. Thus, a reference to a derived class instance can be treated as a reference to a base class instance.
  • A class can declare virtual methods, properties, and indexers, and derived classes can override the implementation of these function members. This enables classes to exhibit polymorphic behavior wherein the actions performed by a function member invocation varies depending on the run-time type of the instance through which the function member is invoked.


10.2.2 The new modifier

A class-member-declaration is permitted to declare a member with the same name or signature as an inherited member. When this occurs, the derived class member is said to hide the base class member. Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. To suppress the warning, the declaration of the derived class member can include a new modifier to indicate that the derived member is intended to hide the base member. This topic is discussed further in §3.5.1.2.

If a new modifier is included in a declaration that doesn’t hide an inherited member, a warning is issued to that effect. This warning is suppressed by removing the new modifier.

It is an error to use the new and override modifiers in the same declaration.


10.2.3 Access modifiers

A class-member-declaration can have any one of the five possible types of declared accessibility (§3.3.1): public, protected internal, protected, internal, or private. Except for the protected internal combination, it is an error to specify more than one access modifier. When a class-member-declaration does not include any access modifiers, the declaration defaults to private declared accessibility.


10.2.4 Constituent types

Types that are referenced in the declaration of a member are called the constituent types of the member. Possible constituent types are the type of a constant, field, property, event, or indexer, the return type of a method or operator, and the parameter types of a method, indexer, operator, or constructor.

The constituent types of a member must be at least as accessible as the member itself (§3.3.4).


10.2.5 Static and instance members

Members of a class are either static members or instance members. Generally speaking, it is useful to think of static members as belonging to classes and instance members as belonging to objects (instances of classes).

When a field, method, property, event, operator, or constructor declaration includes a static modifier, it declares a static member. In addition, a constant or type declaration implicitly declares a static member. Static members have the following characteristics:

  • When a static member is referenced in a member-access (§7.5.4) of the form E.M, E must denote a type. It is an error for E to denote an instance.
  • A static field identifies exactly one storage location. No matter how many instances of a class are created, there is only ever one copy of a static field.
  • A static function member (method, property, indexer, operator, or constructor) does not operate on a specific instance, and it is an error to refer to this in a static function member.

When a field, method, property, event, indexer, constructor, or destructor declaration does not include a static modifier, it declares an instance member. An instance member is sometimes called a non-static member. Instance members have the following characteristics:

  • When an instance member is referenced in a member-access (§7.5.4) of the form E.M, E must denote an instance. It is an error for E to denote a type.
  • Every instance of a class contains a separate copy of all instance fields of the class.
  • An instance function member (method, property accessor, indexer accessor, constructor, or destructor) operates on a given instance of the class, and this instance can be accessed as this (§7.5.7).

The following example illustrates the rules for accessing static and instance members:

class Test
{
   int x;
   static int y;
   void F() {
      x = 1;         // Ok, same as this.x = 1
      y = 1;         // Ok, same as Test.y = 1
   }
   static void G() {
      x = 1;         // Error, cannot access this.x
      y = 1;         // Ok, same as Test.y = 1
   }
   static void Main() {
      Test t = new Test();
      t.x = 1;         // Ok
      t.y = 1;         // Error, cannot access static member through instance
      Test.x = 1;      // Error, cannot access instance member through type
      Test.y = 1;      // Ok
   }
}

The F method shows that in an instance function member, a simple-name (§7.5.2) can be used to access both instance members and static members. The G method shows that in a static function member, it is an error to access an instance member through a simple-name. The Main method shows that in a member-access (§7.5.4), instance members must be accessed through instances, and static members must be accessed through types.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

10.2.6 Nested types

© 2000 Microsoft Corporation. All rights reserved.


10.3 Constants

Constants are members that represent constant values. A constant-declaration introduces one or more constants of a given type.

constant-declaration:
attributesopt constant-modifiersopt const type constant-declarators ;
constant-modifiers:
constant-modifier
constant-modifiers constant-modifier
constant-modifier:
new
public
protected
internal
private
constant-declarators:
constant-declarator
constant-declarators , constant-declarator
constant-declarator:
identifier = constant-expression

A constant-declaration may include set of attributes (§17), a new modifier (§10.2.2), and a valid combination of the four access modifiers (§10.2.3). The attributes and modifiers apply to all of the members declared by the constant-declaration. Even though constants are considered static members, a constant-declaration neither requires nor allows a static modifier.

The type of a constant-declaration specifies the type of the members introduced by the declaration. The type is followed by a list of constant-declarators, each of which introduces a new member. A constant-declarator consists of an identifier that names the member, followed by an "=" token, followed by a constant-expression (§7.15) that gives the value of the member.

The type specified in a constant declaration must be sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, an enum-type, or a reference-type. Each constant-expression must yield a value of the target type or of a type that can be converted to the target type by an implicit conversion (§6.1).

The type of a constant must be at least as accessible as the constant itself (§3.3.4).

A constant can itself participate in a constant-expression. Thus, a constant may be used in any construct that requires a constant-expression. Examples of such constructs include case labels, goto case statements, enum member declarations, attributes, and other constant declarations.

As described in §7.15, a constant-expression is an expression that can be fully evaluated at compile-time. Since the only way to create a non-null value of a reference-type other than string is to apply the new operator, and since the new operator is not permitted in a constant-expression, the only possible value for constants of reference-types other than string is null.

When a symbolic name for a constant value is desired, but when type of the value is not permitted in a constant declaration or when the value cannot be computed at compile-time by a constant-expression, a readonly field (§10.4) may be used instead.

A constant declaration that declares multiple constants is equivalent to multiple declarations of single constants with the same attributes, modifiers, and type. For example

class A
{
   public const double X = 1.0, Y = 2.0, Z = 3.0;
}

is equivalent to

class A
{
   public const double X = 1.0;
   public const double Y = 2.0;
   public const double Z = 3.0;
}

Constants are permitted to depend on other constants within the same project as long as the dependencies are not of a circular nature. The compiler automatically arranges to evaluate the constant declarations in the appropriate order. In the example

class A
{
   public const int X = B.Z + 1;
   public const int Y = 10;
}
class B
{
   public const int Z = A.Y + 1;
}

the compiler first evaluates Y, then evaluates Z, and finally evaluates X, producing the values 10, 11, and 12. Constant declarations may depend on constants from other projects, but such dependencies are only possible in one direction. Referring to the example above, if A and B were declared in separate projects, it would be possible for A.X to depend on B.Z, but B.Z could then not simultaneously depend on A.Y.


10.4 Fields

Fields are members that represent variables. A field-declaration introduces one or more fields of a given type.

field-declaration:
attributesopt field-modifiersopt type variable-declarators ;
field-modifiers:
field-modifier
field-modifiers field-modifier
field-modifier:
new
public
protected
internal
private
static
readonly
variable-declarators:
variable-declarator
variable-declarators , variable-declarator
variable-declarator:
identifier
identifier = variable-initializer
variable-initializer:
expression
array-initializer

A field-declaration may include set of attributes (§17), a new modifier (§10.2.2), a valid combination of the four access modifiers (§10.2.3), a static modifier (§10.4.1), and a readonly modifier (§10.4.2). The attributes and modifiers apply to all of the members declared by the field-declaration.

The type of a field-declaration specifies the type of the members introduced by the declaration. The type is followed by a list of variable-declarators, each of which introduces a new member. A variable-declarator consists of an identifier that names the member, optionally followed by an "=" token and a variable-initializer (§10.4.4) that gives the initial value of the member.

The type of a field must be at least as accessible as the field itself (§3.3.4).

The value of a field is obtained in an expression using a simple-name (§7.5.2) or a member-access (§7.5.4). The value of a field is modified using an assignment (§7.13).

A field declaration that declares multiple fields is equivalent to multiple declarations of single fields with the same attributes, modifiers, and type. For example

class A
{
   public static int X = 1, Y, Z = 100;
}

is equivalent to

class A
{
   public static int X = 1;
   public static int Y;
   public static int Z = 100;
}


10.4.1 Static and instance fields

When a field-declaration includes a static modifier, the fields introduced by the declaration are static fields. When no static modifier is present, the fields introduced by the declaration are instance fields. Static fields and instance fields are two of the several kinds of variables (§5) supported by C#, and are at times referred to as static variables and instance variables.

A static field identifies exactly one storage location. No matter how many instances of a class are created, there is only ever one copy of a static field. A static field comes into existence when the type in which it is declared is loaded, and ceases to exist when the type in which it is declared is unloaded.

Every instance of a class contains a separate copy of all instance fields of the class. An instance field comes into existence when a new instance of its class is created, and ceases to exist when there are no references to that instance and the destructor of the instance has executed.

When a field is referenced in a member-access (§7.5.4) of the form E.M, if M is a static field, E must denote a type, and if M is an instance field, E must denote an instance.

The differences between static and instance members are further discussed in §10.2.5.


10.4.2 Readonly fields

When a field-declaration includes a readonly modifier, assignments to the fields introduced by the declaration can only occur as part of the declaration or in a constructor in the same class. Specifically, assignments to a readonly field are permitted only in the following contexts:

  • In the variable-declarator that introduces the field (by including a variable-initializer in the declaration).
  • For an instance field, in the instance constructors of the class that contains the field declaration, or for a static field, in the static constructor of the class the contains the field declaration. These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

Attempting to assign to a readonly field or pass it as an out or ref parameter in any other context is an error.


10.4.2.1 Using static readonly fields for constants

A static readonly field is useful when a symbolic name for a constant value is desired, but when the type of the value is not permitted in a const declaration or when the value cannot be computed at compile-time by a constant-expression. In the example

public class Color
{
   public static readonly Color Black = new Color(0, 0, 0);
   public static readonly Color White = new Color(255, 255, 255);
   public static readonly Color Red = new Color(255, 0, 0);
   public static readonly Color Green = new Color(0, 255, 0);
   public static readonly Color Blue = new Color(0, 0, 255);
   private byte red, green, blue;
   public Color(byte r, byte g, byte b) {
      red = r;
      green = g;
      blue = b;
   }
}

the Black, Write, Red, Green, and Blue members cannot be declared as const members because their values cannot be computed at compile-time. However, declaring the members as static readonly fields has much the same effect.


10.4.2.2 Versioning of constants and static readonly fields

Constants and readonly fields have different binary versioning semantics. When an expression references a constant, the value of the constant is obtained at compile-time, but when an expression references a readonly field, the value of the field is not obtained until run-time. Consider an application that consists of two separate projects:

namespace Project1
{
   public class Utils
   {
      public static readonly int X = 1;
   }
}
namespace Project2
{
   class Test
   {
      static void Main() {
         Console.WriteLine(Project1.Utils.X);
      }
   }
}

The Project1 and Project2 namespaces denote two projects that are compiled separately. Because Project1.Utils.X is declared as a static readonly field, the value output by the Console.WriteLine statement is not known at compile-time, but rather is obtained at run-time. Thus, if the value of X is changed and Project1 is recompiled, the Console.WriteLine statement will output the new value even if Project2 isn’t recompiled. However, had X been a constant, the value of X would have been obtained at the time Project2 was compiled, and would remain unaffected by changes in Project1 until Project2 is recompiled.


10.4.3 Field initialization

The initial value of a field is the default value (§5.2) of the field’s type. When a class is loaded, all static fields are initialized to their default values, and when an instance of a class is created, all instance fields are initialized to their default values. It is not possible to observe the value of a field before this default initialization has occurred, and a field is thus never "uninitialized". The example

class Test
{
   static bool b;
   int i;
   static void Main() {
      Test t = new Test();
      Console.WriteLine("b = {0}, i = {1}", b, t.i);
   }
}

produces the output

b = False, i = 0

because b is automatically initialized to its default value when the class is loaded and i is automatically initialized to its default value when an instance of the class is created.


10.4.4 Variable initializers

Field declarations may include variable-initializers. For static fields, variable initializers correspond to assignment statements that are executed when the class is loaded. For instance fields, variable initializers correspond to assignment statements that are executed when an instance of the class is created.

The example

class Test
{
   static double x = Math.Sqrt(2.0);
   int i = 100;
   string s = "Hello";
   static void Main() {
      Test t = new Test();
      Console.WriteLine("x = {0}, i = {1}, s = {2}", x, t.i, t.s);
   }
}

produces the output

x = 1.414213562373095, i = 100, s = Hello

because an assignment to x occurs when the class is loaded and assignments to i and s occur when an new instance of the class is created.

The default value initialization described in §10.4.3 occurs for all fields, including fields that have variable initializers. Thus, when a class is loaded, all static fields are first initialized to their default values, and then the static field initializers are executed in textual order. Likewise, when an instance of a class is created, all instance fields are first initialized to their default values, and then the instance field initializers are executed in textual order.

It is possible for static fields with variable initializers to be observed in their default value state, though this is strongly discouraged as a matter of style. The example

class Test
{
   static int a = b + 1;
   static int b = a + 1;
   static void Main() {
      Console.WriteLine("a = {0}, b = {1}, a, b);
   }
}

exhibits this behavior. Despite the circular definitions of a and b, the program is legal. It produces the output

a = 1, b = 2


10.4.4.1 Static field initialization

The static field variable initializers of a class correspond to a sequence of assignments that are executed immediately upon entry to the static constructor of the class. The variable initializers are executed in the textual order they appear in the class declaration. The class loading and initialization process is described further in §10.12.


10.4.4.2 Instance field initialization

The instance field variable initializers of a class correspond to a sequence of assignments that are executed immediately upon entry to one of the instance constructors of the class. The variable initializers are executed in the textual order they appear in the class declaration. The class instance creation and initialization process is described further in §10.10.

A variable initializer for an instance field cannot reference the instance being created. Thus, it is an error to reference this in a variable initializer, as is it an error for a variable initializer to reference any instance member through a simple-name. In the example

class A
{
   int x = 1;
   int y = x + 1;      // Error, reference to instance member of this
}

the variable initializer for y is in error because it references a member of the instance being created.


10.5 Methods

Methods implement the computations and actions that can be performed by a class. Methods are declared using method-declarations:

method-declaration:
method-header method-body
method-header:
attributesopt method-modifiersopt return-type member-name ( formal-parameter-listopt )
method-modifiers:
method-modifier
method-modifiers method-modifier
method-modifier:
new
public
protected
internal
private
static
virtual
override
abstract
extern
return-type:
type
void
member-name:
identifier
interface-type . identifier
method-body:
block
;

A method-declaration may include set of attributes (§17), a new modifier (§10.2.2), a valid combination of the four access modifiers (§10.2.3), one of the static (§10.5.2), virtual (§10.5.3), override (§10.5.4), or abstract (§10.5.5) modifiers, and an extern (§10.5.6) modifier.

The return-type of a method declaration specifies the type of the value computed and returned by the method. The return-type is void if the method does not return a value.

The member-name specifies the name of the method. Unless the method is an explicit interface member implementation, the member-name is simply an identifier. For an explicit interface member implementation (§13.4.1) , the member-name consists of an interface-type followed by a "." and an identifier.

The optional formal-parameter-list specifies the parameters of the method (§10.5.1).

The return-type and each of the types referenced in the formal-parameter-list of a method must be at least as accessible as the method itself (§3.3.4).

For abstract and extern methods, the method-body consists simply of a semicolon. For all other methods, the method-body consists of a block which specifies the statements to execute when the method is invoked.

The name and the formal parameter list of method defines the signature (§3.4) of the method. Specifically, the signature of a method consists of its name and the number, modifiers, and types of its formal parameters. The return type is not part of a method’s signature, nor are the names of the formal parameters.

The name of a method must differ from the names of all other non-methods declared in the same class. In addition, the signature of a method must differ from the signatures of all other methods declared in the same class.


10.5.1 Method parameters

The parameters of a method, if any, are declared by the method’s formal-parameter-list.

formal-parameter-list:
formal-parameter
formal-parameter-list , formal-parameter
formal-parameter:
attributesopt parameter-modifieropt type identifier
parameter-modifier:
ref
out
params

The formal parameter list consists of zero or more formal-parameters, separated by commas. A formal-parameter consists of an optional set of attributes (§17), an optional modifier, a type, and an identifier. Each formal-parameter declares a parameter of the given type with the given name.

A method declaration creates a separate declaration space for parameters and local variables. Names are introduced into this declaration space by the formal parameter list of the method and by local variable declarations in the block of the method. All names in the declaration space of a method must be unique. Thus, it is an error for a parameter or local variable to have the same name as another parameter or local variable.

A method invocation (§7.5.5.1) creates a copy, specific to that invocation, of the formal parameters and local variables of the method, and the argument list of the invocation assigns values or variable references to the newly created formal parameters. Within the block of a method, formal parameters can be referenced by their identifiers in simple-name expressions (§7.5.2).

There are four kinds of formal parameters:

  • Value parameters, which are declared without any modifiers.
  • Reference parameters, which are declared with the ref modifier.
  • Output parameters, which are declared with the out modifier.
  • Params parameters, which are declared with the params modifier.

As described in §3.4, parameter modifiers are part of a method’s signature.


10.5.1.1 Value parameters

A parameter declared with no modifiers is a value parameter. A value parameter corresponds to a local variable that gets its initial value from the corresponding argument supplied in the method invocation.

When a formal parameter is a value parameter, the corresponding argument in a method invocation must be an expression of a type that is implicitly convertible (§6.1) to the formal parameter type.

A method is permitted to assign new values to a value parameter. Such assignments only affect the local storage location represented by the value parameter—they have no effect on the actual argument given in the method invocation.


10.5.1.2 Reference parameters

A parameter declared with a ref modifier is a reference parameter. Unlike a value parameter, a reference parameter does not create a new storage location. Instead, a reference parameter represents the same storage location as the variable given as the argument in the method invocation.

When a formal parameter is a reference parameter, the corresponding argument in a method invocation must consist of the keyword ref followed by a variable-reference (§5.4) of the same type as the formal parameter. A variable must be definitely assigned before it can be passed as a reference parameter.

Within a method, a reference parameter is always considered definitely assigned.

The example

class Test
{
   static void Swap(ref int x, ref int y) {
      int temp = x;
      x = y;
      y = temp;
   }
   static void Main() {
      int i = 1, j = 2;
      Swap(ref i, ref j);
      Console.WriteLine("i = {0}, j = {1}", i, j);
   }
}

produces the output

i = 2, j = 1

For the invocation of Swap in Main, x represents i and y represents j. Thus, the invocation has the effect of swapping the values of i and j.

In a method that takes reference parameters it is possible for multiple names to represent the same storage location. In the example

class A
{
   string s;
   void F(ref string a, ref string b) {
      s = "One";
      a = "Two";
      b = "Three";
   }
   void G() {
      F(ref s, ref s);
   }
}

the invocation of F in G passes a reference to s for both a and b. Thus, for that invocation, the names s, a, and b all refer to the same storage location, and the three assignments all modify the instance field s.


10.5.1.3 Output parameters

A parameter declared with an out modifier is an output parameter. Similar to a reference parameter, an output parameter does not create a new storage location. Instead, an output parameter represents the same storage location as the variable given as the argument in the method invocation.

When a formal parameter is an output parameter, the corresponding argument in a method invocation must consist of the keyword out followed by a variable-reference (§5.4) of the same type as the formal parameter. A variable need not be definitely assigned before it can be passed as an output parameter, but following an invocation where a variable was passed as an output parameter, the variable is considered definitely assigned.

Within a method, just like a local variable, an output parameter is initially considered unassigned and must be definitely assigned before its value is used.

Every output parameter of a method must be definitely assigned before the method returns.

Output parameters are typically used in methods that produce multiple return values. For example:

class Test
{
   static void SplitPath(string path, out string dir, out string name) {
      int i = path.Length;
      while (i > 0) {
         char ch = path[i – 1];
         if (ch == '\\' || ch == '/' || ch == ':') break;
         i--;
      }
      dir = path.Substring(0, i);
      name = path.Substring(i);
   }
   static void Main() {
      string dir, name;
      SplitPath("c:\\Windows\\System\\hello.txt", out dir, out name);
      Console.WriteLine(dir);
      Console.WriteLine(name);
   }
}

The example produces the output:

c:\Windows\System\
hello.txt

Note that the dir and name variables can be unassigned before they are passed to SplitPath, and that they are considered definitely assigned following the call.


10.5.1.4 Params parameters

A parameter declared with a params modifier is a params parameter. A params parameter must be the last parameter in the formal parameter list, and the type of a params parameter must be a single-dimension array type. For example, the types int[] and int[][] can be used as the type of a params parameter, but the type int[,] cannot be used in this way.

A params parameter enables a caller to supply values in one of two ways.

  • The caller may specify an expression of a type that is implicitly convertible (§6.1) to the formal parameter type. In this case, the params parameter acts precisely like a value parameter.
  • Alternatively, the caller may specify zero or more expressions, where the type of each expression is implicitly convertible (§6.1) to the element type of the formal parameter type. In this case, params parameter is initialized with an array of the formal parameter type that contains the value or values provided by the caller.

A method is permitted to assign new values to a params parameter. Such assignments only affect the local storage location represented by the params parameter.

The example

void F(params int[] values) {
   Console.WriteLine("values contains %0 items", values.Length);
   foreach (int value in values)
      Console.WriteLine("\t%0", value);
}
void G() {
   int i = 1, j = 2, k = 3;
   F(new int[] {i, j, k);
   F(i, j, k);
}

shows a method F with a params parameter of type int[]. In the method G, two invocations of F are shown. In the first invocation, F is called with a single argument of type int[]. In the second invocation, F is called with three expressions of type int. The output of each call is the same:

values contains 3 items:
   1
   2
   3

A params parameter can be passed along to another params parameter. In the example

void F(params object[] fparam) {
   Console.WriteLine(fparam.Length);
}
void G(params object[] gparam) {
   Console.WriteLine(gparam.Length);
   F(gparam);
}
void H() {
   G(1, 2, 3);
}

the method G has a params parameter of type object[]. When this parameter is used as an actual argument for the method F, it is passed along without modification. The output is:

3
3

The example

void F(params object[] fparam) {
   Console.WriteLine(fparam.Length);
}
void G(params object[] gparam) {
   Console.WriteLine(gparam.Length);
   F((object) gparam);   // Note: cast to (object)
}
void H() {
   G(1, 2, 3);
}

shows that it is also possible to pass the params parameter as a single value by adding a cast. The output is:


10.5.2 3 1Static and instance methods

NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

10.5.2 3
1Static and instance methods

When a method declaration includes a static modifier, the method is said to be a static method. When no static modifier is present, the method is said to be an instance method.

A static method does not operate on a specific instance, and it is an error to refer to this in a static method. It is furthermore an error to include a virtual, abstract, or override modifier on a static method.

An instance method operates on a given instance of a class, and this instance can be accessed as this (§7.5.7).

The differences between static and instance members are further discussed in §10.2.5.


10.5.3 Virtual methods

When an instance method declaration includes a virtual modifier, the method is said to be a virtual method. When no virtual modifier is present, the method is said to be a non-virtual method.

It is an error for a method declaration that includes the virtual modifier to also include any one of the static, abstract, or override modifiers.

The implementation of a non-virtual method is invariant: The implementation is the same whether the method is invoked on an instance of the class in which it is declared or an instance of a derived class. In contrast, the implementation of a virtual method can be changed by derived classes. The process of changing the implementation of an inherited virtual method is known as overriding the method (§10.5.4).

In a virtual method invocation, the run-time type of the instance for which the invocation takes place determines the actual method implementation to invoke. In a non-virtual method invocation, the compile-time type of the instance is the determining factor. In precise terms, when a method named N is invoked with an argument list A on an instance with a compile-time type C and a run-time type R (where R is either C or a class derived from C), the invocation is processed as follows:

  • First, overload resolution is applied to C, N, and A, to select a specific method M from the set of methods declared in and inherited by C. This is described in §7.5.5.1.
  • Then, if M is a non-virtual method, M is invoked.
  • Otherwise, M is a virtual method, and the most derived implementation of M with respect to R is invoked.

For every virtual method declared in or inherited by a class, there exists a most derived implementation of the method with respect to that class. The most derived implementation of a virtual method M with respect to a class R is determined as follows:

  • If R contains the introducing virtual declaration of M, then this is the most derived implementation of M.
  • Otherwise, if R contains an override of M, then this is the most derived implementation of M.
  • Otherwise, the most derived implementation of M is the same as that of the direct base class of R.

The following example illustrates the differences between virtual and non-virtual methods:

class A
{
   public void F() { Console.WriteLine("A.F"); }
   public virtual void G() { Console.WriteLine("A.G"); }
}
class B: A
{
   new public void F() { Console.WriteLine("B.F"); }
   public override void G() { Console.WriteLine("B.G"); }
}
class Test
{
   static void Main() {
      B b = new B();
      A a = b;
      a.F();
      b.F();
      a.G();
      b.G();
   }
}

In the example, A introduces a non-virtual method F and a virtual method G. B introduces a new non-virtual method F, thus hiding the inherited F, and also overrides the inherited method G. The example produces the output:

A.F
B.F
B.G
B.G

Notice that the statement a.G() invokes B.G, not A.G. This is because the run-time type of the instance (which is B), not the compile-time type of the instance (which is A), determines the actual method implementation to invoke.

Because methods are allowed to hide inherited methods, it is possible for a class to contain several virtual methods with the same signature. This does not present an ambiguity problem, since all but the most derived method are hidden. In the example

class A
{
   public virtual void F() { Console.WriteLine("A.F"); }
}
class B: A
{
   public override void F() { Console.WriteLine("B.F"); }
}
class C: B
{
   new public virtual void F() { Console.WriteLine("C.F"); }
}
class D: C
{
   public override void F() { Console.WriteLine("D.F"); }
}
class Test
{
   static void Main() {
      D d = new D();
      A a = d;
      B b = d;
      C c = d;
      a.F();
      b.F();
      c.F();
      d.F();
   }
}

the C and D classes contain two virtual methods with the same signature: The one introduced by A and the one introduced by C. The method introduced by C hides the method inherited from A. Thus, the override declaration in D overrides the method introduced by C, and it is not possible for D to override the method introduced by A. The example produces the output:

B.F
B.F
D.F
D.F

Note that it is possible to invoke the hidden virtual method by accessing an instance of D through a less derived type in which the method is not hidden.


10.5.4 Override methods

When an instance method declaration includes an override modifier, the method overrides an inherited virtual method with the same signature. Whereas a virtual method declaration introduces a new method, an override method declaration specializes an existing inherited virtual method by providing a new implementation of the method.

It is an error for an override method declaration to include any one of the new, static, virtual, or abstract modifiers.

The method overridden by an override declaration is known as the overridden base method. For an override method M declared in a class C, the overridden base method is determined by examining each base class of C, starting with the direct base class of C and continuing with each successive direct base class, until an accessible method with the same signature as M is located. For purposes of locating the overridden base method, a method is considered accessible if it is public, if it is protected, if it is protected internal, or if it is internal and declared in the same project as C.

A compile-time error occurs unless all of the following are true for an override declaration:

  • An overridden base method can be located as described above.
  • The overridden base method is a virtual, abstract, or override method. In other words, the overridden base method cannot be static or non-virtual.
  • The override declaration and the overridden base method have the same declared accessibility. In other words, an override declaration cannot change the accessibility of the virtual method.

An override declaration can access the overridden base method using a base-access (§7.5.8). In the example

class A
{
   int x;
   public virtual void PrintFields() {
      Console.WriteLine("x = {0}", x);
   }
}
class B: A
{
   int y;
   public override void PrintFields() {
      base.PrintFields();
      Console.WriteLine("y = {0}", y);
   }
}

the base.PrintFields() invocation in B invokes the PrintFields method declared in A. A base-access disables the virtual invocation mechanism and simply treats the base method as a non-virtual method. Had the invocation in B been written ((A)this).PrintFields(), it would recursively invoke the PrintFields method declared in B, not the one declared in A.

Only by including an override modifier can a method override another method. In all other cases, a method with the same signature as an inherited method simply hides the inherited method. In the example

class A
{
   public virtual void F() {}
}
class B: A
{
   public virtual void F() {}      // Warning, hiding inherited F()
}

the F method in B does not include an override modifier and therefore does not override the F method in A. Rather, the F method in B hides the method in A, and a warning is reported because the declaration does not include a new modifier.

In the example

class A
{
   public virtual void F() {}
}
class B: A
{
   new private void F() {}         // Hides A.F within B
}
class C: B
{
   public override void F() {}   // Ok, overrides A.F
}

the F method in B hides the virtual F method inherited from A. Since the new F in B has private access, its scope only includes the class body of B and does not extend to C. The declaration of F in C is therefore permitted to override the F inherited from A.


10.5.5 Abstract methods

When an instance method declaration includes an abstract modifier, the method is said to be an abstract method. An abstract method is implicitly also a virtual method.

An abstract declaration introduces a new virtual method but does not provide an implementation of the method. Instead, non-abstract derived classes are required to provide their own implementation by overriding the method. Because an abstract method provides no actual implementation, the method-body of an abstract method simply consists of a semicolon.

Abstract method declarations are only permitted in abstract classes (§10.1.1.1).

It is an error for an abstract method declaration to include any one of the static, virtual, or override modifiers.

In the example

public abstract class Shape
{
   public abstract void Paint(Graphics g, Rectangle r);
}
public class Ellipse: Shape
{
   public override void Paint(Graphics g, Rectangle r) {
      g.drawEllipse(r);
   }
}
public class Box: Shape
{
   public override void Paint(Graphics g, Rectangle r) {
      g.drawRect(r);
   }
}

the Shape class defines the abstract notion of a geometrical shape object that can paint itself. The Paint method is abstract because there is no meaningful default implementation. The Ellipse and Box classes are concrete Shape implementations. Because theses classes are non-abstract, they are required to override the Paint method and provide an actual implementation.

It is an error for a base-access (§7.5.8) to reference an abstract method. In the example

class A
{
   public abstract void F();
}
class B: A
{
   public override void F() {
      base.F();                  // Error, base.F is abstract
   }
}

an error is reported for the base.F() invocation because it references an abstract method.


10.5.6 External methods

A method declaration may include the extern modifier to indicate that the method is implemented externally. Because an external method declaration provides no actual implementation, the method-body of an external method simply consists of a semicolon.

The extern modifier is typically used in conjunction with a DllImport attribute (§20.1), allowing external methods to be implemented by DLLs (Dynamic Link Libraries). The execution environment may support other mechanisms whereby implementations of external methods can be provided.

It is an error for an external method declaration to also include the abstract modifier. When an external method includes a DllImport attribute, the method declaration must also include a static modifier.

This example demonstrates use of the extern modifier and the DllImport attribute:

class Path
{
   [DllImport("kernel32", setLastError=true)]
   static extern bool CreateDirectory(string name, SecurityAttributes sa);
   [DllImport("kernel32", setLastError=true)]
   static extern bool RemoveDirectory(string name);
   [DllImport("kernel32", setLastError=true)]
   static extern int GetCurrentDirectory(int bufSize, StringBuilder buf);
   [DllImport("kernel32", setLastError=true)]
   static extern bool SetCurrentDirectory(string name);
}


10.5.7 Method body

The method-body of a method declaration consists either of a block or a semicolon.

Abstract and external method declarations do not provide a method implementation, and the method body of an abstract or external method simply consists of a semicolon. For all other methods, the method body is a block (§8.2) that contains the statements to execute when the method is invoked.

When the return type of a method is void, return statements (§8.9.4) in the method body are not permitted to specify an expression. If execution of the method body of a void method completes normally (that is, if control flows off the end of the method body), the method simply returns to the caller.

When the return type of a method is not void, each return statement in the method body must specify an expression of a type that is implicitly convertible to the return type. Execution of the method body of a value-returning method is required to terminate in a return statement that specifies an expression or in a throw statement that throws an exception. It is an error if execution of the method body can complete normally. In other words, in a value-returning method, control is not permitted to flow off the end of the method body.

In the example

class A
{
   public int F() {}         // Error, return value required
   public int G() {
      return 1;
   }
   public int H(bool b) {
      if (b) {
         return 1;
      }
      else {
         return 0;
      }
   }
}

the value-returning F method is in error because control can flow off the end of the method body. The G and H methods are correct because all possible execution paths end in a return statement that specifies a return value.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

10.5.8 Method overloading

The method overload resolution rules are described in §7.4.2.


10.6 Properties

A property is a named attribute associated with 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 execute in order to read or write their values. Properties thus provide a mechanism for associating actions with the reading and writing of an object’s attributes, and they furthermore permit such attributes to be computed.

Properties are declared using property-declarations:

property-declaration:
attributesopt property-modifiersopt type member-name { accessor-declarations }
property-modifiers:
property-modifier
property-modifiers property-modifier
property-modifier:
new
public
protected
internal
private
static
member-name:
identifier
interface-type . identifier

A property-declaration may include set of attributes (§17), a new modifier (§10.2.2), a valid combination of the four access modifiers (§10.2.3), and a static modifier (§10.2.5).

The type of a property declaration specifies the type of the property introduced by the declaration, and the member-name specifies the name of the property. Unless the property is an explicit interface member implementation, the member-name is simply an identifier. For an explicit interface member implementation (§13.4.1) , the member-name consists of an interface-type followed by a "." and an identifier.

The type of a property must be at least as accessible as the property itself (§3.3.4).

The accessor-declarations, which must be enclosed in "{" and "}" tokens, declare the accessors (§10.6.2) of the property. The accessors specify the executable statements associated with reading and writing the property.

Even though the syntax for accessing a property is the same as that for a field, a property is not classified as a variable. Thus, it is not possible to pass a property as a ref or out parameter.


10.6.1 Static properties

When a property declaration includes a static modifier, the property is said to be a static property. When no static modifier is present, the property is said to be an instance property.

A static property is not associated with a specific instance, and it is an error to refer to this in the accessors of a static property. It is furthermore an error to include a virtual, abstract, or override modifier on an accessor of a static property.

An instance property is associated with a given instance of a class, and this instance can be accessed as this (§7.5.7) in the accessors of the property.

When a property is referenced in a member-access (§7.5.4) of the form E.M, if M is a static property, E must denote a type, and if M is an instance property, E must denote an instance.

The differences between static and instance members are further discussed in §10.2.5.


10.6.2 Accessors

The accessor-declarations of a property specify the executable statements associated with reading and writing the property.

accessor-declarations:
get-accessor-declaration set-accessor-declarationopt
set-accessor-declaration get-accessor-declarationopt
get-accessor-declaration:
accessor-modifieropt get accessor-body
set-accessor-declaration:
accessor-modifieropt set accessor-body
accessor-modifier:
virtual
override
abstract
accessor-body:
block
;

The accessor declarations consist of a get-accessor-declaration, a set-accessor-declaration, or both. Each accessor declaration consists of an optional accessor-modifier, followed by the token get or set, followed by an accessor-body. For abstract accessors, the accessor-body is simply a semicolon. For all other accessors, the accessor-body is a block which specifies the statements to execute when the accessor is invoked.

A get accessor corresponds to a parameterless method with a return value of the property type. Except as the target of an assignment, when a property is referenced in an expression, the get accessor of the property is invoked to compute the value of the property (§7.1.1). The body of a get accessor must conform to the rules for value-returning methods described in §10.5.7. In particular, all return statements in the body of a get accessor must specify an expression that is implicitly convertible to the property type. Furthermore, a get accessor is required to terminate in a return statement or a throw statement, and control is not permitted to flow off the end of the get accessor’s body.

A set accessor corresponds to a method with a single value parameter of the property type and a void return type. The implicit parameter of a set accessor is always named value. When a property is referenced as the target of an assignment, the set accessor is invoked with an argument that provides the new value (§7.13.1). The body of a set accessor must conform to the rules for void methods described in §10.5.7. In particular, return statements in the set accessor body are not permitted to specify an expression.

Since a set accessor implicitly has a parameter named value, it is an error for a local variable declaration in a set accessor to use that name.

Based on the presence or absence of the get and set accessors, a property is classified as follows:

  • A property that includes both a get accessor and a set accessor is said to be a read-write property.
  • A property that has only a get accessor is said to be read-only property. It is an error for a read-only property to be the target of an assignment.
  • A property that has only a set accessor is said to be write-only property. Except as the target of an assignment, it is an error to reference a write-only property in an expression.

In the example

public class Button: Control
{
   private string caption;
   public string Caption {
      get {
         return caption;
      }
      set {
         if (caption != value) {
            caption = value;
            Repaint();
         }
      }
   }
   public override void Paint(Graphics g, Rectangle r) {
      // Painting code goes here
   }
}

the Button control declares a public Caption property. The get accessor of the Caption property returns the string stored in the private caption field. The set accessor checks if the new value is different from the current value, and if so, it stores the new value and repaints the control. Properties often follow the pattern shown above: The get accessor simply returns a value stored in a private field, and the set accessor modifies the private field and then performs any additional actions required to fully update the state of the object.

Given the Button class above, the following is an example of use of the Caption property:

Button okButton = new Button();
okButton.Caption = "OK";         // Invokes set accessor
string s = okButton.Caption;      // Invokes get accessor

Here, the set accessor is invoked by assigning a value to the property, and the get accessor is invoked by referencing the property in an expression.

The get and set accessors of a property are not distinct members, and it is not possible to declare the accessors of a property separately. The example

class A
{
   private string name;
   public string Name {            // Error, duplicate member name
      get { return name; }
   }
   public string Name {            // Error, duplicate member name
      set { name = value; }
   }
}

does not declare a single read-write property. Rather, it declares two properties with the same name, one read-only and one write-only. Since two members declared in the same class cannot have the same name, the example causes a compile-time error to occur.

When a derived class declares a property by the same name as an inherited property, the derived property hides the inherited property with respect to both reading and writing. In the example

class A
{
   public int P {
      set {...}
   }
}
class B: A
{
   new public int P {
      get {...}
   }
}

the P property in B hides the P property in A with respect to both reading and writing. Thus, in the statements

B b = new B();
b.P = 1;         // Error, B.P is read-only
((A)b).P = 1;   // Ok, reference to A.P

the assignment to b.P causes an error to be reported, since the read-only P property in B hides the write-only P property in A. Note, however, that a cast can be used to access the hidden P property.

Unlike public fields, properties provide a separation between an object’s internal state and its public interface. Consider the example:

class Label
{
   private int x, y;
   private string caption;
   public Label(int x, int y, string caption) {
      this.x = x;
      this.y = y;
      this.caption = caption;
   }
   public int X {
      get { return x; }
   }
   public int Y {
      get { return y; }
   }
   public Point Location {
      get { return new Point(x, y); }
   }
   public string Caption {
      get { return caption; }
   }
}

Here, the Label class uses two int fields, x and y, to store its location. The location is publicly exposed both as an X and a Y property and as a Location property of type Point. If, in a future version of Label, it becomes more convenient to store the location as a Point internally, the change can be made without affecting the public interface of the class:

class Label
{
   private Point location;
   private string caption;
   public Label(int x, int y, string caption) {
      this.location = new Point(x, y);
      this.caption = caption;
   }
   public int X {
      get { return location.x; }
   }
   public int Y {
      get { return location.y; }
   }
   public Point Location {
      get { return location; }
   }
   public string Caption {
      get { return caption; }
   }
}

Had x and y instead been public readonly fields, it would have been impossible to make such a change to the Label class.

Exposing state through properties is not necessarily any less efficient than exposing fields directly. In particular, when a property accessor is non-virtual and contains only a small amount of code, the execution environment may replace calls to accessors with the actual code of the accessors. This process is known as inlining, and it makes property access as efficient as field access, yet preserves the increased flexibility of properties.

Since invoking a get accessor is conceptually equivalent to reading the value of a field, it is considered bad programming style for get accessors to have observable side-effects. In the example

class Counter
{
   private int next;
   public int Next {
      get { return next++; }
   }
}

the value of the Next property depends on the number of times the property has previously been accessed. Thus, accessing the property produces an observable side-effect, and the property should instead be implemented as a method.

The "no side-effects" convention for get accessors doesn’t mean that get accessors should always be written to simply return values stored in fields. Indeed, get accessors often compute the value of a property by accessing multiple fields or invoking methods. However, a properly designed get accessor performs no actions that cause observable changes in the state of the object.

Properties can be used to delay initialization of a resource until the moment it is first referenced. For example:

public class Console
{
   private static TextReader reader;
   private static TextWriter writer;
   private static TextWriter error;
   public static TextReader In {
      get {
         if (reader == null) {
            reader = new StreamReader(File.OpenStandardInput());
         }
         return reader;
      }
   }
   public static TextWriter Out {
      get {
         if (writer == null) {
            writer = new StreamWriter(File.OpenStandardOutput());
         }
         return writer;
      }
   }
   public static TextWriter Error {
      get {
         if (error == null) {
            error = new StreamWriter(File.OpenStandardError());
         }
         return error;
      }
   }
}

The Console class contains three properties, In, Out, and Error, that represent the standard input, output, and error devices. By exposing these members as properties, the Console class can delay their initialization until they are actually used. For example, upon first referencing the Out property, as in

Console.Out.WriteLine("Hello world");

the underlying TextWriter for the output device is created. But if the application makes no reference to the In and Error properties, then no objects are created for those devices.


10.6.3 Virtual, override, and abstract accessors

Provided a property is not static, a property declaration may include a virtual modifier or an abstract modifier on either or both of its accessors. There is no requirement that the modifiers be the same for each accessor. For example, it is possible for a property to have a non-virtual get accessor and a virtual set accessor.

The virtual accessors of an inherited property can be overridden in a derived class by including a property declaration that specifies override directives on its accessors. This is known as an overriding property declaration. An overriding property declaration does not declare a new property. Instead, it simply specializes the implementations of the virtual accessors of an existing property.

It is an error to mix override and non-override accessors in a property declaration. If a property declaration includes both accessors, then both must include an override directive or both must omit it.

An overriding property declaration must specify the exact same access modifiers, type, and name as the inherited property, and it can override only those inherited accessors that are virtual. For example, if an inherited property has a non-virtual get accessor and a virtual set accessor, then an overriding property declaration can only include an override set accessor.

When both accessors of an inherited property are virtual, an overriding property declaration is permitted to only override one of the accessors.

Except for differences in declaration and invocation syntax, virtual, override, and abstract accessors behave exactly like a virtual, override and abstract methods. Specifically, the rules described in §10.5.3, §10.5.4, and §10.5.5 apply as if accessors were methods of a corresponding form:

  • A get accessor corresponds to a parameterless method with a return value of the property type and a set of modifiers formed by combining the modifiers of the property and the modifier of the accessor.
  • A set accessor corresponds to a method with a single value parameter of the property type, a void return type, and a set of modifiers formed by combining the modifiers of the property and the modifier of the accessor.

In the example

abstract class A
{
   int y;
   public int X {
      virtual get {
         return 0;
      }
   }
   public int Y {
      get {
         return y;
      }
      virtual set {
         y = value;
      }
   }
   protected int Z {
      abstract get;
      abstract set;
   }
}

X is a read-only property with a virtual get accessor, Y is a read-write property with a non-virtual get accessor and a virtual set accessor, and Z is a read-write property with abstract get and set accessors. Because the containing class is abstract, Z is permitted to have abstract accessors.

A class that derives from A is shown below:

class B: A
{
   int z;
   public int X {
      override get {
         return base.X + 1;
      }
   }
   public int Y {
      override set {
         base.Y = value < 0? 0: value;
      }
   }
   protected int Z {
      override get {
         return z;
      }
      override set {
         z = value;
      }
   }
}

Here, because their accessors specify the override modifier, the declarations of X, Y, and Z are overriding property declarations. Each property declaration exactly matches the access modifiers, type, and name of the corresponding inherited property. The get accessor of X and the set accessor of Y use the base keyword to access the inherited accessors. The declaration of Z overrides both abstract accessors—thus, there are no outstanding abstract function members in B, and B is permitted to be a non-abstract class.


10.7 Events

Events permit a class to declare notifications for which clients can attach executable code in the form of event handlers. Events are declared using event-declarations:

event-declaration:
event-field-declaration
event-property-declaration
event-field-declaration:
attributesopt event-modifiersopt event type variable-declarators ;
event-property-declaration:
attributesopt event-modifiersopt event type member-name { accessor-declarations }
event-modifiers:
event-modifier
event-modifiers event-modifier
event-modifier:
new
public
protected
internal
private
static

An event declaration is either an event-field-declaration or an event-property-declaration. In both cases, the declaration may include set of attributes (§17), a new modifier (§10.2.2), a valid combination of the four access modifiers (§10.2.3), and a static modifier (§10.2.5).

The type of an event declaration must be a delegate-type (§15), and that delegate-type must be at least as accessible as the event itself (§3.3.4).

An event field declaration corresponds to a field-declaration (§10.4) that declares one or more fields of a delegate type. The readonly modifier is not permitted in an event field declaration.

An event property declaration corresponds to a property-declaration (§10.6) that declares a property of a delegate type. The member-name and accessor-declarations are equivalent to those of a property declaration, except that an event property declaration must include both a get accessor and a set accessor, and that the accessors are not permitted to include virtual, override, or abstract modifiers.

Within the program text of the class or struct that contains an event member declaration, the event member corresponds exactly to a private field or property of a delegate type, and the member can thus be used in any context that permits a field or property.

Outside the program text of the class or struct that contains an event member declaration, the event member can only be used as the left hand operand of the += and -= operators (§7.13.3). These operators are used to attach or remove event handlers to or from an event member, and the access modifiers of the event member control the contexts in which the operations are permitted.

Since += and -= are the only operations that are permitted on an event member outside the type that declares the event member, external code can append and remove handlers for an event, but cannot in any other way obtain or modify the value of the underlying event field or event property.

In the example

public delegate void EventHandler(object sender, Event e);
public class Button: Control
{
   public event EventHandler Click;
   protected void OnClick(Event e) {
      if (Click != null) Click(this, e);
   }
   public void Reset() {
      Click = null;
   }
}

there are no restrictions on usage of the Click event field within the Button class. As the example demonstrates, the field can be examined, modified, and used in delegate invocation expressions. The OnClick method in the Button class "raises" the Click event. The notion of raising an event is precisely equivalent to invoking the delegate represented by the event member—thus, there are no special language constructs for raising events. Note that the delegate invocation is preceded by a check that ensures the delegate is non-null.

Outside the declaration of the Button class, the Click member can only be used on the left hand side of the += and -= operators, as in

b.Click += new EventHandler(...);

which appends a delegate to the invocation list of the Click event, and

b.Click -= new EventHandler(...);

which removes a delegate from the invocation list of the Click event.

In an operation of the form x += y or x -= y, when x is an event member and the reference takes place outside the type that contains the declaration of x, the result of the operation is void (as opposed to the value of x after the assignment). This rule prohibits external code from indirectly examining the underlying delegate of an event member.

The following example shows how event handlers are attached to instances of the Button class above:

public class LoginDialog: Form
{
   Button OkButton;
   Button CancelButton;
   public LoginDialog() {
      OkButton = new Button(...);
      OkButton.Click += new EventHandler(OkButtonClick);
      CancelButton = new Button(...);
      CancelButton.Click += new EventHandler(CancelButtonClick);
   }
   void OkButtonClick(object sender, Event e) {
      // Handle OkButton.Click event
   }
   void CancelButtonClick(object sender, Event e) {
      // Handle CancelButton.Click event
   }
}

Here, the LoginDialog constructor creates two Button instances and attaches event handlers to the Click events.

Event members are typically fields, as in the Button example above. In cases where the storage cost of one field per event is not acceptable, a class can declare event properties instead of event fields and use a private mechanism for storing the underlying delegates. (In scenarios where most events are unhandled, using a field per event may not be acceptable. The ability to use a properties rather than fields allows for space vs. speed tradeoffs to be made by the developer.)

In the example

class Control: Component
{
   // Unique keys for events
   static readonly object mouseDownEventKey = new object();
   static readonly object mouseUpEventKey = new object();
   // Return event handler associated with key
   protected Delegate GetEventHandler(object key) {...}
   // Set event handler associated with key
   protected void SetEventHandler(object key, Delegate handler) {...}
   // MouseDown event property
   public event MouseEventHandler MouseDown {
      get {
         return (MouseEventHandler)GetEventHandler(mouseDownEventKey);
      }
      set {
         SetEventHandler(mouseDownEventKey, value);
      }
   }
   // MouseUp event property
   public event MouseEventHandler MouseUp {
      get {
         return (MouseEventHandler)GetEventHandler(mouseUpEventKey);
      }
      set {
         SetEventHandler(mouseUpEventKey, value);
      }
   }
}

the Control class implements an internal storage mechanism for events. The SetEventHandler method associates a delegate value with a key, and the GetEventHandler method returns the delegate currently associated with a key. Presumably the underlying storage mechanism is designed such that there is no cost for associating a null delegate value with a key, and thus unhandled events consume no storage.


10.8 Indexers

Indexers permit instances of a class to be indexed in the same way as arrays. Indexers are declared using indexer-declarations:

indexer-declaration:
attributesopt indexer-modifiersopt indexer-declarator { accessor-declarations }
indexer-modifiers:
indexer-modifier
indexer-modifiers indexer-modifier
indexer-modifier:
new
public
protected
internal
private
indexer-declarator:
type this [ formal-index-parameter-list ]
type interface-type . this [ formal-index-parameter-list ]
formal-index-parameter-list:
formal-index-parameter
formal-index-parameter-list , formal-index-parameter
formal-index-parameter:
attributesopt type identifier

An indexer-declaration may include set of attributes (§17), a new modifier (§10.2.2), and a valid combination of the four access modifiers (§10.2.3).

The type of an indexer declaration specifies the element type of the indexer introduced by the declaration. Unless the indexer is an explicit interface member implementation, the type is followed by the keyword this. For an explicit interface member implementation, the type is followed by an interface-type, a ".", and the keyword this. Unlike other members, indexers do not have user-defined names.

The formal-index-parameter-list specifies the parameters of the indexer. The formal parameter list of an indexer corresponds to that of a method (§10.5.1), except that at least one parameter must be specified, and that the ref and out parameter modifiers are not permitted.

The type of an indexer and each of the types referenced in the formal-index-parameter-list must be at least as accessible as the indexer itself (§3.3.4).

The accessor-declarations, which must be enclosed in "{" and "}" tokens, declare the accessors of the indexer. The accessors specify the executable statements associated with reading and writing indexer elements.

Even though the syntax for accessing an indexer element is the same as that for an array element, an indexer element is not classified as a variable. Thus, it is not possible to pass an indexer element as a ref or out parameter.

The formal parameter list of an indexer defines the signature (§3.4) of the indexer. Specifically, the signature of an indexer consists of the number and types of its formal parameters. The element type is not part of an indexer’s signature, nor are the names of the formal parameters.

The signature of an indexer must differ from the signatures of all other indexers declared in the same class.

Indexers and properties are very similar in concept, but differ in the following ways:

  • A property is identified by its name, whereas an indexer is identified by its signature.
  • A property is accessed through a simple-name (§7.5.2) or a member-access (§7.5.4), whereas an indexer element is accessed through an element-access (§7.5.6.2).
  • A property can be a static member, whereas an indexer is always an instance member.
  • A get accessor of a property corresponds to a method with no parameters, whereas a get accessor of an indexer corresponds to a method with the same formal parameter list as the indexer.
  • A set accessor of a property corresponds to a method with a single parameter named value, whereas a set accessor of an indexer corresponds to a method with the same formal parameter list as the indexer, plus an additional parameter named value.
  • It is an error for an indexer accessor to declare a local variable with the same name as an indexer parameter.

With these differences in mind, all rules defined in §10.6.2 and §10.6.3 apply to indexer accessors as well as property accessors.

The example below declares a BitArray class that implements an indexer for accessing the individual bits in the bit array.

class BitArray
{
   int[] bits;
   int length;
   public BitArray(int length) {
      if (length < 0) throw new ArgumentException();
      bits = new int[((length - 1) >> 5) + 1];
      this.length = length;
   }
   public int Length {
      get { return length; }
   }
   public bool this[int index] {
      get {
         if (index < 0 || index >= length) {
            throw new IndexOutOfRangeException();
         }
         return (bits[index >> 5] & 1 << index) != 0;
      }
      set {
         if (index < 0 || index >= length) {
            throw new IndexOutOfRangeException();
         }
         if (value) {
            bits[index >> 5] |= 1 << index;
         }
         else {
            bits[index >> 5] &= ~(1 << index);
         }
      }
   }
}

An instance of the BitArray class consumes substantially less memory than a corresponding bool[] (each value occupies only one bit instead of one byte), but it permits the same operations as a bool[].

The following CountPrimes class uses a BitArray and the classical "sieve" algorithm to compute the number of primes between 1 and a given maximum:

class CountPrimes
{
   static int Count(int max) {
      BitArray flags = new BitArray(max + 1);
      int count = 1;
      for (int i = 2; i <= max; i++) {
         if (!flags[i]) {
            for (int j = i * 2; j <= max; j += i) flags[j] = true;
            count++;
         }
      }
      return count;
   }
   static void Main(string[] args) {
      int max = int.Parse(args[0]);
      int count = Count(max);
      Console.WriteLine("Found {0} primes between 1 and {1}", count, max);
   }
}

Note that the syntax for accessing elements of the BitArray is precisely the same as for a bool[].


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

10.8.1 Indexer overloading

The indexer overload resolution rules are described in §7.4.2.


10.9 Operators

Operators permit a class to define expression operators that can be applied to instances of the class. Operators are declared using operator-declarations:

operator-declaration:
attributesopt operator-modifiers operator-declarator block
operator-modifiers:
public static
static public
operator-declarator:
unary-operator-declarator
binary-operator-declarator
conversion-operator-declarator
unary-operator-declarator:
type operator overloadable-unary-operator ( type identifier )
overloadable-unary-operator: one of
+ - ! ~ ++ -- true false
binary-operator-declarator:
type operator overloadable-binary-operator ( type identifier , type identifier )
overloadable-binary-operator: one of
+ - * / % & | ^ << >> == != > < >= <=
conversion-operator-declarator:
implicit operator type ( type identifier )
explicit operator type ( type identifier )

There are three categories of operators: Unary operators (§10.9.1), binary operators (§10.9.2), and conversion operators (§10.9.3).

The following rules apply to all operator declarations:

  • An operator declaration must include both a public and a static modifier, and is not permitted to include any other modifiers.
  • The parameter(s) of an operator must be value parameters. It is an error to for an operator declaration to specify ref or out parameters.
  • The signature of an operator must differ from the signatures of all other operators declared in the same class.
  • All types referenced in an operator declaration must be at least as accessible as the operator itself (§3.3.4).

Each operator category imposes additional restrictions, as described in the following sections.

Like other members, operators declared in a base class are inherited by derived classes. Because operator declarations always require the class or struct in which the operator is declared to participate in the signature of the operator, it is not possible for an operator declared in a derived class to hide an operator declared in a base class. Thus, the new modifier is never required, and therefore never permitted, in an operator declaration.

For all operators, the operator declaration includes a block which specifies the statements to execute when the operator is invoked. The block of an operator must conform to the rules for value-returning methods described in §10.5.7.

Additional information on unary and binary operators can be found in §7.2.

Additional information on conversion operators can be found in §6.4.


10.9.1 Unary operators

The following rules apply to unary operator declarations, where T denotes the class or struct type that contains the operator declaration:

  • A unary +, -, !, or ~ operator must take a single parameter of type T and can return any type.
  • A unary ++ or -- operator must take a single parameter of type T and must return type T.
  • A unary true or false operator must take a single parameter of type T and must return type bool.

The signature of a unary operator consists of the operator token (+, -, !, ~, ++, --, true, or false) and the type of the single formal parameter. The return type is not part of a unary operator’s signature, nor is the name of the formal parameter.

The true and false unary operators require pair-wise declaration. An error occurs if a class declares one of these operators without also declaring the other. The true and false operators are further described in §7.16.


10.9.2 Binary operators

A binary operator must take two parameters, at least one of which must be of the class or struct type in which the operator is declared. A binary operator can return any type.

The signature of a binary operator consists of the operator token (+, -, *, /, %, &, |, ^, <<, >>, ==, !=, >, <, >=, or <=) and the types of the two formal parameters. The return type is not part of a binary operator’s signature, nor are the names of the formal parameters.

Certain binary operators require pair-wise declaration. For every declaration of either operator of a pair, there must be a matching declaration of the other operator of the pair. Two operator declarations match when they have the same return type and the same type for each parameter. The following operators require pair-wise declaration:

  • operator == and operator !=
  • operator > and operator <
  • operator >= and operator <=


10.9.3 Conversion operators

A conversion operator declaration introduces a user-defined conversion (§6.4) which augments the pre-defined implicit and explicit conversions.

A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. This is described further in §6.1.

A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Explicit conversions can occur in cast expressions, and are described further in §6.2.

A conversion operator converts from a source type, indicated by the parameter type of the conversion operator, to a target type, indicated by the return type of the conversion operator. A class or struct is permitted to declare a conversion from a source type S to a target type T provided all of the following are true:

  • S and T are different types.
  • Either S or T is the class or struct type in which the operator declaration takes place.
  • Neither S nor T is object or an interface-type.
  • T is not a base class of S, and S is not a base class of T.

From the second rule it follows that a conversion operator must either convert to or from the class or struct type in which the operator is declared. For example, it is possible for a class or struct type C to define a conversion from C to int and from int to C, but not from int to bool.

It is not possible to redefine a pre-defined conversion. Thus, conversion operators are not allowed to convert from or to object because implicit and explicit conversions already exist between object and all other types. Likewise, neither of the source and target types of a conversion can be a base type of the other, since a conversion would then already exist.

User-defined conversions are not allowed to convert from or to interface-types. This restriction in particular ensures that no user-defined transformations occur when converting to an interface-type, and that a conversion to an interface-type succeeds only if the object being converted actually implements the specified interface-type.

The signature of a conversion operator consists of the source type and the target type. (Note that this is the only form of member for which the return type participates in the signature.) The implicit or explicit classification of a conversion operator is not part of the operator’s signature. Thus, a class or struct cannot declare both an implicit and an explicit conversion operator with the same source and target types.

In general, user-defined implicit conversions should be designed to never throw exceptions and never lose information. If a user-defined conversion can give rise to exceptions (for example because the source argument is out of range) or loss of information (such as discarding high-order bits), then that conversion should be defined as an explicit conversion.

In the example

public struct Digit
{
   byte value;
   public Digit(byte value) {
      if (value < 0 || value > 9) throw new ArgumentException();
      this.value = value;
   }
   public static implicit operator byte(Digit d) {
      return d.value;
   }
   public static explicit operator Digit(byte b) {
      return new Digit(b);
   }
}

the conversion from Digit to byte is implicit because it never throws exceptions or loses information, but the conversion from byte to Digit is explicit since Digit can only represent a subset of the possible values of a byte.


10.10 Instance constructors

Constructors implement the actions required to initialize instances of a class. Constructors are declared using constructor-declarations:

constructor-declaration:
attributesopt constructor-modifiersopt constructor-declarator block
constructor-modifiers:
constructor-modifier
constructor-modifiers constructor-modifier
constructor-modifier:
public
protected
internal
private
constructor-declarator:
identifier ( formal-parameter-listopt ) constructor-initializeropt
constructor-initializer:
: base ( argument-listopt )
: this ( argument-listopt )

A constructor-declaration may include set of attributes (§17) and a valid combination of the four access modifiers (§10.2.3).

The identifier of a constructor-declarator must name the class in which the constructor is declared. If any other name is specified, an error occurs.

The optional formal-parameter-list of a constructor is subject to the same rules as the formal-parameter-list of a method (§10.5). The formal parameter list defines the signature (§3.4) of a constructor and governs the process whereby overload resolution (§7.4.2) selects a particular constructor in an invocation.

Each of the types referenced in the formal-parameter-list of a constructor must be at least as accessible as the constructor itself (§3.3.4).

The optional constructor-initializer specifies another constructor to invoke before executing the statements given in the block of this constructor. This is described further in §10.10.1.

The block of a constructor declaration specifies the statements to execute in order to initialize a new instance of the class. This corresponds exactly to the block of an instance method with a void return type (§10.5.7).

Constructors are not inherited. Thus, a class has no other constructors than those that are actually declared in the class. If a class contains no constructor declarations, a default constructor is automatically provided (§10.10.4).

Constructors are invoked by object-creation-expressions (§7.5.10.1) and through constructor-initializers.


10.10.1 Constructor initializers

All constructors (except for the constructors of class object) implicitly include an invocation of another constructor immediately before the first statement in the block of the constructor. The constructor to implicitly invoke is determined by the constructor-initializer:

  • A constructor initializer of the form base(...) causes a constructor from the direct base class to be invoked. The constructor is selected using the overload resolution rules of §7.4.2. The set of candidate constructors consists of all accessible constructors declared in the direct base class. If the set of candidate constructors is empty, or if a single best constructor cannot be identified, an error occurs.
  • A constructor initializer of the form this(...) causes a constructor from the class itself to be invoked. The constructor is selected using the overload resolution rules of §7.4.2. The set of candidate constructors consists of all accessible constructors declared in the class itself. If the set of candidate constructors is empty, or if a single best constructor cannot be identified, an error occurs. If a constructor declaration includes a constructor initializer that invokes the constructor itself, an error occurs.

If a constructor has no constructor initializer, a constructor initializer of the form base() is implicitly provided. Thus, a constructor declaration of the form

C(...) {...}

is exactly equivalent to

C(...): base() {...}

The scope of the parameters given by the formal-parameter-list of a constructor declaration includes the constructor initializer of that declaration. Thus, a constructor initializer is permitted to access the parameters of the constructor. For example:

class A
{
   public A(int x, int y) {}
}
class B: A
{
   public B(int x, int y): base(x + y, x - y) {}
}

A constructor initializer cannot access the instance being created. It is therefore an error to reference this in an argument expression of the constructor initializer, as is it an error for an argument expression to reference any instance member through a simple-name.


10.10.2 Instance variable initializers

When a constructor has no constructor initializer or a constructor initializer of the form base(...), the constructor implicitly performs the initializations specified by the variable-initializers of the instance fields declared in the class. This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor and before the implicit invocation of the direct base class constructor. The variable initializers are executed in the textual order they appear in the class declaration.


10.10.3 Constructor execution

It is useful to think of instance variable initializers and constructor initializers as statements that are automatically inserted before the first statement in the block of a constructor. The example

class A
{
   int x = 1, y = -1, count;
   public A() {
      count = 0;
   }
   public A(int n) {
      count = n;
   }
}
class B: A
{
   double sqrt2 = Math.Sqrt(2.0);
   ArrayList items = new ArrayList(100);
   int max;
   public B(): this(100) {
      items.Add("default");
   }
   public B(int n): base(n – 1) {
      max = n;
   }
}

contains several variable initializers and also contains constructor initializers of both forms (base and this). The example corresponds to the code shown below, where each comment indicates an automatically inserted statement (the syntax used for the automatically inserted constructor invocations isn’t valid, but merely serves to illustrate the mechanism).

class A
{
   int x, y, count;
   public A() {
      x = 1;                        // Variable initializer
      y = -1;                        // Variable initializer
      object();                     // Invoke object() constructor
      count = 0;
   }
   public A(int n) {
      x = 1;                        // Variable initializer
      y = -1;                        // Variable initializer
      object();                     // Invoke object() constructor
      count = n;
   }
}
class B: A
{
   double sqrt2;
   ArrayList items;
   int max;
   public B(): this(100) {
      B(100);                        // Invoke B(int) constructor
      items.Add("default");
   }
   public B(int n): base(n – 1) {
      sqrt2 = Math.Sqrt(2.0);         // Variable initializer
      items = new ArrayList(100);   // Variable initializer
      A(n – 1);                     // Invoke A(int) constructor
      max = n;
   }
}

Note that variable initializers are transformed into assignment statements, and that these assignment statements are executed before the invocation of the base class constructor. This ordering ensures that all instance fields are initialized by their variable initializers before any statements that have access to the instance are executed. For example:

class A
{
   public A() {
      PrintFields();
   }
   public virtual void PrintFields() {}
}
class B: A
{
   int x = 1;
   int y;
   public B() {
      y = -1;
   }
   public override void PrintFields() {
      Console.WriteLine("x = {0}, y = {1}", x, y);
   }
}

When new B() is used to create an instance of B, the following output is produced:

x = 1, y = 0

The value of x is 1 because the variable initializer is executed before the base class constructor is invoked. However, the value of y is 0 (the default value of an int) because the assignment to y is not executed until after the base class constructor returns.


10.10.4 Default constructors

If a class contains no constructor declarations, a default constructor is automatically provided. The default constructor is always of the form

public C(): base() {}

where C is the name of the class. The default constructor simply invokes the parameterless constructor of the direct base class. If the direct base class does not have an accessible parameterless constructor, an error occurs. In the example

class Message
{
   object sender;
   string text;
}

a default constructor is provided because the class contains no constructor declarations. Thus, the example is precisely equivalent to

class Message
{
   object sender;
   string text;
   public Message(): base() {}
}


10.10.5 Private constructors

When a class declares only private constructors it is not possible for other classes to derive from the class or create instances of the class (an exception being classes nested within the class). Private constructors are commonly used in classes that contain only static members. For example:

public class Trig
{
   private Trig() {}      // Prevent instantiation
   public const double PI = 3.14159265358979323846;
   public static double Sin(double x) {...}
   public static double Cos(double x) {...}
   public static double Tan(double x) {...}
}

The Trig class provides a grouping of related methods and constants, but is not intended to be instantiated. It therefore declares a single private constructor. Note that at least one private constructor must be declared to suppress the automatic generation of a default constructor (which always has public access).


10.10.6 Optional constructor parameters

The this(...) form of constructor initializers is commonly used in conjunction with overloading to implement optional constructor parameters. In the example

class Text
{
   public Text(): this(0, 0, null) {}
   public Text(int x, int y): this(x, y, null) {}
   public Text(int x, int y, string s) {
      // Actual constructor implementation
   }
}

the first two constructors merely provide the default values for the missing arguments. Both use a this(...) constructor initializer to invoke the third constructor, which actually does the work of initializing the new instance. The effect is that of optional constructor parameters:

Text t1 = new Text();               // Same as Text(0, 0, null)
Text t2 = new Text(5, 10);            // Same as Text(5, 10, null)
Text t3 = new Text(5, 20, "Hello");


10.11 Destructors

Destructors implement the actions required to destruct instances of a class. Destructors are declared using destructor-declarations:

destructor-declaration:
attributesopt ~ identifier ( ) block

A destructor-declaration may include set of attributes (§17).

The identifier of a destructor-declarator must name the class in which the destructor is declared. If any other name is specified, an error occurs.

The block of a destructor declaration specifies the statements to execute in order to initialize a new instance of the class. This corresponds exactly to the block of an instance method with a void return type (§10.5.7).

Destructors are not inherited. Thus, a class has no other destructors than those that are actually declared in the class.

Destructors are invoked automatically, and cannot be invoked explicitly. An instance becomes eligible for destruction when it is no longer possible for any code to use the instance. Execution of the destructor or destructors for the instance may occur at any time after the instance becomes eligible for destruction. When an instance is destructed, the destructors in an inheritance chain are called in order, from most derived to least derived.


10.12 Static constructors

Static constructors implement the actions required to initialize a class. Static constructors are declared using static-constructor-declarations:

static-constructor-declaration:
attributesopt static identifier ( ) block

A static-constructor-declaration may include set of attributes (§17).

The identifier of a static-constructor-declarator must name the class in which the static constructor is declared. If any other name is specified, an error occurs.

The block of a static constructor declaration specifies the statements to execute in order to initialize the class. This corresponds exactly to the block of a static method with a void return type (§10.5.7).

Static constructors are not inherited.

Static constructors are invoked automatically, and cannot be invoked explicitly. The exact timing and ordering of static constructor execution is not defined, though several guarantees are provided:

  • The static constructor for a class is executed before any instance of the class is created.
  • The static constructor for a class is executed before any static member of the class is referenced.
  • The static constructor for a class is executed before the static constructor of any of its derived classes are executed.
  • The static constructor for a class never executes more than once.

The example

using System;
class Test
{
   static void Main() {
      A.F();
      B.F();
   }
}
class A
{
   static A() {
      Console.WriteLine("Init A");
   }
   public static void F() {
      Console.WriteLine("A.F");
   }
}
class B
{
   static B() {
      Console.WriteLine("Init B");
   }
   public static void F() {
      Console.WriteLine("B.F");
   }
}

could produce either the output:

Init A
A.F
Init B
B.F

or the output:

Init B
Init A
A.F
B.F

because the exact ordering of static constructor execution is not defined.

The example

using System;
class Test
{
   static void Main() {
      Console.WriteLine("1");
      B.G();
      Console.WriteLine("2");
   }
}
class A
{
   static A() {
      Console.WriteLine("Init A");
   }
}
class B: A
{
   static B() {
      Console.WriteLine("Init B");
   }
   public static void G() {
      Console.WriteLine("B.G");
   }
}

is guaranteed to produce the output:

Init A
Init B
B.G

because the static constructor for the class A must execute before the static constructor of the class B, which derives from it.


10.12.1 Class loading and initialization

It is possible to construct circular dependencies that allow static fields with variable initializers to be observed in their default value state.

The example

class A
{
   public static int X = B.Y + 1;
}
class B
{
   public static int Y = A.X + 1;
   static void Main() {
      Console.WriteLine("X = {0}, Y = {1}", A.X, B.Y);
   }
}

produces the output

X = 1, Y = 2

To execute the Main method, the system first loads class B. The static constructor of B proceeds to compute the initial value of Y, which recursively causes A to be loaded because the value of A.X is referenced. The static constructor of A in turn proceeds to compute the initial value of X, and in doing so fetches the default value of Y, which is zero. A.X is thus initialized to 1. The process of loading A then completes, returning to the calculation of the initial value of Y, the result of which becomes 2.

Had the Main method instead been located in class A, the example would have produced the output

X = 2, Y = 1

Circular references in static field initializers should be avoided since it is generally not possible to determine the order in which classes containing such references are loaded.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

11. Structs

© 2000 Microsoft Corporation. All rights reserved.


11.1 Struct declarations

struct-declaration:
attributesopt struct-modifiersopt struct identifier struct-interfacesopt struct-body ;opt


11.1.1 Struct modifiers

struct-modifiers:
struct-modifier
struct-modifiers struct-modifier
struct-modifier:
new
public
protected
internal
private


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

11.1.2 Interfaces

struct-interfaces:
: interface-type-list


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

11.1.3 Struct body

struct-body:
{ struct-member-declarationsopt }


11.2 Struct members

struct-member-declarations:
struct-member-declaration
struct-member-declarations struct-member-declaration
struct-member-declaration:
class-member-declaration


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

11.3 Struct examples

© 2000 Microsoft Corporation. All rights reserved.


11.3.1 Database integer type

The DBInt struct below implements an integer type that can represent the complete set of values of the int type, plus an additional state that indicates an unknown value. A type with these characteristics is commonly used in databases.

public struct DBInt
{
   // The Null member represents an unknown DBInt value.
   public static readonly DBInt Null = new DBInt();
   // When the defined field is true, this DBInt represents a known value
   // which is stored in the value field. When the defined field is false,
   // this DBInt represents an unknown value, and the value field is 0.
   int value;
   bool defined;
   // Private constructor. Creates a DBInt with a known value.
   DBInt(int value) {
      this.value = value;
      this.defined = true;
   }
   // The IsNull property is true if this DBInt represents an unknown value.
   public bool IsNull { get { return !defined; } }
   // The Value property is the known value of this DBInt, or 0 if this
   // DBInt represents an unknown value.
   public int Value { get { return value; } }
   // Implicit conversion from int to DBInt.
   public static implicit operator DBInt(int x) {
      return new DBInt(x);
   }
   // Explicit conversion from DBInt to int. Throws an exception if the
   // given DBInt represents an unknown value.
   public static explicit operator int(DBInt x) {
      if (!x.defined) throw new InvalidOperationException();
      return x.value;
   }
   public static DBInt operator +(DBInt x) {
      return x;
   }
   public static DBInt operator -(DBInt x) {
      return x.defined? new DBInt(-x.value): Null;
   }
   public static DBInt operator +(DBInt x, DBInt y) {
      return x.defined && y.defined? new DBInt(x.value + y.value): Null;
   }
   public static DBInt operator -(DBInt x, DBInt y) {
      return x.defined && y.defined? new DBInt(x.value - y.value): Null;
   }
   public static DBInt operator *(DBInt x, DBInt y) {
      return x.defined && y.defined? new DBInt(x.value * y.value): Null;
   }
   public static DBInt operator /(DBInt x, DBInt y) {
      return x.defined && y.defined? new DBInt(x.value / y.value): Null;
   }
   public static DBInt operator %(DBInt x, DBInt y) {
      return x.defined && y.defined? new DBInt(x.value % y.value): Null;
   }
   public static DBBool operator ==(DBInt x, DBInt y) {
      return x.defined && y.defined?
         new DBBool(x.value == y.value): DBBool.Null;
   }
   public static DBBool operator !=(DBInt x, DBInt y) {
      return x.defined && y.defined?
         new DBBool(x.value != y.value): DBBool.Null;
   }
   public static DBBool operator >(DBInt x, DBInt y) {
      return x.defined && y.defined?
         new DBBool(x.value > y.value): DBBool.Null;
   }
   public static DBBool operator <(DBInt x, DBInt y) {
      return x.defined && y.defined?
         new DBBool(x.value < y.value): DBBool.Null;
   }
   public static DBBool operator >=(DBInt x, DBInt y) {
      return x.defined && y.defined?
         new DBBool(x.value >= y.value): DBBool.Null;
   }
   public static DBBool operator <=(DBInt x, DBInt y) {
      return x.defined && y.defined?
         new DBBool(x.value <= y.value): DBBool.Null;
   }
}


11.3.2 Database boolean type

The DBBool struct below implements a three-valued logical type. The possible values of this type are DBBool.True, DBBool.False, and DBBool.Null, where the Null member indicates an unknown value. Such three-valued logical types are commonly used in databases.

public struct DBBool
{
   // The three possible DBBool values.
   public static readonly DBBool Null = new DBBool(0);
   public static readonly DBBool False = new DBBool(-1);
   public static readonly DBBool True = new DBBool(1);
   // Private field that stores –1, 0, 1 for False, Null, True.
   int value;
   // Private constructor. The value parameter must be –1, 0, or 1.
   DBBool(int value) {
      this.value = value;
   }
   // Properties to examine the value of a DBBool. Return true if this
   // DBBool has the given value, false otherwise.
   public bool IsNull { get { return value == 0; } }
   public bool IsFalse { get { return value < 0; } }
   public bool IsTrue { get { return value > 0; } }
   // Implicit conversion from bool to DBBool. Maps true to DBBool.True and
   // false to DBBool.False.
   public static implicit operator DBBool(bool x) {
      return x? True: False;
   }
   // Explicit conversion from DBBool to bool. Throws an exception if the
   // given DBBool is Null, otherwise returns true or false.
   public static explicit operator bool(DBBool x) {
      if (x.value == 0) throw new InvalidOperationException();
      return x.value > 0;
   }
   // Equality operator. Returns Null if either operand is Null, otherwise
   // returns True or False.
   public static DBBool operator ==(DBBool x, DBBool y) {
      if (x.value == 0 || y.value == 0) return Null;
      return x.value == y.value? True: False;
   }
   // Inequality operator. Returns Null if either operand is Null, otherwise
   // returns True or False.
   public static DBBool operator !=(DBBool x, DBBool y) {
      if (x.value == 0 || y.value == 0) return Null;
      return x.value != y.value? True: False;
   }
   // Logical negation operator. Returns True if the operand is False, Null
   // if the operand is Null, or False if the operand is True.
   public static DBBool operator !(DBBool x) {
      return new DBBool(-x.value);
   }
   // Logical AND operator. Returns False if either operand is False,
   // otherwise Null if either operand is Null, otherwise True.
   public static DBBool operator &(DBBool x, DBBool y) {
      return new DBBool(x.value < y.value? x.value: y.value);
   }
   // Logical OR operator. Returns True if either operand is True, otherwise
   // Null if either operand is Null, otherwise False.
   public static DBBool operator |(DBBool x, DBBool y) {
      return new DBBool(x.value > y.value? x.value: y.value);
   }
   // Definitely true operator. Returns true if the operand is True, false
   // otherwise.
   public static bool operator true(DBBool x) {
      return x.value > 0;
   }
   // Definitely false operator. Returns true if the operand is False, false
   // otherwise.
   public static bool operator false(DBBool x) {
      return x.value < 0;
   }
}


12. Arrays

An array is a data structure that contains a number of variables which are accessed through computed indices. The variables contained in an array, also called the elements of the array, are all of the same type, and this type is called the element type of the array.

An array has a rank which determines the number of indices associated with each array element. The rank of an array is also referred to as the dimensions of the array. An array with a rank of one is called a single-dimensional array, and an array with a rank greater than one is called a multi-dimensional array.

Each dimension of an array has an associated length which is an integral number greater than or equal to zero. The dimension lengths are not part of the type of the array, but rather are established when an instance of the array type is created at run-time. The length of a dimension determines the valid range of indices for that dimension: For a dimension of length N, indices can range from 0 to N – 1 inclusive. The total number of elements in an array is the product of the lengths of each dimension in the array. If one or more of the dimensions of an array have a length of zero, the array is said to be empty.

The element type of an array can be any type, including an array type.


12.1 Array types

An array type is written as a non-array-type followed by one or more rank-specifiers:

array-type:
non-array-type rank-specifiers
non-array-type:
type
rank-specifiers:
rank-specifier
rank-specifiers rank-specifier
rank-specifier:
[ dim-separatorsopt ]
dim-separators:
,
dim-separators ,

A non-array-type is any type that is not itself an array-type.

The rank of an array type is given by the leftmost rank-specifier in the array-type: A rank-specifier indicates that the array is an array with a rank of one plus the number of "," tokens in the rank-specifier.

The element type of an array type is the type that results from deleting the leftmost rank-specifier:

  • An array type of the form T[R] is an array with rank R and a non-array element type T.
  • An array type of the form T[R][R1]...[RN] is an array with rank R and an element type T[R1]...[RN].

In effect, the rank-specifiers are read from left to right before the final non-array element type. For example, the type int[][,,][,] is a single-dimensional array of three-dimensional arrays of two-dimensional arrays of int.

Arrays with a rank of one are called single-dimensional arrays. Arrays with a rank greater than one are called multi-dimensional arrays, and are also referred to as two-dimensional arrays, three-dimensional arrays, and so on.

At run-time, a value of an array type can be null or a reference to an instance of that array type.


12.1.1 The System.Array type

The System.Array type is the abstract base type of all array types. An implicit reference conversion (§6.1.4) exists from any array type to System.Array, and an explicit reference conversion (§6.2.3) exists from System.Array to any array type. Note that System.Array is itself not an array-type. Rather, it is a class-type from which all array-types are derived.

At run-time, a value of type System.Array can be null or a reference to an instance of any array type.


12.2 Array creation

Array instances are created by array-creation-expressions (§7.5.10.2) or by field or local variable declarations that include an array-initializer (§12.6).

When an array instance is created, the rank and length of each dimension are established and then remain constant for the entire lifetime of the instance. In other words, it is not possible to change the rank of an existing array instance, nor is it possible to resize its dimensions.

An array instance created by an array-creation-expression is always of an array type. The System.Array type is an abstract type that cannot be instantiated.

Elements of arrays created by array-creation-expressions are always initialized to their default value (§5.2).


12.3 Array element access

Array elements are accessed using element-access expressions (§7.5.6.1) of the form A[I1, I2, ..., IN], where A is an expression of an array type and each IX is an expression of type int. The result of an array element access is a variable, namely the array element selected by the indices.

The elements of an array can be enumerated using a foreach statement (§8.8.4).


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

12.4 Array members

Every array type inherits the members declared by the System.Array type.


12.5 Array covariance

For any two reference-types A and B, if an implicit reference conversion (§6.1.4) or explicit reference conversion (§6.2.3) exists from A to B, then the same reference conversion also exists from the array type A[R] to the array type B[R], where R is any given rank-specifier (but the same for both array types). This relationship is known as array covariance. Array covariance in particular means that a value of an array type A[R] may actually be a reference to an instance of an array type B[R], provided an implicit reference conversion exists from B to A.

Because of array covariance, assignments to elements of reference type arrays include a run-time check which ensures that the value being assigned to the array element is actually of a permitted type (§7.13.1). For example:

class Test
{
   static void Fill(object[] array, int index, int count, object value) {
      for (int i = index; i < index + count; i++) array[i] = value;
   }
   static void Main() {
      string[] strings = new string[100];
      Fill(strings, 0, 100, "Undefined");
      Fill(strings, 0, 10, null);
      Fill(strings, 90, 10, 0);
   }
}

The assignment to array[i] in the Fill method implicitly includes a run-time check which ensures that the object referenced by value is either null or an instance of a type that is compatible with the actual element type of array. In Main, the first two invocations of Fill succeed, but the third invocation causes an ArrayTypeMismatchException to be thrown upon executing the first assignment to array[i]. The exception occurs because a boxed int cannot be stored in a string array.

Array covariance specifically does not extend to arrays of value-types. For example, no conversion exists that permits an int[] to be treated as an object[].


12.6 Array initializers

Array initializers may be specified in field declarations (§10.4), local variable declarations (§8.5.1), and array creation expressions (§7.5.10.2):

array-initializer:
{ variable-initializer-listopt }
{ variable-initializer-list , }
variable-initializer-list:
variable-initializer
variable-initializer-list , variable-initializer
variable-initializer:
expression
array-initializer

An array initializer consists of a sequence of variable initializers, enclosed by "{"and "}" tokens and separated by "," tokens. Each variable initializer is an expression or, in the case of a multi-dimensional array, a nested array initializer.

The context in which an array initializer is used determines the type of the array being initialized. In an array creation expression, the array type immediately precedes the initializer. In a field or variable declaration, the array type is the type of the field or variable being declared. When an array initializer is used in a field or variable declaration, such as:

int[] a = {0, 2, 4, 6, 8};

it is simply shorthand for an equivalent array creation expression:

int[] a = new int[] {0, 2, 4, 6, 8}

For a single-dimensional array, the array initializer must consist of a sequence of expressions that are assignment compatible with the element type of the array. The expressions initialize array elements in increasing order, starting with the element at index zero. The number of expressions in the array initializer determines the length of the array instance being created. For example, the array initializer above creates an int[] instance of length 5 and then initializes the instance with the following values:

a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;

For a multi-dimensional array, the array initializer must have as many levels of nesting as there are dimensions in the array. The outermost nesting level corresponds to the leftmost dimension and the innermost nesting level corresponds to the rightmost dimension. The length of each dimension of the array is determined by the number of elements at the corresponding nesting level in the array initializer. For each nested array initializer, the number of elements must be the same as the other array initializers at the same level. The example:

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

creates a two-dimensional array with a length of five for the leftmost dimension and a length of two for the rightmost dimension:

int[,] b = new int[5, 2];

and then initializes the array instance with the following values:

b[0, 0] = 0; b[0, 1] = 1;
b[1, 0] = 2; b[1, 1] = 3;
b[2, 0] = 4; b[2, 1] = 5;
b[3, 0] = 6; b[3, 1] = 7;
b[4, 0] = 8; b[4, 1] = 9;

When an array creation expression includes both explicit dimension lengths and an array initializer, the lengths must be constant expressions and the number of elements at each nesting level must match the corresponding dimension length. Some examples:

int i = 3;
int[] x = new int[3] {0, 1, 2};      // OK
int[] y = new int[i] {0, 1, 2};      // Error, i not a constant
int[] z = new int[3] {0, 1, 2, 3};   // Error, length/initializer mismatch

Here, the initializer for y is in error because the dimension length expression is not a constant, and the initializer for z is in error because the length and the number of elements in the initializer do not agree.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

13. Interfaces

© 2000 Microsoft Corporation. All rights reserved.


13.1 Interface declarations

An interface-declaration is a type-declaration (§9.5) that declares a new interface type.

interface-declaration:
attributesopt interface-modifiersopt interface identifier interface-baseopt interface-body ;opt

An interface-declaration consists of an optional set of attributes (§17), followed by an optional set of interface-modifiers (§13.1.1), followed by the keyword interface and an identifier that names the interface, optionally followed by an optional interface-base specification (§13.1.2), followed by a interface-body (§13.1.3), optionally followed by a semicolon.


13.1.1 Interface modifiers

An interface-declaration may optionally include a sequence of interface modifiers:

interface-modifiers:
interface-modifier
interface-modifiers interface-modifier
interface-modifier:
new
public
protected
internal
private

It is an error for the same modifier to appear multiple times in an interface declaration.

The new modifier is only permitted on nested interfaces. It specifies that the interface hides an inherited member by the same name, as described in §10.2.2.

The public, protected, internal, and private modifiers control the accessibility of the interface. Depending on the context in which the interface declaration occurs, only some of these modifiers may be permitted (§3.3.1).


13.1.2 Base interfaces

An interface can inherit from zero or more interfaces, which are called the explicit base interfaces of the interface. When an interface has more than zero explicit base interfaces then in the declaration of the interface, the interface identifier is followed by a colon and a comma-separated list of base interface identifiers.

interface-base:
: interface-type-list

The explicit base interfaces of an interface must be at least as accessible as the interface itself (§3.3.4). For example, it is an error to specify a private or internal interface in the interface-base of a public interface.

It is an error for an interface to directly or indirectly inherit from itself.

The base interfaces of an interface are the explicit base interfaces and their base interfaces. In other words, the set of base interfaces is the complete transitive closure of the explicit base interfaces, their explicit base interfaces, and so on. 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 base interfaces of IComboBox are IControl, ITextBox, and IListBox.

An interface inherits all members of its base interfaces. In other words, the IComboBox interface above inherits members SetText and SetItems as well as Paint.

A class or struct that implements an interface also implicitly implements all of the interface’s base interfaces.


13.1.3 Interface body

The interface-body of an interface defines the members of the interface.

interface-body:
{ interface-member-declarationsopt }


13.2 Interface members

The members of an interface are the members inherited from the base interfaces and the members declared by the interface itself.

interface-member-declarations:
interface-member-declaration
interface-member-declarations interface-member-declaration
interface-member-declaration:
interface-method-declaration
interface-property-declaration
interface-event-declaration
interface-indexer-declaration

An interface declaration may declare zero or more members. The members of an interface must be methods, properties, events, or indexers. An interface cannot contain constants, fields, operators, constructors, destructors, static constructors, or types, nor can an interface contain static members of any kind.

All interface members implicitly have public access. It is an error for interface member declarations to include any modifiers. In particular, interface members cannot be declared with the abstract, public, protected, internal, private, virtual, override, or static modifiers.

The example

public delegate void StringListEvent(IStringList sender);
public interface IStringList
{
   void Add(string s);
   int Count { get; }
   event StringListEvent Changed;
   string this[int index] { get; set; }
}

declares an interface that contains one each of the possible kinds of members: A method, a property, an event, and an indexer.

An interface-declaration creates a new declaration space (§3.1), and the interface-member-declarations immediately contained by the interface-declaration introduce new members into this declaration space. The following rules apply to interface-member-declarations:

  • The name of a method must differ from the names of all properties and events declared in the same interface. In addition, the signature (§3.4) of a method must differ from the signatures of all other methods declared in the same interface.
  • The name of a property or event must differ from the names of all other members declared in the same interface.
  • The signature of an indexer must differ from the signatures of all other indexers declared in the same interface.

The inherited members of an interface are specifically not part of the declaration space of the interface. Thus, an interface is allowed to declare a member with the same name or signature as an inherited member. When this occurs, the derived interface member is said to hide the base interface member. Hiding an inherited member is not considered an error, but it does cause the compiler to issue a warning. To suppress the warning, the declaration of the derived interface member must include a new modifier to indicate that the derived member is intended to hide the base member. This topic is discussed further in §3.5.1.2.

If a new modifier is included in a declaration that doesn’t hide an inherited member, a warning is issued to that effect. This warning is suppressed by removing the new modifier.


13.2.1 Interface methods

Interface methods are declared using interface-method-declarations:

interface-method-declaration:
attributesopt newopt return-type identifier ( formal-parameter-listopt ) ;

The attributes, return-type, identifier, and formal-parameter-list of an interface method declaration have the same meaning as those of a method declaration in a class (§10.5). An interface method declaration is not permitted to specify a method body, and the declaration therefore always ends with a semicolon.


13.2.2 Interface properties

Interface properties are declared using interface-property-declarations:

interface-property-declaration:
attributesopt newopt type identifier { interface-accessors }
interface-accessors:
get ;
set ;
get ; set ;
set ; get ;

The attributes, type, and identifier of an interface property declaration have the same meaning as those of a property declaration in a class (§10.6).

The accessors of an interface property declaration correspond to the accessors of a class property declaration (§10.6.2), except that no modifiers can be specified and the accessor body must always be a semicolon. Thus, the accessors simply indicate whether the property is read-write, read-only, or write-only.


13.2.3 Interface events

Interface events are declared using interface-event-declarations:

interface-event-declaration:
attributesopt newopt event type identifier ;

The attributes, type, and identifier of an interface event declaration have the same meaning as those of an event declaration in a class (§10.7).


13.2.4 Interface indexers

Interface indexers are declared using interface-indexer-declarations:

interface-indexer-declaration:
attributesopt newopt type this [ formal-index-parameter-list ] { interface-accessors }

The attributes, type, and formal-parameter-list of an interface indexer declaration have the same meaning as those of an indexer declaration in a class (§10.8).

The accessors of an interface indexer declaration correspond to the accessors of a class indexer declaration (§???), except that no modifiers can be specified and the accessor body must always be a semicolon. Thus, the accessors simply indicate whether the indexer is read-write, read-only, or write-only.


13.2.5 Interface member access

Interface members are accessed through member access (§7.5.4) and indexer access (§7.5.6.2) expressions of the form I.M and I[A], where I is an instance of an interface type, M is a method, property, or event of that interface type, and A is an indexer argument list.

For interfaces that are strictly single-inheritance (each interface in the inheritance chain has exactly zero or one direct base interface), the effects of the member lookup (§7.3), method invocation (§7.5.5.1), and indexer access (§7.5.6.2) rules are exactly the same as for classes and structs: More derived members hide less derived members with the same name or signature. However, for multiple-inheritance interfaces, ambiguities can occur when two or more unrelated base interfaces declare members with the same name or signature. This section shows several examples of such situations. In all cases, explicit casts can be included in the program code to resolve the ambiguities.

In the example

interface IList
{
   int Count { get; set; }
}
interface ICounter
{
   void Count(int i);
}
interface IListCounter: IList, ICounter {}
class C
{
   void Test(IListCounter x) {
      x.Count(1);                  // Error, Count is ambiguous
      x.Count = 1;               // Error, Count is ambiguous
      ((IList)x).Count = 1;      // Ok, invokes IList.Count.set
      ((ICounter)x).Count(1);      // Ok, invokes ICounter.Count
   }
}

the first two statements cause compile-time errors because the member lookup (§7.3) of Count in IListCounter is ambiguous. As illustrated by the example, the ambiguity is resolved by casting x to the appropriate base interface type. Such casts have no run-time costs—they merely consist of viewing the instance as a less derived type at compile-time.

In the example

interface IInteger
{
   void Add(int i);
}
interface IDouble
{
   void Add(double d);
}
interface INumber: IInteger, IDouble {}
class C
{
   void Test(INumber n) {
      n.Add(1);               // Error, both Add methods are applicable
      n.Add(1.0);               // Ok, only IDouble.Add is applicable
      ((IInteger)n).Add(1);   // Ok, only IInteger.Add is a candidate
      ((IDouble)n).Add(1);      // Ok, only IDouble.Add is a candidate
   }
}

the invocation n.Add(1) is ambiguous because a method invocation (§7.5.5.1) requires all overloaded candidate methods to be declared in the same type. However, the invocation n.Add(1.0) is permitted because only IDouble.Add is applicable. When explicit casts are inserted, there is only one candidate method, and thus no ambiguity.

In the example

interface IBase
{
   void F(int i);
}
interface ILeft: IBase
{
   new void F(int i);
}
interface IRight: IBase
{
   void G();
}
interface IDerived: ILeft, IRight {}
class A
{
   void Test(IDerived d) {
      d.F(1);               // Invokes ILeft.F
      ((IBase)d).F(1);      // Invokes IBase.F
      ((ILeft)d).F(1);      // Invokes ILeft.F
      ((IRight)d).F(1);      // Invokes IBase.F
   }
}

the IBase.F member is hidden by the ILeft.F member. The invocation d.F(1) thus selects ILeft.F, even though IBase.F appears to not be hidden in the access path that leads through IRight.

The intuitive rule for hiding in multiple-inheritance interfaces is simply this: If a member is hidden in any access path, it is hidden in all access paths. Because the access path from IDerived to ILeft to IBase hides IBase.F, the member is also hidden in the access path from IDerived to IRight to IBase.


13.3 Fully qualified interface member names

An interface member is sometimes referred to by its fully qualified name. The fully qualified name of an interface member consists of the name of the interface in which the member is declared, followed by a dot, followed by the name of the member. For example, given the declarations

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

the fully qualified name of Paint is IControl.Paint and the fully qualified name of SetText is ITextBox.SetText.

Note that the fully qualified name of a member references the interface in which the member is declared. Thus, in the example above, it is not possible to refer to Paint as ITextBox.Paint.

When an interface is part of a namespace, the fully qualified name of an interface member includes the namespace name. For example

namespace System
{
   public interface ICloneable
   {
      object Clone();
   }
}

Here, the fully qualified name of the Clone method is System.ICloneable.Clone.


13.4 Interface implementations

Interfaces may be implemented by classes and structs. To indicate that a class or struct implements an interface, the interface identifier is included in the base class list of the class or struct.

interface ICloneable
{
   object Clone();
}
interface IComparable
{
   int CompareTo(object other);
}
class ListEntry: ICloneable, IComparable
{
   public object Clone() {...}
   public int CompareTo(object other) {...}
}

A class or struct that implements an interface also implicitly implements all of the interface’s base interfaces. This is true even if the class or struct doesn’t explicitly list all base interfaces in the base class list.

interface IControl
{
   void Paint();
}
interface ITextBox: IControl
{
   void SetText(string text);
}
class TextBox: ITextBox
{
   public void Paint() {...}
   public void SetText(string text) {...}
}

Here, class TextBox implements both IControl and ITextBox.


13.4.1 Explicit interface member implementations

For purposes of implementing interfaces, a class or struct may declare explicit interface member implementations. An explicit interface member implementation is a method, property, event, or indexer declaration that references a fully qualified interface member name. For example

interface ICloneable
{
   object Clone();
}
interface IComparable
{
   int CompareTo(object other);
}
class ListEntry: ICloneable, IComparable
{
   object ICloneable.Clone() {...}
   int IComparable.CompareTo(object other) {...}
}

Here, ICloneable.Clone and IComparable.CompareTo are explicit interface member implementations.

It is not possible to access an explicit interface member implementation through its fully qualified name in a method invocation, property access, or indexer access. An explicit interface member implementation can only be accessed through an interface instance, and is in that case referenced simply by its member name.

It is an error for an explicit interface member implementation to include access modifiers, as is it an error to include the abstract, virtual, override, or static modifiers.

Explicit interface member implementations have different accessibility characteristics than other members. Because explicit interface member implementations are never accessible through their fully qualified name in a method invocation or a property access, they are in a sense private. However, since they can be accessed through an interface instance, they are in a sense also public.

Explicit interface member implementations serve two primary purposes:

  • Because explicit interface member implementations are not accessible through class or struct instances, they allow interface implementations to be excluded from the public interface of a class or struct. This is particularly useful when a class or struct implements an internal interface that is of no interest to a consumer of the class or struct.
  • Explicit interface member implementations allow disambiguation of interface members with the same signature. Without explicit interface member implementations it would be impossible for a class or struct to have different implementations of interface members with the same signature and return type, as would it be impossible for a class or struct to have any implementation at all of interface members with the same signature but with different return types.

For an explicit interface member implementation to be valid, the class or struct must name an interface in its base class list that contains a member whose fully qualified name, type, and parameter types exactly match those of the explicit interface member implementation. Thus, in the following class

class Shape: ICloneable
{
   object ICloneable.Clone() {...}
   int IComparable.CompareTo(object other) {...}
}

the declaration of IComparable.CompareTo is invalid because IComparable is not listed in the base class list of Shape and is not a base interface of ICloneable. Likewise, in the declarations

class Shape: ICloneable
{
   object ICloneable.Clone() {...}
}
class Ellipse: Shape
{
   object ICloneable.Clone() {...}
}

the declaration of ICloneable.Clone in Ellipse is in error because ICloneable is not explicitly listed in the base class list of Ellipse.

The fully qualified name of an interface member must reference the interface in which the member was declared. Thus, in the declarations

interface IControl
{
   void Paint();
}
interface ITextBox: IControl
{
   void SetText(string text);
}
class TextBox: ITextBox
{
   void IControl.Paint() {...}
   void ITextBox.SetText(string text) {...}
}

the explicit interface member implementation of Paint must be written as IControl.Paint.


13.4.2 Interface mapping

A class or struct must provide implementations of all members of the interfaces that are listed in the base class list of the class or struct. The process of locating implementations of interface members in an implementing class or struct is known as interface mapping.

Interface mapping for a class or struct C locates an implementation for each member of each interface specified in the base class list of C. The implementation of a particular interface member I.M, where I is the interface in which the member M is declared, is determined by examining each class or struct S, starting with C and repeating for each successive base class of C, until a match is located:

  • If S contains a declaration of an explicit interface member implementation that matches I and M, then this member is the implementation of I.M.
  • Otherwise, if S contains a declaration of a non-static public member that matches M, then this member is the implementation of I.M.

An error occurs if implementations cannot be located for all members of all interfaces specified in the base class list of C. Note that the members of an interface include those members that are inherited from base interfaces.

For purposes of interface mapping, a class member A matches an interface member B when:

  • A and B are methods, and the name, type, and formal parameter lists of A and B are identical.
  • A and B are properties, the name and type of A and B are identical, and A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation).
  • A and B are events, and the name and type of A and B are identical.
  • A and B are indexers, the type and formal parameter lists of A and B are identical, and A has the same accessors as B (A is permitted to have additional accessors if it is not an explicit interface member implementation).

Notable implications of the interface mapping algorithm are:

  • Explicit interface member implementations take precedence over other members in the same class or struct when determining the class or struct member that implements an interface member.
  • Private, protected, and static members do not participate in interface mapping.

In the example

interface ICloneable
{
   object Clone();
}
class C: ICloneable
{
   object ICloneable.Clone() {...}
   public object Clone() {}
}

the ICloneable.Clone member of C becomes the implementation of Clone in ICloneable because explicit interface member implementations take precedence over other members.

If a class or struct implements two or more interfaces containing a member with the same name, type, and parameter types, it is possible to map each of those interface members onto a single class or struct member. For example

interface IControl
{
   void Paint();
}
interface IForm
{
   void Paint();
}
class Page: IControl, IForm
{
   public void Paint() {...}
}

Here, the Paint methods of both IControl and IForm are mapped onto the Paint method in Page. It is of course also possible to have separate explicit interface member implementations for the two methods.

If a class or struct implements an interface that contains hidden members, then some members must necessarily be implemented through explicit interface member implementations. For example

interface IBase
{
   int P { get; }
}
interface IDerived: IBase
{
   new int P();
}

An implementation of this interface would require at least one explicit interface member implementation, and would take one of the following forms

class C: IDerived
{
   int IBase.P { get {...} }
   int IDerived.P() {...}
}
class C: IDerived
{
   public int P { get {...} }
   int IDerived.P() {...}
}
class C: IDerived
{
   int IBase.P { get {...} }
   public int P() {...}
}

When a class implements multiple interfaces that have the same base interface, there can be only one implementation of the base interface. In the example

interface IControl
{
   void Paint();
}
interface ITextBox: IControl
{
   void SetText(string text);
}
interface IListBox: IControl
{
   void SetItems(string[] items);
}
class ComboBox: IControl, ITextBox, IListBox
{
   void IControl.Paint() {...}
   void ITextBox.SetText(string text) {...}
   void IListBox.SetItems(string[] items) {...}
}

it is not possible to have separate implementations for the IControl named in the base class list, the IControl inherited by ITextBox, and the IControl inherited by IListBox. Indeed, there is no notion of a separate identity for these interfaces. Rather, the implementations of ITextBox and IListBox share the same implementation of IControl, and ComboBox is simply considered to implement three interfaces, IControl, ITextBox, and IListBox.

The members of a base class participate in interface mapping. In the example

interface Interface1
{
   void F();
}
class Class1
{
   public void F() {}
   public void G() {}
}
class Class2: Class1, Interface1
{
   new public void G() {}
}

the method F in Class1 is used in Class2's implementation of Interface1.


13.4.3 Interface implementation inheritance

A class inherits all interface implementations provided by its base classes.

Without explicitly re-implementing an interface, a derived class cannot in any way alter the interface mappings it inherits from its base classes. For example, in the declarations

interface IControl
{
   void Paint();
}
class Control: IControl
{
   public void Paint() {...}
}
class TextBox: Control
{
   new public void Paint() {...}
}

the Paint method in TextBox hides the Paint method in Control, but it does not alter the mapping of Control.Paint onto IControl.Paint, and calls to Paint through class instances and interface instances will have the following effects

Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint();         // invokes Control.Paint();
t.Paint();         // invokes TextBox.Paint();
ic.Paint();         // invokes Control.Paint();
it.Paint();         // invokes Control.Paint();

However, when an interface method is mapped onto a virtual method in a class, it is possible for derived classes to override the virtual method and alter the implementation of the interface. For example, rewriting the declarations above to

interface IControl
{
   void Paint();
}
class Control: IControl
{
   public virtual void Paint() {...}
}
class TextBox: Control
{
   public override void Paint() {...}
}

the following effects will now be observed

Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint();         // invokes Control.Paint();
t.Paint();         // invokes TextBox.Paint();
ic.Paint();         // invokes Control.Paint();
it.Paint();         // invokes TextBox.Paint();

Since explicit interface member implementations cannot be declared virtual, it is not possible to override an explicit interface member implementation. It is however perfectly valid for an explicit interface member implementation to call another method, and that other method can be declared virtual to allow derived classes to override it. For example

interface IControl
{
   void Paint();
}
class Control: IControl
{
   void IControl.Paint() { PaintControl(); }
   protected virtual void PaintControl() {...}
}
class TextBox: Control
{
   protected override void PaintControl() {...}
}

Here, classes derived from Control can specialize the implementation of IControl.Paint by overriding the PaintControl method.


13.4.4 Interface re-implementation

A class that inherits an interface implementation is permitted to re-implement the interface by including it in the base class list.

A re-implementation of an interface follows exactly the same interface mapping rules as an initial implementation of an interface. Thus, the inherited interface mapping has no effect whatsoever on the interface mapping established for the re-implementation of the interface. For example, in the declarations

interface IControl
{
   void Paint();
}
class Control: IControl
{
   void IControl.Paint() {...}
}
class MyControl: Control, IControl
{
   public void Paint() {}
}

the fact that Control maps IControl.Paint onto Control.IControl.Paint doesn’t affect the re-implementation in MyControl, which maps IControl.Paint onto MyControl.Paint.

Inherited public member declarations and inherited explicit interface member declarations participate in the interface mapping process for re-implemented interfaces. For example

interface IMethods
{
   void F();
   void G();
   void H();
   void I();
}
class Base: IMethods
{
   void IMethods.F() {}
   void IMethods.G() {}
   public void H() {}
   public void I() {}
}
class Derived: Base, IMethods
{
   public void F() {}
   void IMethods.H() {}
}

Here, the implementation of IMethods in Derived maps the interface methods onto Derived.F, Base.IMethods.G, Derived.IMethods.H, and Base.I.

When a class implements an interface, it implicitly also implements all of the interface’s base interfaces. Likewise, a re-implementation of an interface is also implicitly a re-implementation of all of the interface’s base interfaces. For example

interface IBase
{
   void F();
}
interface IDerived: IBase
{
   void G();
}
class C: IDerived
{
   void IBase.F() {...}
   void IDerived.G() {...}
}
class D: C, IDerived
{
   public void F() {...}
   public void G() {...}
}

Here, the re-implementation of IDerived also re-implements IBase, mapping IBase.F onto D.F.


13.4.5 Abstract classes and interfaces

Like a non-abstract class, an abstract class must provide implementations of all members of the interfaces that are listed in the base class list of the class. However, an abstract class is permitted to map interface methods onto abstract methods. For example

interface IMethods
{
   void F();
   void G();
}
abstract class C: IMethods
{
   public abstract void F();
   public abstract void G();
}

Here, the implementation of IMethods maps F and G onto abstract methods, which must be overridden in non-abstract classes that derive from C.

Note that explicit interface member implementations cannot be abstract, but explicit interface member implementations are of course permitted to call abstract methods. For example

interface IMethods
{
   void F();
   void G();
}
abstract class C: IMethods
{
   void IExample.F() { FF(); }
   void IExample.G() { GG(); }
   protected abstract void FF();
   protected abstract void GG();
}

Here, non-abstract classes that derive from C would be required to override FF and GG, thus providing the actual implementation of IMethods.


14. Enums

An enum type is a distinct type with named constants. Enum declarations may appear in the same places that class declarations can occur.

The example

using System;
enum Color
{
   Red,
   Green,
   Blue
}

declares an enum type named Color with members Red, Green, and Blue.


14.1 Enum declarations

An enum declaration declares a new enum type. An enum declaration begins with the keyword enum, and defines the name, accessibility, underlying type, and members of the enum.

enum-declaration:
attributesopt enum-modifiersopt enum identifier enum-baseopt enum-body ;opt
enum-modifiers:
enum-modifier
enum-modifiers enum-modifier
enum-modifier:
new
public
protected
internal
private
enum-base:
: integral-type
enum-body:
{ enum-member-declarationsopt }
{ enum-member-declarations , }

Each enum type has a corresponding integral type called the underlying type of the enum type. This underlying type can represent all the enumerator values defined in the enumeration. An enum declaration may explicitly declare an underlying type of byte, sbyte, short, ushort, int, uint, long or ulong. Note that char cannot be used as an underlying type. An enum declaration that does not explicitly declare an underlying type has an underlying type of int.

The example

enum Color: long
{
   Red,
   Green,
   Blue
}

declares an enum with an underlying type of long. A developer might choose to use an underlying type of long, as in the example, to enable the use of values that are in the range of long but not in the range of int, or to preserve this option for the future.


14.2 Enum members

The body of an enum type declaration defines zero or more enum members, which are the named constants of the enum type. No two enum members can have the same name. An enum declaration can not contain declarations of methods, properties, events, operators, or types.

enum-member-declarations:
enum-member-declaration
enum-member-declarations , enum-member-declaration
enum-member-declaration:
attributesopt identifier
attributesopt identifier = constant-expression

Each enum member has an associated constant value. The type of this value is the underlying type for the containing enum. The constant value for each enum member must be in the range of the underlying type for the enum. The example

enum Color: uint
{
   Red = -1
   Green = -2,
   Blue = -3
}

is in error because the constant values -1, -2, and –3 are not in the range of the underlying integral type uint.

Multiple enum members may share the same associated value. The example

enum Color
{
   Red,
   Green,
   Blue,

   Max = Blue,
}

shows an eum that has two enum members – Blue and Max – that have the same associated value.

The associated value of an enum member is assigned either implicitly or explicitly. If the declaration of the enum member has a constant-expression initializer, the value of that constant expression, implicitly converted to the underlying type of the enum, is the associated value of the enum member. If the declaration of the enum member has no initializer, its associated value is set implicitly, as follows:

  • If the enum member is the first enum member declared in the enum type, its associated value is zero.
  • Otherwise, the associated value of the enum member is obtained by increasing the associated value of the previous enum member by one. This increased value must be within the range of values that can be represented by the underlying type.

The example

using System;
enum Color
{
   Red,
   Green = 10,
   Blue
}
class Test
{
   static void Main() {
      Console.WriteLine(StringFromColor(Color.Red));
      Console.WriteLine(StringFromColor(Color.Green));
      Console.WriteLine(StringFromColor(Color.Blue));
   }
   static string StringFromColor(Color c) {
      switch (c) {
         case Color.Red:
            return String.Format("Red = {0}", (int) c);
         case Color.Green:
            return String.Format("Green = {0}", (int) c);
         case Color.Blue:
            return String.Format("Blue = {0}", (int) c);
         default:
            return "Invalid color";
      }
   }
}

prints out the enum member names and their associated values. The output is:

Red = 0
Blue = 10
Green = 11

for the following reasons:

  • the enum member Red is automatically assigned the value zero (since it has no initializer and is the first enum member);
  • the enum member Blue is explicitly given the value 10;
  • and the enum member Green is automatically assigned the value one greater than the member that textually precedes it.

The associated value of an enum member may not, directly or indirectly, use the value of its own associated enum member. Other than this circularity restriction, enum member initializers may freely refer to other enum member initializers, regardless of their textual position. Within an enum member initializer, values of other enum members are always treated as having the type of their underlying type, so that casts are not necessary when referring to other enum members.

The example

enum Circular
{
   A = B
   B
}

is invalid because the declarations of A and B are circular. A depends on B explicitly, and B depends on A implicitly.

Enum members are named and scoped in a manner exactly analogous to fields within classes. The scope of an enum member is the body of its containing enum type. Within that scope, enum members can be referred to by their simple name. From all other code, the name of an enum member must be qualified with the name of its enum type. Enum members do not have any declared accessibility—an enum member is accessible if its containing enum type is accessible.


14.3 Enum values and operations

Each enum type defines a distinct type; an explicit enumeration conversion (§6.2.2) is required to convert between an enum type and an integral type, or between two enum types. The set of values that an enum type can take on is not limited by its enum members. In particular, any value of the underlying type of an enum can be cast to the enum type, and is a distinct valid value of that enum type.

Enum members have the type of their containing enum type (except within other enum member initializers: see §14.2). The value of an enum member declared in enum type E with associated value v is (E)v.

The following operators can be used on values of enum types: ==, !=, <, >, <=, >= (§7.9.5), + (§7.7.4), - (§7.7.5), ^, &, | (§7.10.2), ~ (§7.6.4), ++, -- (§7.5.9, §7.6.7), sizeof (§7.5.12).

Every enum type automatically derives from the class System.Enum. Thus, inherited methods and properties of this class can be used on values of an enum type.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

15. Delegates

© 2000 Microsoft Corporation. All rights reserved.


15.1 Delegate declarations

delegate-declaration:
attributesopt delegate-modifiersopt delegate result-type identifier ( formal-parameter-listopt ) ;


15.1.1 Delegate modifiers

delegate-modifiers:
delegate-modifier
delegate-modifiers delegate-modifier
delegate-modifier:
new
public
protected
internal
private


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

16. Exceptions

© 2000 Microsoft Corporation. All rights reserved.


17. Attributes

Much of the C# language enables the programmer to specify declarative information about the entities defined in the program. For example, the accessibility of a method in a class is specified by decorating it with the method-modifiers public, protected, internal, and private.

C# enables programmers to invent new kinds of declarative information, to specify declarative information for various program entities, and to retrieve attribute information in a run-time environment. For instance, a framework might define a HelpAttribute attribute that can be placed on program elements such as classes and methods to provide a mapping from program elements to documentation for them.

New kinds of declarative information are defined through the declaration of attribute classes (§17.1), which may have positional and named parameters (§17.1.2). Declarative information is specified a C# program using attributes (§17.2), and can be retrieved at run-time as attribute instances (§17.3).


17.1 Attribute classes

The declaration of an attribute class defines a new kind of attribute that can be placed on a declaration. A class that derives from the abstract class System.Attribute, whether directly or indirectly, is an attribute class.

A declaration of an attribute class is subject to the following additional restrictions:

  • A non-abstract attribute class must have public accessibility.
  • All of the types in which a non-abstract attribute class is nested must have public accessibility.
  • A non-abstract attribute class must have at least one public constructor.
    • Each of the formal parameter types for each of the public constructors of an attribute class must be an attribute parameter type (§17.1.3).

By convention, attribute classes are named with a suffix of Attribute. Uses of an attribute may either include or omit this suffix.


17.1.1 The AttributeUsage attribute

The AttributeUsage attribute is used to describe how an attribute class can be used.

The AttributeUsage attribute has a positional parameter named that enables an attribute class to specify the kinds of declarations on which it can be used. The example

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class SimpleAttribute: System.Attribute
{}

defines an attribute class named SimpleAttribute that can be placed on class-declarations and interface-declarations. The example

[Simple] class Class1 {…}
[Simple] interface Interface1 {…}

shows several uses of the Simple attribute. The attribute is defined with a class named SimpleAttribute, but uses of this attribute may omit the Attribute suffix, thus shortening the name to Simple. The example above is semantically equivalent to the example

[SimpleAttribute] class Class1 {…}
[SimpleAttribute] interface Interface1 {…}

The AttributeUsage attribute has an AllowMultiple named parameter that specifies whether the indicated attribute can be specified more than once for a given entity. An attribute that can be specified more than once on an entity is called a multi-use attribute class. An attribute that can be specified at most once on an entity is called a single-use attribute class.

The example

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class AuthorAttribute: System.Attribute {
   public AuthorAttribute(string value);
   public string Value { get {…} }
}

defines a multi-use attribute class named AuthorAttribute. The example

[Author("Brian Kernighan"), Author("Dennis Ritchie")]
class Class1 {…}

shows a class declaration with two uses of the Author attribute.


17.1.2 Positional and named parameters

Attribute classes can have positional parameters and named parameters. Each public constructor for an attribute class defines a valid sequence of positional parameters for the attribute class. Each non-static public read-write field and property for an attribute class defines a named parameter for the attribute class.

The example

[AttributeUsage(AttributeTargets.Class]
public class HelpAttribute: System.Attribute
{
   public HelpAttribute(string url) {   // url is a positional parameter
      …
   }
   public string Topic {   // Topic is a named parameter
      get {...}
      set {...}
   }
   public string Url { get {…} }
}

defines an attribute class named HelpAttribute that has one positional parameter (string url) and one named argument (string Topic). The read-only Url property does not define a named parameter. It is non-static and public, but since it is read-only it does not define a named parameter.

The example

[HelpAttribute("http://www.mycompany.com/…/Class1.htm")]
class Class1 {
}
[HelpAttribute("http://www.mycompany.com/…/Misc.htm", Topic ="Class2")]
class Class2 {
}

shows several uses of the attribute.


17.1.3 Attribute parameter types

The types of positional and named parameters for an attribute class are limited to the attribute parameter types. A type is an attribute type if it is one of the following:

  • One of the following types: bool, byte, char, double, float, int, long, short, string.
  • The type object.
  • The type System.Type.
  • An enum type provided that it has public accessibility and that the types in which it is nested (if any) also have public accessibility.

An attribute class that defines a positional or named parameter whose type is not an attribute parameter type is in error. The example

public class InvalidAttribute: System.Attribute
{
   public InvalidAttribute(Class1 c) {…}         // error
}
public class Class1 {
   ...
}

is in error because it defines an attribute class with a positional parameter of type Class1, which is not an attribute parameter type.


17.2 Attribute specification

An attribute is a piece of additional declarative information that is specified for a declaration. Attributes can be specified for type-declarations, class-member-declarations, interface-member-declarations, enum-member-declarations, property-accessor-declarations and formal-parameter declarations.

Attributes are specified in attribute sections. Each attribute section is surrounded in square brackets, with multiple attributes specified in a comma-separated lists. The order in which attributes are specified, and the manner in which they are arranged in sections is not significant. The attribute specifications [A][B], [B][A], [A, B], and [B, A] are equivalent.

attributes:
attribute-sections
attribute-sections:
attribute-section
attribute-sections attribute-section
attribute-section:
[ attribute-list ]
[ attribute-list ,]
attribute-list:
attribute
attribute-list , attribute
attribute:
attribute-name attribute-argumentsopt
attribute-name:
reserved-attribute-name
type-name
attribute-arguments:
( positional-argument-list )
( positional-argument-list , named-argument-list )
( named-argument-list )
positional-argument-list:
positional-argument
positional-argument-list , positional-argument
positional-argument:
attribute-argument-expression
named-argument-list:
named-argument
named-argument-list , named-argument
named-argument:
identifier = attribute-argument-expression
attribute-argument-expression:
expression

An attribute consists of an attribute-name and an optional list of positional and named arguments. The positional arguments (if any) precede the named arguments. A positional argument consists of an attribute-argument-expression; a named argument consists of a name, followed by an equal sign, followed by an attribute-argument-expression.

The attribute-name identifies either a reserved attribute or an attribute class. If the form of attribute-name is type-name then this name must refer to an attribute class. Otherwise, a compile-time error occurs. The example

class Class1 {}
[Class1] class Class2 {}   // Error

is in error because it attempts to use Class1, which is not an attribute class, as an attribute class.

It is an error to use a single-use attribute class more than once on the same entity. The example

[AttributeUsage(AttributeTargets.Class)]
public class HelpStringAttribute: System.Attribute
{
   string value;
   public HelpStringAttribute(string value) {
      this.value = value;
   }
   public string Value { get {…} }
}
[HelpString("Description of Class1")]
[HelpString("Another description of Class1")]
public class Class1 {}

is in error because it attempts to use HelpString, which is a single-use attribute class, more than once on the declaration of Class1.

An expression E is an attribute-argument-expression if all of the following statements are true:

  • The type of E is an attribute parameter type (§17.1.3).
  • At compile-time, the value of E can be resolved to one of the following:
    • A constant value.
    • A System.Type object.
    • A one-dimensional array of attribute-argument-expressions.


17.3 Attribute instances

An attribute instance is an instance that represents an attribute at run-time. An attribute is defined with an attribute, positional arguments, and named arguments. An attribute instance is an instance of the attribute class that is initialized with the positional and named arguments.

Retrieval of an attribute instance involves both compile-time and run-time processing, as described in the following sections.


17.3.1 Compilation of an attribute

The compilation of an attribute with attribute class T, positional-argument-list P and named-argument-list N, consists of the following steps:

  • Follow the compile-time processing steps for compiling an object-creation-expression of the form new T(P). These steps either result in a compile-time error, or determine a constructor on T that can be invoked at run-time. Call this constructor C.
  • If the constructor determined in the step above does not have public accessibility, then a compile-time error occurs.
  • For each named-argument Arg in N:
    • Let Name be the identifier of the named-argument Arg.
    • Name must identify a non-static read-write public field or property on T. If T has no such field or property, then a compile-time error occurs.
  • Keep the following information for run-time instantiation of the attribute instance: the attribute class T, the constructor C on T, the positional-argument-list P and the named-argument-list N.


17.3.2 Run-time retrieval of an attribute instance

Compilation of an attribute yields an attribute class T, constructor C on T, positional-argument-list P and named-argument-list N. Given this information, an attribute instance can be retrieved at run-time using the following steps:

  • Follow the run-time processing steps for executing an object-creation-expression of the form T(P), using the constructor C as determined at compile-time. These steps either result in an exception, or produce an instance of T. Call this instance O.
  • For each named-argument Arg in N, in order:
    • Let Name be the identifier of the named-argument Arg. If Name does not identify a non-static public read-write field or property on O, then an exception (TODO: which exception?) is thrown.
    • Let Value be the result of evaluating the attribute-argument-expression of Arg.
    • If Name identifies a field on O, then set this field to the value Value.
    • Otherwise, Name identifies a property on O. Set this property to the value Value.
    • The result is O, an instance of the attribute class T that has been initialized with the positional-argument-list P and the named-argument-list N.


17.4 Reserved attributes

A small number of attributes affect the language in some way. These attributes include:

  • System.AttributeUsageAttribute, which is used to describe the ways in which an attribute class can be used.
  • System.ConditionalAttribute, which is used to define conditional methods.
  • System.ObsoleteAttribute, which is used to mark a member as obsolete.


17.4.1 The AttributeUsage attribute

The AttributeUsage attribute is used to describe the manner in which the attribute class can be used.

A class that is decorated with the AttributeUsage attribute must derive from System.Attribute, either directly or indirectly. Otherwise, a compile-time error occurs.

[AttributeUsage(AttributeTargets.Class)]
public class AttributeUsageAttribute: System.Attribute
{
   public AttributeUsageAttribute(AttributeTargets validOn) {…}
   public AttributeUsageAttribute(AttributeTargets validOn,
                                    bool allowMultiple,
                                    bool inherited) {…}
   public bool AllowMultiple { virtual get {…} virtual set {…} }
   public bool Inherited { virtual get {…} virtual set {…} }
   public AttributeTargets ValidOn { virtual get {…} }
}
public enum AttributeTargets
{
   Assembly    = 0x0001,
   Module       = 0x0002,
   Class       = 0x0004,
   Struct       = 0x0008,
   Enum          = 0x0010,
   Constructor = 0x0020,
   Method       = 0x0040,
   Property    = 0x0080,
   Field       = 0x0100,
   Event       = 0x0200,
   Interface    = 0x0400,
   Parameter    = 0x0800,
   Delegate    = 0x1000,
   All = Assembly | Module | Class | Struct | Enum | Constructor |
         Method | Property | Field | Event | Interface | Parameter |
         Delegate,
   ClassMembers  =  Class | Struct | Enum | Constructor | Method |
                  Property | Field | Event | Delegate | Interface,
}


17.4.2 The Conditional attribute

The Conditional attribute enables the definition of conditional methods. The Conditional attribute indicates a condition in the form of a pre-processing identifier. Calls to a conditional method are either included or omitted depending on whether this symbol is defined at the point of the call. If the symbol is defined, then the method call is included if the symbol is undefined, then the call is omitted.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class ConditionalAttribute: System.Attribute
{
   public ConditionalAttribute(string conditionalSymbol) {…}
   public string ConditionalSymbol { get {…} }
}

A conditional method is subject to the following restrictions:

  • The conditional method must be a method in a class-declaration. A compile-time error occurs if the Conditional attribute is specified on an interface method.
  • The conditional method must return have a return type of void.
  • The conditional method must not be marked with the override modifier. A conditional method may be marked with the virtual modifier. Overrides of such a method are implicitly conditional, and must not be explicitly marked with a Conditional attribute.
  • The conditional method must not be an implementation of an interface method. Otherwise, a compile-time error occurs.

Also, a compile-time error occurs if a conditional method is used in a delegate-creation-expression. The example

#define DEBUG
class Class1
{
   [Conditional("DEBUG")]
   public static void M() {
      Console.WriteLine("Executed Class1.M");
   }
}
class Class2
{
   public static void Test() {
      Class1.M();
   }
}

declares Class1.M as a conditional method. Class2's Test method calls this method. Since the pre-processing symbol DEBUG is defined, if Class2.Test is called, it will call M. If the symbol DEBUG had not been defined, then Class2.Test would not call Class1.M.

It is important to note that the inclusion or exclusion of a call to a conditional method is controlled by the pre-processing identifiers at the point of the call. In the example

// Begin class1.cs
   class Class1
   {
      [Conditional("DEBUG")]
      public static void F() {
         Console.WriteLine("Executed Class1.F");
      }
   }
// End class1.cs

// Begin class2.cs
   #define DEBUG
   class Class2
   {
      public static void G {
         Class1.F();            // F is called
      }
   }
// End class2.cs

// Begin class3.cs
   #undef DEBUG
   class Class3
   {
      public static void H {
         Class1.F();            // F is not called
      }
   }
// End class3.cs

the classes Class2 and Class3 each contain calls to the conditional method Class1.F, which is conditional based on the presence or absence of DEBUG. Since this symbol is defined in the context of Class2 but not Class3, the call to F in Class2 is actually made, while the call to F in Class3 is omitted.

The use of conditional methods in an inheritance chain can be confusing. Calls made to a conditional method through base, of the form base.M, are subject to the normal conditional method call rules. In the example

class Class1
{
   [Conditional("DEBUG")]
   public virtual void M() {
      Console.WriteLine("Class1.M executed");
   }
}
class Class2: Class1
{
   public override void M() {
      Console.WriteLine("Class2.M executed");
      base.M();                  // base.M is not called!
   }
}
#define DEBUG
class Class3
{
   public static void Test() {
      Class2 c = new Class2();
      c.M();                     // M is called
   }
}

Class2 includes a call the M defined in its base class. This call is omitted because the base method is conditional based on the presence of the symbol DEBUG, which is undefined. Thus, the method writes to the console only "Class2.M executed". Judicious use of pp-declarations can eliminate such problems.


17.4.3 The Obsolete attribute

The Obsolete attribute is used to mark program elements that should no longer be used.

[AttributeUsage(AttributeTargets.All)]
public class ObsoleteAttribute: System.Attribute
{
   public ObsoleteAttribute(string message) {…}
   public string Message { get {…} }
   public bool IsError{ get {…} set {…} }
}


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

18. Versioning

© 2000 Microsoft Corporation. All rights reserved.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

19. Unsafe code

© 2000 Microsoft Corporation. All rights reserved.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

19.1 Unsafe code

© 2000 Microsoft Corporation. All rights reserved.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

19.2 Pointer types

pointer-type:
unmanaged-type *
void *
unmanaged-type:
value-type


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

20. Interoperability

© 2000 Microsoft Corporation. All rights reserved.


20.1 Attributes

The attributes described in this chapter are used for creating NGWS programs that interoperate with COM programs.


20.1.1 The COMImport attribute

When placed on a class, the COMImport attribute marks the class as an externally implemented COM class. Such a class declaration enables the use of a C# name to refer to a COM class.

[AttributeUsage(AttributeTargets.Class)]
public class COMImportAttribute: System.Attribute
{
   public COMImportAttribute() {…}
}

A class that is decorated with the COMImport attribute is subject to the following restrictions:

  • It must also be decorated with the Guid attribute, which specifies the CLSID for the COM class being imported. A compile-time error occurs if a class declaration includes the COMImport attribute but fails to include the Guid attribute.
  • It must not have any members. (A public constructor with no parameters is automatically provided.)
  • It must not derive from a class other than object.

The example

[COMImport, Guid("00020810-0000-0000-C000-000000000046")]
class Worksheet {}
class Test
{
   static void Main() {
      Worksheet w = new Worksheet();   // Creates an Excel worksheet
   }
}

declares a class Worksheet as a class imported from COM that has a CLSID of "00020810-0000-0000-C000-000000000046". Instantiating a Worksheet instance causes a corresponding COM instantiation.


20.1.2 The COMSourceInterfaces attribute

The COMSourceInterfaces attribute is used to list the source interfaces on the imported coclass.

[AttributeUsage(AttributeTargets.Class)]
public class ComSourceInterfacesAttribute: System.Attribute
{
   public ComSourceInterfacesAttribute(string value) {…}
   public string Value { get {…} }
}


20.1.3 The COMVisibility attribute

The COMVisibility attribute is used to specify whether or not a class or interface is visible in COM.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class COMVisibilityAttribute: System.Attribute
{
   public COMVisibilityAttribute(System.Interop.ComVisibility value) {…}
   public ComVisibilityAttribute Value { get {…} }
}


20.1.4 The DispId attribute

The DispId attribute is used to specify an OLE Automation DISPID. (A DISPID is an integral value that identifies a member in a dispinterface.)

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
public class DispIdAttribute: System.Attribute
{
   public DispIdAttribute(int value) {…}
   public int Value { get {…} }
}


20.1.5 The DllImport attribute

The DllImport attribute is used to specify the dll location that contains the implementation of an extern method.

[AttributeUsage(AttributeTargets.Method)]
public class DllImportAttribute: System.Attribute
{
   public DllImportAttribute(string dllName) {…}
   public CallingConvention CallingConvention;
   public CharSet CharSet;
   public string DllName { get {…} }
   public string EntryPoint;
   public bool ExactSpelling;
   public bool SetLastError;
}

Specifically, the DllImport attribute has the following behaviors:

  • It can only be placed on method declarations.
  • It has a single positional parameter: a dllName parameter that specifies name of the dll in which the imported method can be found.
  • It has four named parameters:
    • The CallingConvention parameter indicates the calling convention for the entry point. If no CallingConvention is specified, a default of CallingConvention.WinAPI is used.
    • The CharSet parameter indicates the character set used in the entry point. If no CharSet is specified, a default of CharSet.Auto is used.
    • The EntryPoint parameter gives the name of the entry point in the dll. If no EntryPoint is specified, then the name of the method itself is used.
    • The ExactSpelling parameter indicates whether EntryPoint must exactly match the spelling of the indicated entry point. If no ExactSpelling is specified, a default of false is used.
    • The SetLastError parameter indicates whether the method preserves the Win32 "last error". If no SetLastError is specified, a default of false is used.
  • It is a single-use attribute class.

In addition, a method that is decorated with the DllImport attribute must have the extern modifier.


20.1.6 The GlobalObject attribute

The presence of the GlobalObject attribute specifies that a class is a "global" or "appobject" class in COM.

[AttributeUsage(AttributeTargets.Class)]
public class GlobalObjectAttribute: System.Attribute
{
   public GlobalObjectAttribute() {…}
}


20.1.7 The Guid attribute

The Guid attribute is used to specify a globally unique identifier (GUID) for a class or an interface. This information is primarily useful for interoperability between the NGWS runtime and COM.

[AttributeUsage(AttributeTargets.Class
               | AttributeTargets.Interface
               | AttributeTargets.Enum
               | AttributeTargets.Delegate
               | AttributeTargets.Struct)]
public class GuidAttribute: System.Attribute
{
   public GuidAttribute(string uuid) {…}
   public Guid Value { get {…} }
}

The format of the positional string argument is verified at compile-time. It is an error to specify a string argument that is not a syntactically valid GUID.


20.1.8 The HasDefaultInterface attribute

If present, the HasDefaultInterface attribute indicates that a class has a default interface.

[AttributeUsage(AttributeTargets.Class)]
public class HasDefaultInterfaceAttribute: System.Attribute
{
   public HasDefaultInterfaceAttribute() {…}
}


20.1.9 The ImportedFromCOM attribute

The ImportedFromCOM attribute is used to specify that a module was imported from a COM type library.

[AttributeUsage(AttributeTargets.Module)]
public class ImportedFromCOMAttribute: System.Attribute
{
   public ImportedFromCOMAttribute(string value) {…}
   public string Value { get {..} }
}


20.1.10 The In and Out attributes

The In and Out attributes are used to provide custom marshalling information for parameters. All combinations of these marshalling attributes are permitted.

[AttributeUsage(AttributeTargets.Parameter)]
public class InAttribute: System.Attribute
{
   public InAttribute() {…}
}
[AttributeUsage(AttributeTargets.Parameter)]
public class OutAttribute: System.Attribute
{
   public OutAttribute() {…}
}

If a parameter is not decorated with either marshalling attribute, then it is marshalled based on the its parameter-modifiers, as follows. If the parameter has no modifiers then the marshalling is [In]. If the parameter has the ref modifier then the marshalling is [In, Out]. If the parameter has the out modifier then the marshalling is [Out].

Note that out is a keyword, and Out is an attribute. The example

class Class1
{
   void M([Out] out int i) {
      …
   }
}

shows that the use of out as a parameter-modifier and the use of Out in an attribute.


20.1.11 The InterfaceType attribute

When placed on an interface, the InterfaceType attribute specifies the manner in which the interface is treated in COM.

[AttributeUsage(AttributeTargets.Interface)]
public class InterfaceTypeAttribute: System.Attribute
{
   public InterfaceTypeAttribute(System.Interop.ComInterfaceType value)
   {…}
   public System.Interop.ComInterfaceType Value { get {…} }
}


20.1.12 The IsCOMRegisterFunction attribute

The presence of the IsCOMRegisterFunction attribute on a method indicates that the method should be called during the COM registration process.

[AttributeUsage(AttributeTargets.Method)]
public class IsCOMRegisterFunctionAttribute: System.Attribute
{
   public IsComRegisterFunctionAttribute() {…}
}


20.1.13 The Marshal attribute

The Marshal attribute is used to describe the marshalling format for a field, method, or parameter.

[AttributeUsage(AttributeTargets.Method |
            AttributeTargets.Parameter |
            AttributeTargets.Field)]
public class MarshalAttribute: System.Attribute
{
   public MarshalAttribute(UnmanagedType type) {…}
   public string Cookie;
   public Guid IID;
   public Type Marshaler;
   public UnmanagedType NativeType { get {…} }
   public int Size;
   public UnmanagedType SubType;
}

The Marshal attribute has the following behaviors:

  • It can only be placed on field declarations, method declarations, and formal parameters.
  • It has a single positional parameter of type UnmanagedType.
  • It has five named parameters.
    • The Cookie parameter gives a cookie that should be passed to the marshaler.
    • The IID parameter gives the Guid for NativeType.Interface types.
    • The Marshaler parameter specifies a marshaling class.
    • The Size parameter describes the size of a fixed size array or string. (Issue: what value is returned for other types?)
    • The SubType parameter describes the subsidiary type for NativeType.Ptr and NativeType.FixedArray types.
    • It is a single-use attribute class.


20.1.14 The Name attribute

The Name attribute is used to specify the property name that underlies an indexer in NGWS. If no Name attribute is specified, then the property is named Item.

[AttributeUsage(AttributeTargets.Indexer)]
public class NameAttribute: System.Attribute
{
   public NameAttribute(string value) {…}
   public string Value { get {…} }
}

The identifier must be a legal C# identifier. Otherwise, a compile-time error occurs.


20.1.15 The NoIDispatch attribute

The presence of the NoIDispatch attribute indicates that the class or interface should derive from IUnknown rather than IDispatch when exported to COM.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class NoIDispatchAttribute: System.Attribute
{
   public NoIDispatchAttribute() {…}
}


20.1.16 The NonSerialized attribute

The presence of the NonSerialized attribute on a field or property indicates that that field or property should not be serialized.

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class NonSerializedAttribute: System.Attribute
{
   public NonSerializedAttribute() {…}
}


20.1.17 The Predeclared attribute

The presence of the Predeclared attribute denotes a predeclared object imported from COM.

[AttributeUsage(Attribute(AttributeTargets.Class)]
public class PredeclaredAttribute: System.Attribute
{
   public PredeclaredAttribute() {…}
}


20.1.18 The ReturnsHResult attribute

The ReturnsHResult attribute is used to mark a method as returning an HRESULT result in COM .

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
public class ReturnsHResultAttribute: System.Attribute
{
   public ReturnsHResultAttribute(bool value) {…}
   public bool Value { get {…} }
}

A method that is decorated with the ReturnsHResult attribute must not have a body. Thus, the ReturnsHResult attribute may be placed on an interface method or on an extern class methods that have the extern modifier. A compile-time error occurs if any other method declaration includes the ReturnsHResult attribute.

The example

class interface Interface1
{
   [ReturnsHResult]
   int M(int x, int y);
}

declares that the M method of Interface1 returns an HRESULT. The corresponding COM signature for M is a method that takes three arguments (the two int arguments x and y plus a third argument of type int* that is used for the return value) and returns an HRESULT.


20.1.19 The Serializable attribute

The presence of the Serializable attribute on a class indicates that the class can be serialized..

[AttributeUsage(AttributeTargets.Class
                  | AttributeTargets.Delegate
                  | AttributeTargets.Enum
                  | AttributeTargets.Struct)]
public class SerializableAttribute: System.Attribute
{
   public SerializableAttribute() {…}
}


20.1.20 The StructLayout attribute

The StructLayout attribute is used to specify the layout of fields for the struct.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class StructLayoutAttribute: System.Attribute
{
    public StructLayoutAttribute(LayoutKind kind) {…}
    public CharSet CharSet;
    public int Pack;
    public LayoutKind StructLayoutKind { get {…} }
}

The StructLayout attribute has the following behaviors:

  • It can only be placed struct declarations.
  • It has a positional parameter of type Layout.
  • It has three named parameters:
    • The CharSet named parameter indicates the default character set for containing char and string types. The default is CharSet.Auto.
    • The Pack named parameter indicates the packing size, in bytes. The packing size must be a power of two. The default packing size is 4.
  • It is a single-use attribute class.

If LayoutKind.Explicit is specified, then every field in the struct must have the StructOffset attribute. If LayoutKind.Explicit is not specified, then use of the StructOffset attribute is prohibited.


20.1.21 The StructOffset attribute

The StructOffset attribute is used to specify the layout of fields for the struct.

[AttributeUsage(AttributeTargets.Field)]
public class StructOffsetAttribute: System.Attribute
{
   public StructOffsetAttribute(int offset) {…}
}

The StructOffset attribute may not be placed on a field declarations that is a member of a class.


20.1.22 The TypeLibFunc attribute

The TypeLibFunc attribute is used to specify typelib flags, for interoperability with COM.

[AttributeUsage(AttributeTargets.Method)]
public class TypeLibFuncAttribute: System.Attribute
{
   public TypeLibFuncAttribute(short value) {…}
   public short Value { get {…} }
}


20.1.23 The TypeLibType attribute

The TypeLibType attribute is used to specify typelib flags, for interoperability with COM.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class TypeLibTypeAttribute: System.Attribute
{
   public TypeLibTypeAttribute(short value) {…}
   public short Value { get {…} }
}


20.1.24 The TypeLibVar attribute

The TypeLibVar attribute is used to specify typelib flags, for interoperability with COM.

[AttributeUsage(AttributeTargets.Field)]
public class TypeLibVarAttribute: System.Attribute
{
   public TypeLibVarAttribute(short value) {…}
   public short Value { get {…} }
}


20.2 Supporting enums

namespace System.Interop {
   public enum CallingConvention
   {
      WinAPI = 1,
      Cdecl = 2,
      Stdcall = 3,
      Thiscall = 4,
      Fastcall = 5
   }
   public enum CharSet
   {
      None
      Auto,
      Ansi,
      Unicode
   }
   public enum ComInterfaceType
   {
      Dual = 0,
      IUnknown = 1,
      IDispatch = 2,
   }
   public enum COMVisibility
   {
      VisibilityDefault   = 0,
      VisibilityOmitted   = 1,
   }
   public enum LayoutKind
   {
       Sequential,
       Union,
       Explicit,
   }
   public enum UnmanagedType
   {
      Bool         = 0x2,
      I1            = 0x3,
      U1            = 0x4,
      I2            = 0x5,
      U2            = 0x6,
      I4            = 0x7,
      U4            = 0x8,
      I8            = 0x9,
      U8            = 0xa,
      R4            = 0xb,
      R8            = 0xc,
      BStr         = 0x13,
      LPStr         = 0x14,
      LPWStr      = 0x15,
      LPTStr      = 0x16,
      ByValTStr   = 0x17,
      Struct      = 0x1b,
      Interface   = 0x1c,
      SafeArray   = 0x1d,
      ByValArray   = 0x1e,
      SysInt      = 0x1f,
      SysUInt      = 0x20,
      VBByRefStr   = 0x22,
      AnsiBStr      = 0x23,
      TBStr         = 0x24,
      VariantBool   = 0x25,
      FunctionPtr   = 0x26,
      LPVoid      = 0x27,
      AsAny         = 0x28,
      RPrecise      = 0x29,
      LPArray      = 0x2a,
      LPStruct      = 0x2b,
      CustomMarshaller = 0x2c,
   }
}


21. References

Unicode Consortium. The Unicode Standard, Version 3.0. Addison-Wesley, Reading, Massachusetts, 2000, ISBN 0-201-616335-5.

IEEEE. IEEE Standard for Binary Floating-Point Arithmetic. ANSI/IEEE Standard 754-1985. Available from http://www.ieee.org.

ISO/IEC. C++. ANSI/ISO/IEC 14882:1998.


22. C# Grammar Summary

This section summarizes the C# language grammar. For information on how to read the grammar, see §2.2.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

22.1 Introduction Grammar

This section of the Language Reference does not have any grammar.


22.2 Lexical structure Grammar

2.2 Grammar notation

nonsense:
terminal1
terminal2
letter: one of
A B C a b c
letter: one of
A
B
C
a
b
c
whole:
first-part second-partopt last-part
whole:
first-part last-part
first-part second-part last-part

2.3 Pre-processing

pp-unit:
pp-groupopt
pp-group:
pp-group-part
pp-group pp-group-part
pp-group-part:
pp-tokensopt new-line
pp-declaration
pp-if-section
pp-control-line
pp-line-number
pp-tokens:
pp-token
pp-tokens pp-token
pp-token:
identifier
keyword
literal
operator-or-punctuator
new-line:
The carriage return character (U+000D)
The line feed character (U+000A)
The carriage return character followed by a line feed character
The line separator character (U+2028)
The paragraph separator character (U+2029)

2.3.1 Pre-processing declarations

pp-declaration:
#define pp-identifier
#undef pp-identifier

2.3.2 #if, #elif, #else, #endif

pp-if-section:
pp-if-group pp-elif-groupsopt pp-else-groupopt pp-endif-line
pp-if-group:
#if pp-expression new-line pp-groupopt
pp-elif-groups
pp-elif-group
pp-elif-groups pp-elif-group
pp-elif-group:
#elif pp-expression new-line groupopt
pp-else-group:
#else new-line groupopt
pp-endif-line
#endif new-line

2.3.3 Pre-processing control lines

pp-control-line:
#error pp-message
#warning pp-message
pp-message:
pp-tokensopt

2.3.4 #line

pp-line-number:
#line integer-literal
#line integer-literal string-literal
pp-integer-literal:
decimal-digit
decimal-digits decimal-digit
pp-string-literal:
" pp-string-literal-characters "
pp-string-literal-characters:
pp-string-literal-character
pp-string-literal-characters pp-string-literal-character

2.3.5 pp-string-literal-character:
Any character except " (U+0022), and white-spacePre-processing identifiers

pp-identifier:
pp-available-identifier
pp-available-identifier:
A pp-identifier-or-keyword that is not true or false
pp-identifier-or-keyword:
identifier-start-character identifier-part-charactersopt

2.3.6 Pre-processing expressions

pp-expression:
pp-equality-expression
pp-primary-expression:
true
false
pp-identifier
( pp-expression )
pp-unary-expression:
pp-primary-expression
! pp-unary-expression
pp-equality-expression:
pp-equality-expression == pp-logical-and-expression
pp-equality-expression != pp-logical-and-expression
pp-logical-and-expression:
pp-unary-expression
pp-logical-and-expression && pp-unary-expression
pp-logical-or-expression:
pp-logical-and-expression
pp-logical-or-expression || pp-logical-and-expression

2.4.1 Input

input:
input-elementsopt
input-elements:
input-element
input-elements input-element
input-element:
comment
white-space
token

2.4.2 Input characters

input-character:
any Unicode character

2.4.3 Line terminators

line-terminator:
TBD

2.4.4 Comments

comment:
TBD

2.4.5 White space

white-space:
new-line
The tab character (U+0009)
The vertical tab character (U+000B)
The form feed character (U+000C)
The "control-Z" or "substitute" character (U+001A)
All characters with Unicode class "Zs"

2.4.6 Tokens

token:
identifier
keyword
literal
operator-or-punctuator

2.5 Processing of Unicode character escape sequences

unicode-character-escape-sequence:
\u hex-digit hex-digit hex-digit hex-digit

2.5.1 Identifiers

identifier:
available-identifier
@ identifier-or-keyword
available-identifier:
An identifier-or-keyword that is not a keyword
identifier-or-keyword:
identifier-start-character identifier-part-charactersopt
identifier-start-character:
letter-character
underscore-character
identifier-part-characters:
identifier-part-character
identifier-part-characters identifier-part-character
identifier-part-character:
letter-character
combining-character
decimal-digit-character
underscore-character
letter-character:
A Unicode character of classes Lu, Ll, Lt, Lm, Lo, or Nl
A unicode-character-escape-sequence representing a character of classes Lu, Ll, Lt, Lm, Lo, or Nl
combining-character:
A Unicode character of classes Mn or Mc
A unicode-character-escape-sequence representing a character of classes Mn or Mcdecimal-digit-character:
A Unicode character of the class Nd
A unicode-character-escape-sequence representing a character of the class Nd
underscore-character:
A Unicode character of the class Pc
A unicode-character-escape-sequence representing a character of the class Pc

2.5.2 Keywords

keyword: one of
abstract base bool break byte
case catch char checked class
const continue decimal default delegate
do double else enum event
explicit extern false finally fixed
float for foreach goto if
implicit in int interface internal
is lock long namespace new
null object operator out override
params private protected public readonly
ref return sbyte sealed short
sizeof static string struct switch
this throw true try typeof
uint ulong unchecked unsafe ushort
using virtual void while

2.5.3 Literals

literal:
boolean-literal
integer-literal
real-literal
character-literal
string-literal
null-literal

2.5.3.1 Boolean literals

boolean-literal:
true
false

2.5.3.2 Integer literals

integer-literal:
decimal-integer-literal
hexadecimal-integer-literal
decimal-integer-literal:
decimal-digits integer-type-suffixopt
decimal-digits:
decimal-digit
decimal-digits decimal-digit
decimal-digit: one of
0 1 2 3 4 5 6 7 8 9
integer-type-suffix: one of
U u L l UL Ul uL ul LU Lu lU lu
hexadecimal-integer-literal:
0x hex-digits integer-type-suffixopt
hex-digits:
hex-digit
hex-digits hex-digit
hex-digit: one of
0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f

2.5.3.3 Real literals

real-literal:
decimal-digits . decimal-digits exponent-partopt real-type-suffixopt
. decimal-digits exponent-partopt real-type-suffixopt
decimal-digits exponent-part real-type-suffixopt
decimal-digits real-type-suffix
exponent-part:
e signopt decimal-digits
E signopt decimal-digits
sign: one of
+ -
real-type-suffix: one of
F f D d M m

2.5.3.4 Character literals

character-literal:
' character '
character:
single-character
simple-escape-sequence
hexadecimal-escape-sequence
unicode-character-escape-sequence
single-character:
Any character except ' (U+0027), \ (U+005C), and white-space other than space (U+0020)
simple-escape-sequence: one of
\' \" \\ \0 \a \b \f \n \r \t \v
hexadecimal-escape-sequence:
\x hex-digit hex-digitopt hex-digitopt hex-digitopt

2.5.3.5 String literals

string-literal:
regular-string-literal
verbatim-string-literal
regular-string-literal:
" regular-string-literal-charactersopt "
regular-string-literal-characters:
regular-string-literal-character
regular-string-literal-characters regular-string-literal-character
regular-string-literal-character:
single-regular-string-literal-character
simple-escape-sequence
hexadecimal-escape-sequence
unicode-character-escape-sequence
single-regular-string-literal-character:
Any character except " (U+0022), \ (U+005C), and white-space other than space (U+0020)
verbatim-string-literal:
@" verbatim -string-literal-charactersopt "
verbatim-string-literal-characters:
verbatim-string-literal-character
verbatim-string-literal-characters verbatim-string-literal-character
verbatim-string-literal-character:
single-verbatim-string-literal-character
quote-escape-sequence
single-verbatim-string-literal-character:
any character except "
quote-escape-sequence:
""

2.5.3.6 The null literal

null-literal:
null

2.5.4 Operators and punctuators

operator-or-punctuator: one of
{ } [ ] ( ) . , : ;
+ - * / % & | ^ ! ~
= < > ? ++ -- && || << >>
== != <= >= += -= *= /= %= &=
|= ^= <<= >>= ->


22.3 Basic concepts Grammar

3.6 Namespace and type names

namespace-name:
namespace-or-type-name
type-name:
namespace-or-type-name
namespace-or-type-name:
identifier
namespace-or-type-name . identifier


22.4 Types Grammar

4.1 Value types

value-type:
struct-type
enum-type
struct-type:
type-name
simple-type
simple-type:
numeric-type
bool
numeric-type:
integral-type
floating-point-type
decimal
integral-type:
sbyte
byte
short
ushort
int
uint
long
ulong
char
floating-point-type:
float
double
enum-type:
type-name

4.2 Reference types

reference-type:
class-type
interface-type
array-type
delegate-type
class-type:
type-name
object
string
interface-type:
type-name
array-type:
non-array-type rank-specifiers
non-array-type:
type
rank-specifiers:
rank-specifier
rank-specifiers rank-specifier
rank-specifier:
[ dim-separatorsopt ]
dim-separators:
,
dim-separators ,
delegate-type:
type-name


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

22.5 Variables Grammar

5.4 Variable references

variable-reference:
expression


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

22.6 Conversions Grammar

This section of the Language Reference does not have any grammar.


22.7 Expressions Grammar

7.4.1 Argument lists

argument-list:
argument
argument-list , argument
argument:
expression
ref variable-reference
out variable-reference

7.5 Primary expressions

primary-expression:
literal
simple-name
parenthesized-expression
member-access
invocation-expression
element-access
this-access
base-access
post-increment-expression
post-decrement-expression
new-expression
typeof-expression
sizeof-expression
checked-expression
unchecked-expression

7.5.2 Simple names

simple-name:
identifier

7.5.3 Parenthesized expressions

parenthesized-expression:
( expression )

7.5.4 Member access

member-access:
primary-expression . identifier
predefined-type . identifier
predefined-type: one of
bool byte char decimal double float int long
object sbyte short string uint ulong ushort

7.5.5 Invocation expressions

invocation-expression:
primary-expression ( argument-listopt )

7.5.6 Element access

element-access:
primary-expression [ expression-list ]
expression-list:
expression
expression-list , expression

7.5.7 This access

this-access:
this

7.5.8 Base access

base-access:
base . identifier
base [ expression-list ]

7.5.9 Postfix increment and decrement operators

post-increment-expression:
primary-expression ++
post-decrement-expression:
primary-expression --

7.5.10 new operator

new-expression:
object-creation-expression
array-creation-expression
delegate-creation-expression

7.5.10.1 Object creation expressions

object-creation-expression:
new type ( argument-listopt )

7.5.10.2 Array creation expressions

array-creation-expression:
new non-array-type [ expression-list ] rank-specifiersopt array-initializeropt
new array-type array-initializer

7.5.10.3 Delegate creation expressions

delegate-creation-expression:
new delegate-type ( expression )

7.5.11 typeof operator

typeof-expression:
typeof ( type )

7.5.12 sizeof operator

sizeof-expression:
sizeof ( type )

7.5.13 checked and unchecked operators

checked-expression:
checked ( expression )
unchecked-expression:
unchecked ( expression )

7.6 Unary expressions

unary-expression:
primary-expression
+ unary-expression
- unary-expression
! unary-expression
~ unary-expression
* unary-expression
& unary-expression
pre-increment-expression
pre-decrement-expression
cast-expression

7.6.7 Prefix increment and decrement operators

pre-increment-expression:
++ unary-expression
pre-decrement-expression:
-- unary-expression

7.6.8 Cast expressions

cast-expression:
( type ) unary-expression

7.7 Arithmetic operators

multiplicative-expression:
unary-expression
multiplicative-expression * unary-expression
multiplicative-expression / unary-expression
multiplicative-expression % unary-expression
additive-expression:
multiplicative-expression
additive-expression + multiplicative-expression
additive-expression – multiplicative-expression

7.8 Shift operators

shift-expression:
additive-expression
shift-expression << additive-expression
shift-expression >> additive-expression

7.9 Relational operators

relational-expression:
shift-expression
relational-expression < shift-expression
relational-expression > shift-expression
relational-expression <= shift-expression
relational-expression >= shift-expression
relational-expression is reference-type
equality-expression:
relational-expression
equality-expression == relational-expression
equality-expression != relational-expression

7.10 Logical operators

and-expression:
equality-expression
and-expression & equality-expression
exclusive-or-expression:
and-expression
exclusive-or-expression ^ and-expression
inclusive-or-expression:
exclusive-or-expression
inclusive-or-expression | exclusive-or-expression

7.11 Conditional logical operators

conditional-and-expression:
inclusive-or-expression
conditional-and-expression && inclusive-or-expression
conditional-or-expression:
conditional-and-expression
conditional-or-expression || conditional-and-expression

7.12 Conditional operator

conditional-expression:
conditional-or-expression
conditional-or-expression ? expression : expression

7.13 Assignment operators

assignment:
unary-expression assignment-operator expression
assignment-operator: one of
= += -= *= /= %= &= |= ^= <<= >>=

7.14 Expression

expression:
conditional-expression
assignment

7.15 Constant expressions

constant-expression:
expression

7.16 Boolean expressions

boolean-expression:
expression


22.8 Statements Grammar

8.2 Blocks

block:
{ statement-listopt }

8.2.1 Statement lists

statement-list:
statement
statement-list statement

8.3 The empty statement

empty-statement:
;

8.4 Labeled statements

labeled-statement:
identifier : statement

8.5 Declaration statements

declaration-statement:
local-variable-declaration ;
local-constant-declaration ;

8.5.1 Local variable declarations

local-variable-declaration:
type variable-declarators
variable-declarators:
variable-declarator
variable-declarators , variable-declarator
variable-declarator:
identifier
identifier = variable-initializer
variable-initializer:
expression
array-initializer

8.5.2 Local constant declarations

local-constant-declaration:
const type constant-declarators
constant-declarators:
constant-declarator
constant-declarators , constant-declarator
constant-declarator:
identifier = constant-expression

8.6 Expression statements

expression-statement:
statement-expression ;
statement-expression:
invocation-expression
object-creation-expression
assignment
post-increment-expression
post-decrement-expression
pre-increment-expression
pre-decrement-expression

8.7 Selection statements

selection-statement:
if-statement
switch-statement

8.7.1 The if statement

if-statement:
if ( boolean-expression ) embedded-statement
if ( boolean-expression ) embedded-statement else embedded-statement
boolean-expression:
expression

8.7.2 The switch statement

switch-statement:
switch ( expression ) switch-block
switch-block:
{ switch-sectionsopt }
switch-sections:
switch-section
switch-sections switch-section
switch-section:
switch-labels statement-list
switch-labels:
switch-label
switch-labels switch-label
switch-label:
case constant-expression :
default :

8.8 Iteration statements

iteration-statement:
while-statement
do-statement
for-statement
foreach-statement

8.8.1 The while statement

while-statement:
while ( boolean-expression ) embedded-statement

8.8.2 The do statement

do-statement:
do embedded-statement while ( boolean-expression ) ;

8.8.3 The for statement

for-statement:
for ( for-initializeropt ; for-conditionopt ; for-iteratoropt ) embedded-statement
for-initializer:
local-variable-declaration
statement-expression-list
for-condition:
boolean-expression
for-iterator:
statement-expression-list
statement-expression-list:
statement-expression
statement-expression-list , statement-expression

8.8.4 The foreach statement

foreach-statement:
foreach ( type identifier in expression ) embedded-statement

8.9 Jump statements

jump-statement:
break-statement
continue-statement
goto-statement
return-statement
throw-statement

8.9.1 The break statement

break-statement:
break ;

8.9.2 The continue statement

continue-statement:
continue ;

8.9.3 The goto statement

goto-statement:
goto identifier ;
goto case constant-expression ;
goto default ;

8.9.4 The return statement

return-statement:
return expressionopt ;

8.9.5 The throw statement

throw-statement:
throw expressionopt ;

8.10 The try statement

try-statement:
try block catch-clauses
try block finally-clause
try block catch-clauses finally-clause
catch-clauses:
specific-catch-clauses general-catch-clauseopt
specific-catch-clausesopt general-catch-clause
specific-catch-clauses:
specific-catch-clause
specific-catch-clauses specific-catch-clause
specific-catch-clause:
catch ( class-type identifieropt ) block
general-catch-clause:
catch block
finally-clause:
finally block

8.11 The checked and unchecked statements

checked-statement:
checked block
unchecked-statement:
unchecked block

8.12 The lock statement

lock-statement:
lock ( expression ) embedded-statement


22.9 Namespaces Grammar

9.1 Compilation units

compilation-unit:
using-directivesopt namespace-member-declarationsopt

9.2 Namespace declarations

namespace-declaration:
namespace qualified-identifier namespace-body ;opt
qualified-identifier:
identifier
qualified-identifier . identifier
namespace-body:
{ using-directivesopt namespace-member-declarationsopt }

9.3 Using directives

using-directives:
using-directive
using-directives using-directive
using-directive:
using-alias-directive
using-namespace-directive

9.3.1 Using alias directives

using-alias-directive:
using identifier = namespace-or-type-name ;

9.3.2 Using namespace directives

using-namespace-directive:
using namespace-name ;

9.4 Namespace members

namespace-member-declarations:
namespace-member-declaration
namespace-member-declarations namespace-member-declaration
namespace-member-declaration:
namespace-declaration
type-declaration

9.5 Type declarations

type-declaration:
class-declaration
struct-declaration
interface-declaration
enum-declaration
delegate-declaration


22.10 Classes Grammar

10.1 Class declarations

class-declaration:
attributesopt class-modifiersopt class identifier class-baseopt class-body ;opt

10.1.1 Class modifiers

class-modifiers:
class-modifier
class-modifiers class-modifier
class-modifier:
new
public
protected
internal
private
abstract
sealed

10.1.2 Class base specification

class-base:
: class-type
: interface-type-list
: class-type , interface-type-list
interface-type-list:
interface-type
interface-type-list , interface-type

10.1.3 Class body

class-body:
{ class-member-declarationsopt }

10.2 Class members

class-member-declarations:
class-member-declaration
class-member-declarations class-member-declaration
class-member-declaration:
constant-declaration
field-declaration
method-declaration
property-declaration
event-declaration
indexer-declaration
operator-declaration
constructor-declaration
destructor-declaration
static-constructor-declaration
type-declaration

10.3 Constants

constant-declaration:
attributesopt constant-modifiersopt const type constant-declarators ;
constant-modifiers:
constant-modifier
constant-modifiers constant-modifier
constant-modifier:
new
public
protected
internal
private
constant-declarators:
constant-declarator
constant-declarators , constant-declarator
constant-declarator:
identifier = constant-expression

10.4 Fields

field-declaration:
attributesopt field-modifiersopt type variable-declarators ;
field-modifiers:
field-modifier
field-modifiers field-modifier
field-modifier:
new
public
protected
internal
private
static
readonly
variable-declarators:
variable-declarator
variable-declarators , variable-declarator
variable-declarator:
identifier
identifier = variable-initializer
variable-initializer:
expression
array-initializer

10.5 Methods

method-declaration:
method-header method-body
method-header:
attributesopt method-modifiersopt return-type member-name ( formal-parameter-listopt )
method-modifiers:
method-modifier
method-modifiers method-modifier
method-modifier:
new
public
protected
internal
private
static
virtual
override
abstract
extern
return-type:
type
void
member-name:
identifier
interface-type . identifier
method-body:
block
;

10.5.1 Method parameters

formal-parameter-list:
formal-parameter
formal-parameter-list , formal-parameter
formal-parameter:
attributesopt parameter-modifieropt type identifier
parameter-modifier:
ref
out
params

10.6 Properties

property-declaration:
attributesopt property-modifiersopt type member-name { accessor-declarations }
property-modifiers:
property-modifier
property-modifiers property-modifier
property-modifier:
new
public
protected
internal
private
static
member-name:
identifier
interface-type . identifier

10.6.2 Accessors

accessor-declarations:
get-accessor-declaration set-accessor-declarationopt
set-accessor-declaration get-accessor-declarationopt
get-accessor-declaration:
accessor-modifieropt get accessor-body
set-accessor-declaration:
accessor-modifieropt set accessor-body
accessor-modifier:
virtual
override
abstract
accessor-body:
block
;

10.7 Events

event-declaration:
event-field-declaration
event-property-declaration
event-field-declaration:
attributesopt event-modifiersopt event type variable-declarators ;
event-property-declaration:
attributesopt event-modifiersopt event type member-name { accessor-declarations }
event-modifiers:
event-modifier
event-modifiers event-modifier
event-modifier:
new
public
protected
internal
private
static

10.8 Indexers

indexer-declaration:
attributesopt indexer-modifiersopt indexer-declarator { accessor-declarations }
indexer-modifiers:
indexer-modifier
indexer-modifiers indexer-modifier
indexer-modifier:
new
public
protected
internal
private
indexer-declarator:
type this [ formal-index-parameter-list ]
type interface-type . this [ formal-index-parameter-list ]
formal-index-parameter-list:
formal-index-parameter
formal-index-parameter-list , formal-index-parameter
formal-index-parameter:
attributesopt type identifier

10.9 Operators

operator-declaration:
attributesopt operator-modifiers operator-declarator block
operator-modifiers:
public static
static public
operator-declarator:
unary-operator-declarator
binary-operator-declarator
conversion-operator-declarator
unary-operator-declarator:
type operator overloadable-unary-operator ( type identifier )
overloadable-unary-operator: one of
+ - ! ~ ++ -- true false
binary-operator-declarator:
type operator overloadable-binary-operator ( type identifier , type identifier )
overloadable-binary-operator: one of
+ - * / % & | ^ << >> == != > < >= <=
conversion-operator-declarator:
implicit operator type ( type identifier )
explicit operator type ( type identifier )

10.10 Instance constructors

constructor-declaration:
attributesopt constructor-modifiersopt constructor-declarator block
constructor-modifiers:
constructor-modifier
constructor-modifiers constructor-modifier
constructor-modifier:
public
protected
internal
private
constructor-declarator:
identifier ( formal-parameter-listopt ) constructor-initializeropt
constructor-initializer:
: base ( argument-listopt )
: this ( argument-listopt )

10.11 Destructors

destructor-declaration:
attributesopt ~ identifier ( ) block

10.12 Static constructors

static-constructor-declaration:
attributesopt static identifier ( ) block


22.11 Structs Grammar

11.1 Struct declarations

struct-declaration:
attributesopt struct-modifiersopt struct identifier struct-interfacesopt struct-body ;opt

11.1.1 Struct modifiers

struct-modifiers:
struct-modifier
struct-modifiers struct-modifier
struct-modifier:
new
public
protected
internal
private

11.1.2 Interfaces

struct-interfaces:
: interface-type-list

11.1.3 Struct body

struct-body:
{ struct-member-declarationsopt }

11.2 Struct members

struct-member-declarations:
struct-member-declaration
struct-member-declarations struct-member-declaration
struct-member-declaration:
class-member-declaration


22.12 Arrays Grammar

12.1 Array types

array-type:
non-array-type rank-specifiers
non-array-type:
type
rank-specifiers:
rank-specifier
rank-specifiers rank-specifier
rank-specifier:
[ dim-separatorsopt ]
dim-separators:
,
dim-separators ,

12.6 Array initializers

array-initializer:
{ variable-initializer-listopt }
{ variable-initializer-list , }
variable-initializer-list:
variable-initializer
variable-initializer-list , variable-initializer
variable-initializer:
expression
array-initializer


22.13 Interfaces Grammar

13.1 Interface declarations

interface-declaration:
attributesopt interface-modifiersopt interface identifier interface-baseopt interface-body ;opt

13.1.1 Interface modifiers

interface-modifiers:
interface-modifier
interface-modifiers interface-modifier
interface-modifier:
new
public
protected
internal
private

13.1.2 Base interfaces

interface-base:
: interface-type-list

13.1.3 Interface body

interface-body:
{ interface-member-declarationsopt }

13.2 Interface members

interface-member-declarations:
interface-member-declaration
interface-member-declarations interface-member-declaration
interface-member-declaration:
interface-method-declaration
interface-property-declaration
interface-event-declaration
interface-indexer-declaration

13.2.1 Interface methods

interface-method-declaration:
attributesopt newopt return-type identifier ( formal-parameter-listopt ) ;

13.2.2 Interface properties

interface-property-declaration:
attributesopt newopt type identifier { interface-accessors }
interface-accessors:
get ;
set ;
get ; set ;
set ; get ;

13.2.3 Interface events

interface-event-declaration:
attributesopt newopt event type identifier ;

13.2.4 Interface indexers

interface-indexer-declaration:
attributesopt newopt type this [ formal-index-parameter-list ] { interface-accessors }


22.14 Enums Grammar

14.1 Enum declarations

enum-declaration:
attributesopt enum-modifiersopt enum identifier enum-baseopt enum-body ;opt
enum-modifiers:
enum-modifier
enum-modifiers enum-modifier
enum-modifier:
new
public
protected
internal
private
enum-base:
: integral-type
enum-body:
{ enum-member-declarationsopt }
{ enum-member-declarations , }

14.2 Enum members

enum-member-declarations:
enum-member-declaration
enum-member-declarations , enum-member-declaration
enum-member-declaration:
attributesopt identifier
attributesopt identifier = constant-expression


22.15 Delegates Grammar

15.1 Delegate declarations

delegate-declaration:
attributesopt delegate-modifiersopt delegate result-type identifier ( formal-parameter-listopt ) ;

15.1.1 Delegate modifiers

delegate-modifiers:
delegate-modifier
delegate-modifiers delegate-modifier
delegate-modifier:
new
public
protected
internal
private


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

22.16 Exceptions Grammar

© 2000 Microsoft Corporation. All rights reserved.


22.17 Attributes Grammar

17.2 Attribute specification

attributes:
attribute-sections
attribute-sections:
attribute-section
attribute-sections attribute-section
attribute-section:
[ attribute-list ]
[ attribute-list ,]
attribute-list:
attribute
attribute-list , attribute
attribute:
attribute-name attribute-argumentsopt
attribute-name:
reserved-attribute-name
type-name
attribute-arguments:
( positional-argument-list )
( positional-argument-list , named-argument-list )
( named-argument-list )
positional-argument-list:
positional-argument
positional-argument-list , positional-argument
positional-argument:
attribute-argument-expression
named-argument-list:
named-argument
named-argument-list , named-argument
named-argument:
identifier = attribute-argument-expression
attribute-argument-expression:
expression


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

22.18 Versioning Grammar

© 2000 Microsoft Corporation. All rights reserved.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

22.19 Unsafe code Grammar

19.2 Pointer types

pointer-type:
unmanaged-type *
void *
unmanaged-type:
value-type


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

22.20 Interoperability Grammar

This section of the Language Reference does not have any grammar.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

22.21 References Grammar

This section of the Language Reference does not have any grammar.


Access Keywords

This section introduces the following access keywords:

  • base Access the members of the base class.
  • this Refer to the current object for which a method is called.

See Also

C# Keywords


base

The base keyword is used to access members of the base class from within a derived class:

  • Call a method on the base class that has been overridden by another method.
  • Specify which base-class constructor should be called when creating instances of the derived class.

A base class access is permitted only in a constructor, an instance method, or an instance property accessor.

It is an error to use the base keyword from within a static method.

Example

In this example, both the base class, Person, and the derived class, Employee, have a method named Getinfo. By using the base keyword, it is possible to call the Getinfo method on the base class, from within the derived class.

// Accessing base class members
using System;
   public class Person {
      protected string ssn = "444-55-6666";
      protected string name = "John L. Malgraine";
      public virtual void Getinfo() {
         Console.WriteLine("Name: {0}", name);
         Console.WriteLine("SSN: {0}", ssn);
      }
   }
   class Employee: Person {
      public string id = "ABC567EFG";
      public override void Getinfo() {
         // Calling the base class Getinfo method:
         base.Getinfo();
         Console.WriteLine("Emplyee ID: {0}", id);
      }
   }
class TestClass {
   public static void Main() {
      Employee E = new Employee();
      E.Getinfo();
   }
}

Output

Name: John L. Malgraine
SSN: 444-55-6666
Emplyee ID: ABC567EFG

For additional examples, see new, virtual, and override.

Example

This sample shows how you can specify the base-class constructor called when creating instances of a derived class.

using System;
public class MyBase
{
   int num;
   public MyBase() {
      Console.WriteLine("in MyBase()");
   }
   public MyBase(int i )
   {
      num = i;
      Console.WriteLine("in MyBase(int i)");
   }

   public int GetNum()
   {
      return num;
   }
}

public class MyDerived : MyBase
{
   static int i = 32;

   // this constructor will call Base::Base()
   public MyDerived(int ii)  : base()
   {
   }

   // this constructor will call Base::Base(int i)
   public MyDerived()  : base(i)
   {
   }

   public static void Main() {

      MyDerived md = new MyDerived();
      MyDerived md1 = new MyDerived(1);
   }
}

Output

in MyBase(int i)
in MyBase()

See Also

C# Keywords | this


this

The this keyword refers to the current instance for which a method is called. Static member functions do not have a this pointer. The this keyword can be used to access members from within constructors, instance methods, and instance accessors.

The following are common uses of this:

  • To qualify members hidden by similar names, for example:
    public Employee(string name, string alias) {
   this.name = name;
   this.alias = alias;
}
  • To pass an object as a parameter to other methods, for example:
    CalcTax(this);
  • To declare indexers, for example:
    public int this [int param] {
      get {
         return array[param];
      }
      set {
         array[param] = value;
      }
   }

It is an error to refer to this in a static method, static property accessor, or variable initializer of a field declaration.

Example

In this example, this is used to qualify the Employee class members, name and alias, which are hidden by similar names. It is also used to pass an object to the method CalcTax, which belongs to another class.

// this example
using System;
public class Employee {
   public string name;
   public string alias;
   public decimal salary = 3000.00m;
   // Constructor:
   public Employee(string name, string alias) {
      // Use this to qualify the fields, name and alias:
      this.name = name;
      this.alias = alias;
   }
   // Printing method:
   public void printEmployee() {
      Console.WriteLine("Name: {0}\nAlias: {1}", name, alias);
   // Passing the object to the CalcTax method by using this:
      Console.WriteLine("Taxes: {0}",
                        decimal.Format(Tax.CalcTax(this), "C"));
   }
}
public class Tax {
   public static decimal CalcTax(Employee E) {
      return (0.08m*(E.salary));
   }
}
public class MainClass {
   public static void Main() {
      // Create objects:
      Employee E1 = new Employee ("John M. Trainer", "jtrainer");
      // Display results:
      E1.printEmployee();
   }
}

Output

Name: John M. Trainer
Alias: jtrainer
Taxes: $240.00

For additional examples, see class and struct.

See Also

C# Keywords | base


C# Language Features

This section of the C# Programmer's Reference provides information and examples for the following language features:

  • Arrays
  • Main
  • Properties
  • Indexers

See Also

C# Programmer's Reference


Arrays

An array is a data structure that contains a number of variables called the elements of the array. The array elements are accessed through computed indexes. C# arrays are zero indexed; that is, the array indexes start at zero. All the array elements must be of the same type, which is called the element type of the array. Array elements can be of any type, including an array type. An array can be a single-dimensional array, or a multidimensional array. Array types are reference types derived from the abstract base type System.Array.

The following topics are discussed in this section:

  • Single-dimensional Arrays
  • Multidimensional Arrays
  • Passing Arrays Using ref and out

See Also

Reference Types| §12 Arrays in the language reference


Single-dimensional Arrays

You can declare an array of five integers as in the following example:

int[] myArray = new int [5];

This array contains the elements from myArray[0] to myArray[4]. The new operator is used to create the array and initialize the array elements to their default values. In this example, all the array elements are initialized to zero.

An array that stores string elements can be declared in the same way. For example:

string[] myStringArray = new string[6];

Array Initialization

It is possible to initialize an array upon declaration, in which case, the rank specifier is not needed because it is already supplied by the number of elements in the initialization list. For example:

int[] myArray = new int[] {1, 3, 5, 7, 9};

A string array can be initialized in the same way. The following is a declaration of a string array where each array element is initialized by a name of a day:

string[] weekDays = new string[]
               {"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};

When you initialize an array upon declaration, it is possible to use the following shortcuts:

int[] myArray = {1, 3, 5, 7, 9};
string[] weekDays = {"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};

It is possible to declare an array variable without initialization, but you must use the new operator when you assign an array to this variable. For example:

int[] myArray;
myArray = new int[] {1, 3, 5, 7, 9};   // OK
myArray = {1, 3, 5, 7, 9};   // Error

Passing Arrays as Parameters

You can pass an initialized array to a method. For example:

PrintArray(myArray);

You can also initialize and pass a new array in one step. For example:

PrintArray(new int[] {1, 3, 5, 7, 9});

Example

In the following example, a string array is initialized and passed as a parameter to the PrintArray method, where its elements are displayed:

// Single-dimentional arrays
using System;
public class ArrayClass {
   static void PrintArray(string[] w) {
      for (int i=0; i < w.Length; i++)
         Console.Write(w[i] + " ");
   }
   public static void Main() {
      // Declare and initialize an array:
      string[] WeekDays = new string []
         {"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};
      // Pass the array as a parameter:
      PrintArray(WeekDays);
   }
}

Output

Sun Sat Mon Tue Wed Thu Fri

Example

This example builds an array, myArray, whose elements are arrays. Each one of the array elements has a different size.

// Array of arrays
using System;
public class ArrayTest {
   public static void Main() {
      // Declare the array of two elements:
      int[][] myArray = new int[2][];
      // Initialize the elements:
      myArray[0] = new int[5] {1,3,5,7,9};
      myArray[1] = new int[4] {2,4,6,8};
      // Display the array elements:
      for (int i=0; i < 2; i++) {
         Console.Write("Element({0}): ", i);
         for (int j=0; j < myArray[i].Length; j++)
            Console.Write("{0} ", myArray[i][j]);
         Console.WriteLine();
      }
   }
}

Output

Element(0): 1 3 5 7 9

Element(1): 2 4 6 8

See Also

Arrays | Multidimensional Arrays


Multidimensional Arrays

The following declaration creates a two-dimensional array of four rows and two columns:

int[,] myArray = new int[4, 2];

Array Initialization

You can initialize the array upon declaration as shown in the following example:

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

You can also initialize the array without specifying the rank:

int[,] myArray = {{1,2} , {3,4} , {5,6} , {7,8}};

If you choose to declare an array variable without initialization, you must use the new operator to assign an array to the variable. For example:

int[,] myArray;
myArray = new int[,]{{1,2} , {3,4} , {5,6} , {7,8}};   // OK
myArray = {{1,2} , {3,4} , {5,6} , {7,8}};   // Error

Passing Arrays as Parameters

You can pass an initialized array to a method. For example:

PrintArray(myArray);

You can also initialize and pass a new array in one step. For example:

PrintArray(new int[,]{{1,2} , {3,4} , {5,6} , {7,8}});

Example

In this example, a two-dimensional array is initialized and passed to the PrintArray method, where its elements are displayed.

// Two-dimentional arrays
using System;
public class ArrayClass {
   static void PrintArray(int[,] w) {
      // Display the array elements:
      for (int i=0; i < 4; i++)
         for (int j=0; j < 2; j++)
            Console.WriteLine("Element({0},{1})={2}", i, j, w[i,j]);
   }
   public static void Main() {
      // Pass the array as a parameter:
      PrintArray(new int[,]{{1,2} , {3,4} , {5,6} , {7,8}});
   }
}

Output

Element(0,0)=1
Element(0,1)=2
Element(1,0)=3
Element(1,1)=4
Element(2,0)=5
Element(2,1)=6
Element(3,0)=7
Element(3,1)=8

See Also

Arrays | Single-dimensional Arrays


Passing Arrays Using ref and out

Like all out parameters, an out parameter of an array type must be assigned before it is used; that is, it must be assigned by the callee. For example:

public static void MyMethod(out int[] arr) {
   arr = new int[10];   // definite assignment of arr
}

Like all ref parameters, a ref parameter of an array type must be definitely assigned by the caller. Therefore, there is no need to be definitely assigned by the callee. A ref parameter of an array type may be altered as a result of the call. For example, the array can be assigned the null value or can be initialized to a different array. For example:

public static void MyMethod(ref int[] arr) {
   arr = new int[10];   // arr initialized to a different array
}

The following two examples demonstrate the difference between out and ref when used in passing arrays to methods.

Example

In this example, the array myArray is declared in the caller (the Main method), and initialized in the FillArray method. Then, the array elements are returned to the caller and displayed.

// Using out to pass the array to a method.
using System;
class TestOut {
   static public void FillArray(out int[] myArray) {
      // Initialize the array:
      myArray = new int[5] {1, 2, 3, 4, 5};
   }
   static public void Main() {
      int[] myArray; // Initialization is not required
      // Pass the array to the callee using out:
      FillArray(out myArray);
      // Display the array elements:
      Console.WriteLine("Array elements are:");
      for (int i=0; i < myArray.Length; i++)
         Console.WriteLine(myArray[i]);
   }
}

Output

Array elements are:
1
2
3
4
5

Example

In this example, the array myArray is initialized in the caller (the Main method), and passed to the FillArray method by using the ref parameter. Some of the array elements are updated in the FillArray method. Then, the array elements are returned to the caller and displayed.

// Using ref to pass the array to a method
using System;
class TestRef {
   public static void FillArray(ref int[] arr) {
      // Create the array on demand:
      if (arr == null)
         arr = new int[10];
      // Otherwise fill the array:
      arr[0] = 123;
      arr[4] = 1024;
   }
   static public void Main () {
      // Initialize the array:
      int[] myArray = {1,2,3,4,5};
      // Pass the array using ref:
      FillArray(ref myArray);
      // Display the updated array:
      Console.WriteLine("Array elements are:");
      for (int i = 0; i < myArray.Length; i++)
         Console.WriteLine(myArray[i]);
   }
}

Output

Array elements are:
123
2
3
4
1024

See Also

Arrays


C# Attributes

This section contains an introduction to attributes and descriptions of the predefined attributes.

Custom attributes

AttributeUsage Describes how a custom attribute class can be used.

Conditional methods

conditional Marks a conditional method, a method whose execution depends on a specified preprocessing identifier.

Interoperability with unmanaged code

comimport | Marks a class as being an externally implemented COM coclass.
dispid | Specifies the COM dispatch ID of a method, field, or property.
dllimport | Identifies a method as being implemented externally by a function exported by an unmanaged DLL.
guid | Specifies a globally unique identifier (GUID) for a class or an interface.
in | Indicates a parameter that is to be passed from the caller to the called method.
interfacetype | Specifies the type of an interface: dispinterface, dual, or derived from IUnknown.
marshal | Describes the unmanaged marshaling format for a parameter, field, or method return value.
out | Indicates a parameter that is to be passed from the called method to the caller.
returnshresult | Marks a method or property as adhering to COM calling conventions rather than NGWS calling conventions.
structlayout | Specifies the unmanaged layout of fields in a managed class or struct.
structoffset | Specifies the offsets for fields in a class or struct.

Serialization

nonserialized | Prevents a field or property from being serialized.
serializable | Marks a class or struct as being serializable.

Obsolete entities

obsolete Marks a program entity that should not be used.

See Also

CLR 17 Attributes | Attributes Tutorial | Introduction to Attributes


Introduction to Attributes

C# provides a mechanism for defining declarative tags, called attributes, which you can place on certain entities in your source code to specify additional information. The information that attributes contain can be retrieved at run time through reflection. You can use predefined attributes or you can define your own custom attributes. This section provides a general introduction to attributes in C#; for more detailed information, refer to CLR 17 Attributes.

  • Using Attributes
  • Global Attributes
  • Creating Custom Attributes
  • Retrieving Attribute Information

See Also

C# Attributes | CLR 17 Attributes | Attributes Tutorial


Using Attributes

Attributes can be placed on most any declaration (though a specific attribute might restrict the types of declarations on which it is valid). Syntactically, an attribute is specified by placing the name of the attribute, enclosed in square brackets, in front of the declaration of the entity to which it applies. For example, a class with the predefined attribute comimport is declared like this:

[comimport] public class MyComimportClass { ... }

Many attributes have parameters, which can be either positional (unnamed) or named. Any positional parameters must be specified in a certain order and cannot be omitted; named parameters are optional and can be specified in any order. Positional parameters are specified first. For example, these three attributes are equivalent:

[dllimport("user32.dll", SetLastError=false, ExactSpelling=false)]
[dllimport("user32.dll", ExactSpelling=false, SetLastError=false)]
[dllimport("user32.dll")]

The first parameter, the DLL name, is positional and always comes first; the others are named. In this case, both named parameters default to false, so they can be omitted (refer to the individual attribute's documentation for information on default parameter values).

More than one attribute can be placed on a declaration, either separately or within the same set of brackets:

bool AMethod([in][out]ref double x);
bool AMethod([out][in]ref double x);
bool AMethod([in,out]ref double x);

Some attributes can be specified more than once for a given entity. An example of such a multi-use attribute is conditional:

[conditional("DEBUG"), conditional("TEST1")] void TraceMethod() {...}

See Also

Introduction to Attributes | Global Attributes | Creating Custom Attributes | Retrieving Attribute Information


Global Attributes

Most attributes are attached to specific language elements, such as classes or methods; however, some attributes are global—they apply to an entire assembly or module. Assembly-level attributes are specified as follows:

[assembly: attribute-list]

Module-level attributes are specified as follows:

[module: attribute-list]

where

attribute-list
A list of applicable global attributes.

Global attributes appear in the source code after any top-level using directives and before any type or namespace declarations. Global attributes can appear in multiple source files in a single compilation.

Note Assembly-level attributes can be specified only if an assembly manifest is being created.

The following NGWS attributes let you modify the information in an assembly:

  • System.Reflection.AssemblyConfigurationAttribute
  • System.Runtime.CompilerServices.AssemblyCultureAttribute
  • System.Reflection.AssemblyDescriptionAttribute
  • System.Runtime.CompilerServices.AssemblyKeyFileAttribute
  • System.Runtime.CompilerServices.AssemblyKeyNameAttribute
  • System.Runtime.CompilerServices.AssemblyDelaySignAttribute
  • System.Reflection.AssemblyTitleAttribute
  • System.Runtime.CompilerServices.AssemblyVersionAttribute

If you do not want an assembly created for a compilation, use the compiler option /target:module.

Example

[assembly: System.CLSCompliant(true)]

See Also

Introduction to Attributes | Using Attributes | Creating Custom Attributes | Retrieving Attribute Information


Creating Custom Attributes

You can create your own custom attributes by defining an attribute class, a class that derives directly or indirectly from System.Attribute (which makes identifying attribute definitions in metadata fast and easy). Suppose you want to tag classes and structs with the name of the programmer who wrote the class or struct. You might define a custom Author attribute class:

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct)]
public class Author : System.Attribute {
   public Author(string name) { this.name = name; version = 1.0; }
   public double version;
   string name;
}

The class name is the attribute's name, Author. It is derived from System.Attribute, so it is a custom attribute class. The constructor's parameters are the custom attribute's positional parameters (in this case, name), and any public read-write fields or properties are named parameters (in this case, version is the only named parameter). Note the use of the AttributeUsage attribute to make the Author attribute valid only on class and struct declarations.

You could use this new attribute as follows:

[Author("H. Ackerman", version=1.1)] class SomeClass{...}

AttributeUsage has a named parameter, AllowMultiple, with which you can make a custom attribute single-use or multi-use.

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct,
   AllowMultiple=true)] // multi-use attribute
public class Author : Attribute {...}
Note To prevent namespace collisions, all attribute names implicitly end with "Attribute"; in the preceding example, Author and AuthorAttribute could be used interchangeably. The same applies to AttributeUsage, which is defined in the base class library as System.AttributeUsageAttribute.

See Also

Introduction to Attributes | Using Attributes | Global Attributes | Retrieving Attribute Information


Retrieving Attribute Information

The fact that you can define custom attributes and place them in your source code would be of little value without some way of retrieving that information and acting on it. Fortunately, C# has a reflection system that allows you to do just that. The key method is GetCustomAttributes, which returns an array of objects that are the run-time equivalents of the source code attributes.

An attribute specification such as

[Author("H. Ackerman", version=1.1)] class AClass {...}

is conceptually equivalent to this:

Author anonymousAuthorObject = new Author("H. Ackerman");
anonymousAuthorObject.version = 1.1;

However, the code is not executed until AClass is queried for attributes. Calling GetCustomAttributes on AClass causes an Author object to be constructed and initialized as above. If the class has other attributes, other attribute objects are constructed similarly. GetCustomAttributes then returns the Author object and any other attribute objects in an array. You can then iterate over this array, determine what attributes were applied based on the type of each array element, and extract information from the attribute objects.

Example

Here is a complete example. A custom attribute is defined, applied to several entities, and retrieved via reflection.

using System;
 [AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct,
   AllowMultiple=true)]
public class Author : Attribute {
   public Author(string name) { this.name = name; version = 1.0; }
   public double version;
   string name;
   public string GetName() { return name; }
}

[Author("H. Ackerman")]
class FirstClass { /*...*/ }

class SecondClass { /*...*/ } // no Author attribute

[Author("H. Ackerman"), Author("M. Knott", version=1.1)]
class Steerage { /*...*/ }

class AuthorInfo {
   public static void Main() {
      PrintAuthorInfo(typeof(FirstClass));
      PrintAuthorInfo(typeof(SecondClass));
      PrintAuthorInfo(typeof(Steerage));
   }
   public static void PrintAuthorInfo(Type t) {
      Console.WriteLine("Author information for {0}", t);
      object[] attrs = t.GetCustomAttributes();
      for (int i=0; i<attrs.Length; ++i) {
         if (attrs[i] is Author) {
            Author a = (Author)attrs[i];
            Console.WriteLine("   {0}, version {1:f}",
a.GetName(), a.version);
         }
      }
   }
}

Output

Author information for FirstClass
   H. Ackerman, version 1.00
Author information for SecondClass
Author information for Steerage
   H. Ackerman, version 1.00
   M. Knott, version 1.10

See Also

Introduction to Attributes | Using Attributes | Global Attributes | Creating Custom Attributes


AttributeUsage

Describes how a custom attribute class can be used.

[AttributeUsage(
   validon,
   AllowMultiple=allowmultiple,
   Inherited=inherited
)]

Parameters

validon
Specifies the language elements on which the attribute may be placed; a combination of AttributeTargets values. Default value is AttributeTargets.All.
allowmultiple (Optional)
A bool; if true, the attribute is multi-use. Default is false (single-use).
inherited (Optional)
A bool; if true, the attribute is inherited by derived classes. If AllowMultiple is true, the attribute accumulates on the derived member; if AllowMultiple is false, the attribute will override (or replace) in inheritance. Default is false (not inherited).

Applies To

Class declarations.

Remarks

AttributeUsage is a single-use attribute. AttributeUsage is an alias for System.AttributeUsageAttribute.

Example

See the example in Introduction to Attributes.

See Also

C# Attributes | Attributes Tutorial


comimport

Marks a class as being an externally implemented COM coclass.

[comimport]

Applies To

Class declarations.

Remarks

comimport is a single-use attribute. comimport is an alias for System.InterOp.ComImportAttribute.

A class with the comimport attribute must also have a guid attribute. A comimport class must be derived directly from object and must not have any members. Creating an instance of a comimport class using new is equivalent to calling CoCreateInstance.

Example

[comimport, guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")]
class FilgraphManager
{
    // cannot have any members here
    // NOTE: the C# compiler will add a default parameterless
       constructor
}
class MainClass
{
    public static void Main()
    {
        // Create an instance of a COM classic coclass - calls
        // CoCreateInstance(E436EBB3-524F-11CE-9F53-0020AF0BA770,
        //                  NULL, CLSCTX_ALL,
        //                  IID_IUnknown, &f)
        // returns null on failure.
        FilgraphManager f = new FilgraphManager();
    }
}

See Also

C# Attributes | COM InterOp Tutorial


conditional

Marks a conditional method, a method whose execution depends on a specified pre-processing identifier.

[conditional(conditionalSymbol)]

Parameters

conditionalSymbol
A string representing the name of a preprocessing identifier.

Applies To

Method declarations.

Remarks

The conditional attribute is a multi-use attribute. conditional is an alias for System.Diagnostics.ConditionalAttribute.

Wherever a conditional method is called, the presence or absence of the preprocessing symbol specified by conditionalSymbol at the point of the call determines whether the call is included or omitted. If the symbol is defined, the call is included, otherwise the call is omitted. Conditional methods provide a cleaner, more elegant alternative to enclosing the method call in #if conditionalSymbol...#endif preprocessor directives.

A conditional method must be a method in a class or struct declaration and must have a return type of void (for other restrictions, see 17.4.4 conditional).

If a method has multiple conditional attributes, a call to the method is included if at least one of the conditional symbols is defined (in other words, the symbols are logically ORed together).

[conditional("A"), conditional("B")] public static void IfAorB() {...}

To achieve the effect of logically ANDing symbols, you can define serial conditional methods:

[conditional("A")] public static void IfAandB() { AandBPrivate(); }
[conditional("B")] static void AandBPrivate() {
/* code to execute when both A and B are defined... */
}

Call IfAandB; if both A and B are defined, AandBPrivate will execute.

Example

#define DEBUG
using System;
public class Trace {
[conditional("DEBUG")] public static void Msg(string msg) {
      Console.WriteLine(msg);
   }
}
class Test {
   static void A() {
      Trace.Msg("now in A.");
      B();
   }
   static void B() {
      Trace.Msg("now in B.");
   }
   public static void Main() {
      Trace.Msg("now in Main.");
      A();
      Console.WriteLine("done.");
   }
}

Output

now in Main.
now in A.
now in B.
done.

Example

Same as above, but with first line changed to

#undef DEBUG

Output

done.

See Also

C# Attributes


dispid

Specifies the COM dispatch ID of a method, field, or property.

[dispid(val)]

Parameters

val
An int; the dispatch ID.

Applies To

Method, field, and property declarations.

Remarks

dispid is a single-use attribute. dispid is an alias for System.InterOp.DispIdAttribute.

This attribute is used for COM interoperability and is typically placed on interface members.

Example

interface ISpellCheck {
   [dispid(1001)]
   bool CheckSpelling(string word, out string[] suggestedSpellings);
}

See Also

C# Attributes


dllimport

Identifies a method as being implemented externally by a function exported by an unmanaged DLL.

[dllimport(
   dllName,
   EntryPoint=entrypoint,
   CharSet=charset,
   SetLastError=setlasterror,
   ExactSpelling=exactspelling,
   CallingConvention=callingconvention
)]

Parameters

dllName
A string specifying the name of the DLL.
entrypoint (Optional)
A string specifying the name of the entry point in the DLL. Default value is the name of the method.
charset (Optional)
A CharSet value specifying the default marshaling of string parameters. Default value is CharSet.Auto.
setlasterror (Optional)
A bool; if true, the method preserves the Win32 "last error" information. Default is false ("last error" is not preserved, but performance is increased).
exactspelling (Optional)
A bool; if true, EntryPoint must exactly match the spelling of the indicated entry point. Default is false, which enables entry point name-matching heuristics.
callingconvention (Optional)
A CallingConvention value specifying the calling convention of the DLL entry point. Default value is CallingConvention.Winapi.

Applies To

Method declarations.

Remarks

dllimport is a single-use attribute. dllimport is an alias for System.InterOp.DllImportAttribute.

The dllimport attribute is used to specify the DLL that contains the implementation of an extern method.

Example

In this example, because ExactSpelling is false (its default), name-matching heuristics will match MessageBox to either MessageBoxA or MessageBoxW, depending on the platform. Strings will automatically be marshaled as ANSI or Unicode as appropriate (because CharSet defaults to CharSet.Auto).

class DllimportExample
{
    [dllimport("user32.dll")]
    public static extern int MessageBox(int h, string m,
       string c, int type);
    public static int Main() {
        return MessageBox(0, "Hello World!", "Caption", 0);
    }
}

See Also

C# Attributes | C# Tutorial – Native Code


guid

Specifies a globally unique identifier (GUID) for a class or an interface. This information is primarily useful for interoperability with COM.

[guid(guid)]

Parameters

guid
A string specifying the GUID.

Applies To

Class, interface, enum, and delegate declarations.

Remarks

guid is an alias for System.InterOp.GuidAttribute.

The guid attribute is a single-use attribute and takes one parameter, a GUID specified as a string of the form "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" (where each 'X' is a hexadecimal digit).

Example

[guid("34C134A9-5B50-49f1-9724-EFF39517A642")]
interface ISpellCheck {
   bool CheckSpelling(string word, out string[] suggestedSpellings);
}

See Also

C# Attributes


in

Indicates a parameter that is to be passed from the caller to the called method. This information is useful for custom marshaling.

[in]

Applies To

Parameter declarations.

Remarks

in is a single-use attribute. in is an alias for System.InterOp.InAttribute.

The in attribute can be used in combination with the out attribute. If a parameter has neither in nor out specified, its marshaling depends on its parameter modifiers: the ref modifier implies [in, out] marshaling; the out modifier implies [out] marshaling; the absence of any modifier implies [in] marshaling.

Example

In this hypothetical spelling-checker interface, the word to be checked is marshaled [in] to the CheckSpelling method and an array of suggested spellings is marshaled [out] to the caller:

interface ISpellCheck {
   bool CheckSpelling([in]string word, [out]out string[] suggestions);
}

See Also

C# Attributes | out


interfacetype

Specifies the type of an interface for COM interoperability: dispinterface, dual, or derived from IUnknown.

[interfacetype(val)]

Parameters

val
A ComInterfaceType value specifying the interface type.

Applies To

Interface declarations.

Remarks

interfacetype is a single-use attribute. interfacetype is an alias for System.InterOp.InterfaceTypeAttribute.

An interface with the interfacetype attribute must also have a guid specified.

Example

[
   comimport,
   guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"),
   interfacetype(ComInterfaceType.Dual)
]
interface IMediaControl // can not list any base interfaces here
{
   [returnshresult(true)] void Run();
   [returnshresult(true)] void Pause();
   [returnshresult(true)] void Stop();
   ...

See Also

C# Attributes


marshal

Describes the unmanaged marshaling format for a parameter, field, or method return value.

[marshal(
   unmanagedType,
   Cookie=cookie,
   IID=iid,
   Marshaler=marshaler,
   SubType=subtype,
   Size=size
)]

Applies To

Method, parameter, field, and property declarations.

Parameters

unmanagedtype
An UnmanagedType value specifying the unmanaged marshaling format.
cookie (Optional)
A string to be passed to the marshaler.
iid (Optional)
A string specifying the GUID when unmanagedtype is UnmanagedType.Interface.
marshaler (Optional)
A string specifying a marshaling class.
subtype (Optional)
An UnmanagedType value specifying the subsidiary type for UnmanagedType.Ptr and UnmanagedType.FixedArray types.
size (Optional)
An int; the size of a fixed-size array or string.

Remarks

marshal is a single-use attribute. marshal is an alias for System.InterOp.MarshalAttribute.

The marshal attribute indicates the manner in which to marshal the item. For example, a parameter of type string might be marshaled as a BStr, an LPStr, an LPWStr, an LPTStr, or a FixedSysString. For methods, marshal specifies how to marshal the return value of the method.

Example

In the following interface definition, the method AddSourceFilter takes one parameter of type string marshaled as a BStr and one of type object marshaled as an IDispatch; the return value of FilterCollection, an object, is marshaled as an IDispatch.

[guid("56A868B1-0AD4-11CE-B03A-0020AF0BA771"),
   InterfaceType(ComInterfaceType.Dual)]
interface IMediaControlFilter // can not list any base interfaces here
{
   [returnshresult(true)]
   void AddSourceFilter(
     [in, marshal(UnmanagedType.BStr)] string strFilename,
     [out, marshal(UnmanagedType.IDispatch)] object ppUnk
   );
   [returnshresult(true), marshal(UnmanagedType.IDispatch)]
   object FilterCollection();
}

See Also

C# Attributes | COM InterOp Tutorial | Default marshaling table


nonserialized

Prevents a field or property from being serialized.

[nonserialized]

Applies To

Field and property declarations.

Remarks

nonserialized is a single-use attribute. nonserialized is an alias for System.Serialization.NonSerializedAttribute.

If a class has the serializable attribute, the base class library serialization services will, by default, serialize all the fields and properties of the class. In some cases, a field or property should not be serialized, either for performance or correctness reasons (for example, it does not make sense to serialize a file handle). The nonserialized attribute placed on a field or property prevents it from being serialized.

Example

See the example for the serializable attribute.

See Also

C# Attributes | serializable


obsolete

Marks a program entity that should not be used.

[obsolete(message)]
[obsolete(message, iserror)]

Parameters

message
A string; ideally, a human-readable explanation of why the item is obsolete and what to use instead.
iserror
A bool; if true, the compiler should treat the use of the item as an error. Default value is false (compiler generates a warning).

Applies To

Any declaration that allows attributes.

Remarks

obsolete is a single-use attribute. obsolete is an alias for System.ObsoleteAttribute.

When an entity marked obsolete is used in a program, the compiler issues either an error or a warning (depending on IsError) and prints out message.

Example

using System;
public class MyClass {
   [obsolete("Don't use OldWay; use NewWay instead", true)]
   static void OldWay() { Console.WriteLine("Silly me!"); }
   static void NewWay() { Console.WriteLine("D'oh!"); }
   public static void Main() {
      OldWay();
   }
}

See Also

C# Attributes


out

Indicates a parameter that is to be passed from the called method to the caller. This information is useful for custom marshaling.

[out]

Applies To

Parameter declarations.

Remarks

out is a single-use attribute. out is an alias for System.InterOp.OutAttribute.

The out attribute can be used in combination with the in attribute. If a parameter has neither in nor out specified, its marshaling depends on its parameter modifiers:

  • ref modifier implies [in, out] marshaling
  • out modifier implies [out] marshaling
  • the absence of any modifier implies [in] marshaling

Example

See the example for the in attribute.

See Also

C# Attributes | in


returnshresult

Marks a method or property as adhering to COM calling conventions rather than NGWS calling conventions.

[returnshresult(value)]

Applies To

Method and property declarations.

Parameters

value
A bool; if true, the method or property follows COM calling conventions; if false, it follows NGWS calling conventions.

Remarks

returnshresult is a single-use attribute. returnshresult is an alias for System.InterOp.ReturnsHResultAttribute.

Typically, an unmanaged COM interface method returns an HRESULT indicating success or failure, and returns its actual result through a parameter. In contrast, the return value of a NGWS method is its actual result, and errors are reported through exceptions. The returnshresult attribute maps the COM calling convention to the NGWS convention.

For example, a COM method might be declared as follows (in MIDL):

interface IExample {
   HRESULT ComputeSquareRoot([in]double x, [out,retval]double *result);
}

The real return value is the last parameter of the method, which is marked with the out and retval attributes in the MIDL interface definition.

A literal translation to C# would look like this:

interface IExample {
   HRESULT ComputeSquareRoot(double x, out double returnValue);
}

An invocation of the method might look like this:

double y;
// assume exampleObj is an object that implements IExample
HRESULT hr = exampleObj.ComputeSquareRoot(5.0, out y);
if (hr != 0) throw new COMInteropException(hr);
// do something with y

However, the same ends can be achieved much more concisely by declaring the method to have the returnshresult attribute:

interface IExample {
   [returnshresult(true)] double ComputeSquareRoot(double x);
}

Then invoking the method becomes simply

// assume exampleObj as above
double y = exampleObj.ComputeSquareRoot(5.0);

Declaring the method as returning a double more accurately reflects its purpose; the fact that it is actually a COM method returning an HRESULT is hidden. Code is automatically inserted to check the return code and raise an exception if necessary.

Non-static methods have an implicit returnshresult(true) attribute, and static methods have an implicit returnshresult(false) attribute. In the context of interoperability with unmanaged code, non-static methods correspond to COM interface methods, so the implicit returnshresult(true) attribute is appropriate because most COM methods return an HRESULT. Static methods correspond to externally implemented dllimport methods, which typically do not return an HRESULT. In both cases the default is almost always correct, but you can override it by specifying an explicit returnshresult attribute. Outside the context of interoperability, returnshresult has absolutely no effect.

See Also

C# Attributes


serializable

Marks a class or struct as being serializable.

[serializable]

Applies To

Class and struct declarations.

Remarks

serializable is a single-use attribute. Serializable is an alias for System.Serialization.SerializableAttribute.

The base class library provides serialization services only to classes and structs with the serializable attribute. In the example below, the fact that class SerialExample is marked serializable and has a member of type Serial2 does not automatically confer the serializable attribute to class Serial2.

Within a serializable class or struct, individual fields can be marked nonserialized to prevent them from being serialized.

Example

[serializable]
public class SerialExample {
   int a;
   Serial2 x;
   [nonserialized] int temp; // don't bother to serialize this field
}
[serializable] // have to make this serializable too
public struct Serial2 {
   string s;
}

See Also

C# Attributes


structlayout

Specifies the layout of fields in a class or struct for interoperability with unmanaged code.

[structlayout(
   kind,
   Pack=pack,
   CharSet=charset,
   CheckFastMarshal=checkfastmarshal
)]

Applies To

Class and struct declarations.

Parameters

kind
A LayoutKind value specifying the kind of layout: sequential, union, or explicit.
pack (Optional)
An int; the packing size in bytes. Must be 1, 2, 4, 8, or 16. Default is 8.
charset (Optional)
A CharSet value specifying the default character set for contained character and string types. Default value is CharSet.Auto.
checkfastmarshal (Optional)
A bool; if true, generate a compile-time warning if the type is not blittable. Defaults to false (no warning is generated).

Remarks

structlayout is a single-use attribute. structlayout is an alias for System.InterOp.StructLayoutAttribute.

If kind is Layout.Sequential, successive members are assigned increasing offsets such that no members overlap, with padding inserted as necessary to satisfy the packing size constraint.

If kind is Layout.Union, all members are assigned offset 0. Union members cannot contain reference types.

If kind is Layout.Explicit, every field must have the structoffset attribute; otherwise, use of structoffset is prohibited.

Example

The following PDH_RAW_COUNTER struct exactly matches the Platform SDK PDH_RAW_COUNTER data structure.

[structlayout(LayoutKind.Sequential, pack=8)]
public struct PDH_RAW_COUNTER {
   public int CStatus;
   public FILETIME TimeStamp;
   public long FirstValue;
   public long SecondValue;
   public int MultiCount;
}
[structlayout(LayoutKind.Sequential, pack=4)]
public struct FILETIME {
   public int dwLow;
   public int dwHigh;
}

See Also

C# Attributes


structoffset

Specifies the offsets for fields in a class or struct for interoperability with unmanaged code.

[structoffset(offset)]

Applies To

Field declarations.

Parameters

offset
An int specifying the field offset, in bytes.

Remarks

structoffset is a single-use attribute. structoffset is an alias for System.InterOp.StructOffsetAttribute.

This attribute only applies to fields within a class or struct marked with the StructLayout (LayoutKind.Explicit) attribute.

No checking is performed on explicit offsets; you have total control over the layout of the fields.

Example

The following PDH_RAW_COUNTER struct exactly matches the Platform SDK PDH_RAW_COUNTER data structure.

[structlayout(LayoutKind.Explicit)]
public struct PDH_RAW_COUNTER {
   [structoffset(0)] public int CStatus;
   [structoffset(16)] public long FirstValue;
   [structoffset(4)] public FILETIME TimeStamp;
   [structoffset(24)] public long SecondValue;
   [structoffset(32)] public int MultiCount;
}
[structlayout(LayoutKind.Explicit)]
public struct FILETIME {
   [structoffset(0)] public int dwLow;
   [structoffset(4)] public int dwHigh;
}

See Also

C# Attributes


Supporting enums

The following enums are used with attributes:

  • AttributeTargets
  • CallingConvention
  • CharSet
  • ComInterfaceType
  • LayoutKind
  • UnmanagedType

See Also

C# Attributes


AttributeTargets

This enum is used with the attributeusage attribute.

public enum AttributeTargets
{
    Assembly      = 0x0001;
    Module        = 0x0002;
    Class         = 0x0004;
    Struct        = 0x0008;
    Enum          = 0x0010;
    Constructor   = 0x0020;
    Method        = 0x0040;
    Property      = 0x0080;
    Field         = 0x0100;
    Event         = 0x0200;
    Interface     = 0x0400;
    Parameter     = 0x0800;
    Delegate      = 0x1000;
    All           = Assembly | Module | Class | Struct | Enum |
                    Constructor | Method | Property | Field | Event |
                    Interface | Parameter | Delegate;
    ClassMembers  = Class | Struct | Enum | Constructor | Method |
                    Property | Field | Event | Delegate | Interface;

See Also

Supporting enums


CallingConvention

This enum is used with the dllimport attribute.

public enum CallingConvention
{
    Winapi          = 1;
    Cdecl           = 2;
    StdCall         = 3;
    Thiscall        = 4;
    FastCall        = 5;
}

See Also

Supporting enums


CharSet

This enum is used with the dllimport and structlayout attributes.

public enum CharSet
{
   None = 1;   // User didn't specify how to marshal strings.
   Ansi = 2;   // Strings should be marshaled as ANSI 1 byte chars.
   Unicode = 3;// Strings should be marshaled as Unicode 2 byte chars.
   Auto = 4;   // Marshal Strings in the right way for the target system.
}

See Also

Supporting enums


ComInterfaceType

This enum is used with the interfacetype attribute.

public enum ComInterfaceType
{
    Dual        = 0;
    IUnknown    = 1;
    IDispatch   = 2;
}

See Also

Supporting enums


LayoutKind

This enum is used with the structlayout attribute.

public enum LayoutKind
{
    Sequential;
    Union;
    Explicit;
}

See Also

Supporting enums


UnmanagedType

This enum is used with the marshal attribute.

public serializable enum UnmanagedType
{
    End         = 0x0;
    Void        = 0x1;
    Boolean     = 0x2;
    I1          = 0x3;
    U1          = 0x4;
    I2          = 0x5;
    U2          = 0x6;
    I4          = 0x7;
    U4          = 0x8;
    I8          = 0x9;
    U8          = 0xa;
    R4          = 0xb;
    R8          = 0xc;
    SysChar     = 0xd;
    Variant     = 0xe;
    Currency    = 0xf;
    Ptr         = 0x10;
    Decimal     = 0x11;
    Date        = 0x12;
    BStr        = 0x13;
    LPStr       = 0x14;
    LPWStr      = 0x15;
    LPTStr      = 0x16;
    FixedSysString  = 0x17;
    ObjectRef   = 0x18;
    IUnknown    = 0x19;
    IDispatch   = 0x1a;
    Struct      = 0x1b;
    Interface   = 0x1c;
    SafeArray   = 0x1d;
    FixedArray  = 0x1e;
    Int         = 0x1f;
    UInt        = 0x20;
    NestedStruct  = 0x21;
    ByValStr    = 0x22;
    AnsiBStr    = 0x23;
    TBStr       = 0x24;       // select BSTR or ANSIBSTR
                              //   depending on platform
    Max         = 0x25;       // first invalid element type

See Also

Supporting enums


Conversion Keywords

This section describes keywords used in type conversions:

  • explicit
  • implicit
  • operator

See Also

C# Keywords


explicit

The explicit keyword is used to declare an explicit user-defined type conversion operator (CLR 6.4.4 User-defined explicit conversions).

class MyType {
    public static explicit operator MyType(int i) {
        // code to convert from int to MyType
        ...

Unlike implicit conversion, explicit conversion operators must be invoked via a cast.

int i;
MyType x = (MyType)i; // int-to-MyType requires cast

Omitting the cast results in a compile-time error.

If a conversion operation can cause exceptions or lose information, you should mark it explicit. This prevents the compiler from silently invoking the conversion operation with possibly unforeseen consequences.

Example

The following example defines a struct, Digit, that represents a single decimal digit. An operator is defined for conversions from byte to Digit, but because not all bytes can be converted to a Digit, the conversion is explicit.

using System;
struct Digit
{
   byte value;
   public Digit(byte value) {
      if (value<0 || value>9) throw new ArgumentException();
      this.value = value;
   }
   // define explicit byte-to-Digit conversion operator:
   public static explicit operator Digit(byte b) {
      Console.WriteLine("conversion occurred");
      return new Digit(b);
   }
}
class Test {
   public static void Main() {
      byte b = 3;
      Digit d = (Digit)b; // explicit conversion
   }
}

Output

conversion occurred

See Also

C# Keywords | implicit | operator


implicit

The implicit keyword is used to declare an implicit user-defined type conversion operator (CLR 6.4.3 User-defined implicit conversions). For example:

class MyType {
    public static implicit operator int(MyType m) {
        // code to convert from MyType to int
        ...

Implicit conversion operators can be called implicitly, without being specified by explicit casts in the source code.

MyType x;
int i = x; // implicitly call MyType’s MyType-to-int conversion operator

By eliminating unnecessary casts, implicit conversions can improve source code readability. However, because implicit conversions can occur without the programmer’s specifying them, care must be taken to prevent unpleasant surprises. In general, implicit conversion operators should never throw exceptions and never lose information so that they can be used safely without the programmer’s awareness. If a conversion operator cannot meet those criteria, it should be marked explicit.

Example

The following example defines a struct, Digit, that represents a single decimal digit. An operator is defined for conversions from Digit to byte, and because any Digit can be converted to a byte, the conversion is implicit.

using System;
struct Digit
{
   byte value;
   public Digit(byte value) {
      if (value < 0 || value > 9) throw new ArgumentException();
      this.value = value;
   }
   // define implicit Digit-to-byte conversion operator:
   public static implicit operator byte(Digit d) {
      Console.WriteLine( "conversion occurred" );
      return d.value;
   }
}
class Test {
   public static void Main() {
      Digit d = new Digit(3);
      byte b = d;  // implicit (no cast) conversion
                   //  from Digit to byte
   }
}

Output

conversion occurred

See Also

C# Keywords | explicit | operator


operator

The operator keyword is used to declare an operator in a class or struct declaration. An operator declaration can take one of the following four forms:

public static result-type operator unary-operator ( op-type operand )
public static result-type operator binary-operator (
   op-type operand,
   op-type2 operand2
)
public static implicit operator conv-type-out ( conv-type-in operand )
public static explicit operator conv-type-out ( conv-type-in operand )

where:

result-type
The type of the operator's result.
unary-operator
One of: + - ! ~ ++ -- true false
op-type
The type of the first (or only) parameter.
operand
The name of the first (or only) parameter.
binary-operator
One of: + - * / % & | ^ << >> == != > < >= <=
op-type2
The type of the second parameter.
operand2
The name of the second parameter.
conv-type-out
The target type of a type conversion operator.
conv-type-in
The type of the input to a type conversion operator.

Remarks

The first two forms declare user-defined operators that overload built-in operators. Note that not all the built-in operators can be overloaded (see Overloadable Operators). At least one of op-type and op-type2 must be the enclosing type (that is, the type of which the operator is a member). This prevents redefining the integer addition operator, for instance.

The last two forms declare conversion operators. Exactly one of conv-type-in and conv-type-out must be the enclosing type (that is, a conversion operator can only convert from its enclosing type to some other type, or from some other type to its enclosing type).

Operators can only take value parameters, not ref or out parameters.

Any operator declaration can be preceded by an optional attribute list.

For more information, refer to CLR 10.9 Operators.

Example

The following is an extremely simplified class for rational numbers. It overloads the + and * operators to perform fractional addition and multiplication, and also provides an operator that converts fractions to doubles.

using System;
class Fraction {
   int num, den;
   public Fraction(int num, int den) {
      this.num = num;
      this.den = den;
   }
// overload operator +
   public static Fraction operator +(Fraction a, Fraction b) {
      return new Fraction(a.num * b.den + b.num * a.den,
         a.den * b.den);
   }
// overload operator *
   public static Fraction operator *(Fraction a, Fraction b) {
      return new Fraction(a.num * b.num, a.den * b.den);
   }
// define operator double
   public static implicit operator double(Fraction f) {
      return (double)f.num / f.den;
   }
}
class Test {
   public static void Main() {
      Fraction a = new Fraction(1, 2);
      Fraction b = new Fraction(3, 7);
      Fraction c = new Fraction(2, 3);
      Console.WriteLine((double)(a * b + c));
   }
}

Output

0.88095238095238093

See Also

C# Keywords | implicit | explicit


C# Keywords

Keywords are predefined reserved identifiers that have special meanings to the compiler. They cannot be used as identifiers in your program unless they include @ as a prefix. For example, @if is a legal identifier but if is not because it is a keyword.

abstract | enum | long | stackalloc
as | event | namespace | static
base | explicit | new | string
bool | extern | null | struct
break | false | object | switch
byte | finally | operator | this
case | fixed | out | throw
catch | float | override | true
char | for | params | try
checked | foreach | private | typeof
class | goto | protected | uint
const | if | public | ulong
continue | implicit | readonly | unchecked
decimal | in | ref | unsafe
default | int | return | ushort
delegate | interface | sbyte | using
do | internal | sealed | virtual
double | is | short | void
else | lock | sizeof | while

See Also

C# Preprocessor Directives | C# Operators


C# Programmer's Reference

The C# Programmer's Reference complements the C# Language Reference by providing the following information:

For information about | See
Keywords | C# Keywords
Operators | C# Operators
Attributes | C# Attributes
Conditional compilation | C# Preprocessor Directives
Types | Types Reference Tables
Arrays, Main method, properties, and indexers | C# Language Features

See Also

C# Language Reference | C# Start Page


Indexers

Indexers permit instances of a class or struct to be indexed in the same way as arrays. Indexers are similar to properties except that their accessor take parameters.

In this section, the following topics are introduced:

  • Indexer Declaration
  • Comparison Between Properties and Indexers
  • Interface Indexers

See Also

Properties


Indexer Declaration

Indexers allow you to index a class or a struct instance in the same way as an array. To declare an indexer, use the following declaration:

[attributes] [modifiers] indexer-declarator {accessor-declarations}

The indexer-declarator takes one of the forms:

type this [formal-index-parameter-list]
type interface-type.this [formal-index-parameter-list]

The formal-index-parameter takes the form:

[attributes] type identifier

where:

attributes (Optional)
Additional declarative information. For more information on attributes and attribute classes, see C# Attributes.
modifiers (Optional)
Allowed modifiers are new and a valid combination of the four access modifiers.
indexer-declarator
Includes the type of the element introduced by the indexer, this, and the formal-index-parameter-list. If the indexer is an explicit interface member implementation, the interface-type is included.
formal-index-parameter-list
Specifies the parameters of the indexer. The parameter includes optional attributes, the index type, and the index identifier. At least one parameter must be specified. The parameters out and ref are not allowed.
accessor-declarations
The indexer accessors, which specify the executable statements associated with reading and writing indexer elements.

The get Accessor

The get accessor body of an indexer is similar to a method body. It returns the type of the indexer. The get accessor uses the same formal-index-parameter-list as the indexer. For example:

get {
   return myArray[index];
}

The set Accessor

The set accessor body of an indexer is similar to a method body. It uses the same formal-index-parameter-list as the indexer, in addition to the value implicit parameter. For example:

set {
   myArray[index] = value;
}

Remarks

The type of an indexer and each of the types referenced in the formal-index-parameter-list must be at least as accessible as the indexer itself. For more information on accessibility levels, see Access Modifiers.

The signature of an indexer consists of the number and types of its formal parameters. It does not include the indexer type or the names of the formal parameters.

If you declare more than one indexer in the same class, they must have different signatures.

An indexer value is not classified as a variable; therefore, it is not possible to pass an indexer value as a ref or out parameter.

In order to provide the indexer a name that other languages can use for the default indexed property, use a name attribute in the declaration. For example:

   [name ("MyItem")]
   public int this [int index] {   // indexer declaration

This indexer will have the name MyItem. Without providing the name attribute, the default name will be Item.

Example

This example declares a private array field myArray and an indexer. Using the indexer allows direct access to the instance b[i]. The alternative to using the indexer is to declare the array as a public member and access its members, myArray[i], directly.

// Indexers
using System;
class IndexerClass {
      private int [] myArray = new int[100];
      public int this [int index] {   // indexer declaration
      get {
         // Check the index limits
         if (index < 0 || index >= 100)
            return 0;
         else
            return myArray[index];
      }
      set {
         if (!(index < 0 || index >= 100))
            myArray[index] = value;
      }
   }
}
public class MainClass {
   public static void Main() {
      IndexerClass b = new IndexerClass();
      // call the indexer to initialize the elements #3 and #5:
      b[3] = 256;
      b[5] = 1024;
      for (int i=0; i<=10; i++) {
         Console.WriteLine("Element #{0} = {1}", i, b[i]);
      }
   }
}

Output

Element #0 = 0
Element #1 = 0
Element #2 = 0
Element #3 = 256
Element #4 = 0
Element #5 = 1024
Element #6 = 0
Element #7 = 0
Element #8 = 0
Element #9 = 0
Element #10 = 0

Notice that when an indexer's access is evaluated (for example, in a Console.Write statement) the get accessor is invoked. Therefore, if no get accessor exists, a compile-time error occurs.

See also the example on Indexers in the language reference, §10.8.

See Also

Properties | Accessors | Indexers


Comparison Between Properties and Indexers

Indexers are similar to properties. Except for the differences shown in the following table, all the rules defined for property accessors apply to indexer accessors as well.

Property | Indexer
Identified by its name. | Identified by its signature.
Accessed through a simple name or a member access. | Accessed through an element access.
Can be a static or an instance member. | Must be an instance member.
A get accessor of a property has no parameters. | A get accessor of an indexer has the same formal parameter list as the indexer.
A set accessor of a property contains the implicit value parameter. | A set accessor of an indexer has the same formal parameter list as the indexer, in addition to the value parameter.

See Also

Properties | Accessors | Indexers


Interface Indexers

Indexers can be declared on interfaces. The declaration takes the following form:

[attributes] [new] type this [formal-index-parameter-list] {interface-accessors}

where:

attributes (Optional)
Same as indexers on classes.
type
Same as indexers on classes.
formal-index-parameter-list
Same as indexers on classes.
interface-accessors
The indexer accessors. See the Remarks below.

Remarks

Accessors of interface indexers differ from the accessors of class indexers as follows:

  • Interface accessors don't use modifiers.
  • The body of the accessor consists of a semicolon.

Thus, the purpose of the accessors is to indicate whether the indexer is read-write, read-only, or write-only.

The following is an example of an interface indexer accessor:

public interface IMyInterface {
   ...
   // Indexer declaration:
   string this[int index] {
      get;
      set;
   }
}

The signature of an indexer must differ from the signatures of all other indexers declared in the same interface.

Example

The following example demonstrates implementation of interface indexers.

using System;
// Indexer on an interface:
public interface IMyInterface {
   // indexer declaration:
   int this[int index] {
      get;
      set;
   }
}
// Implementing the interface:
class IndexerClass {
      private int [] myArray = new int[100];
      public int this [int index] {   // indexer declaration
      get {
         // Check the index limits
         if (index < 0 || index >= 100)
            return 0;
         else
            return myArray[index];
      }
      set {
         if (!(index < 0 || index >= 100))
            myArray[index] = value;
      }
   }
}
public class MainClass {
   public static void Main() {
      IndexerClass b = new IndexerClass();
      // call the indexer to initialize the elements #3 and #5:
      b[2] = 4;
      b[5] = 32;
      for (int i=0; i<=10; i++) {
         Console.WriteLine("Element #{0} = {1}", i, b[i]);
      }
   }
}

Output

Element #0 = 0
Element #1 = 0
Element #2 = 4
Element #3 = 0
Element #4 = 0
Element #5 = 32
Element #6 = 0
Element #7 = 0
Element #8 = 0
Element #9 = 0
Element #10 = 0

In the preceding example, you could use the explicit interface member implementation by using the fully qualified name of the interface member. For example:

   public string IMyInterface.this {...}

However, the fully qualified name is only needed to avoid ambiguity when the class is implementing more than one interface with the same indexer signature. For example, if the class Employee is implementing two interfaces, ICitizen and IEmployee, and both interfaces have the same indexer signature, the explicit interface member implementation will be necessary. That is, the following indexer declaration:

   public string IEmployee.this {...}

implements the indexer on the IEmployee interface, while the following declaration:

   public string ICitizen.this {...}

implements the indexer on the ICitizen interface.

For more information on Explicit Interface Member Implementation, see § 13.4.1 in the language reference.

See Also

Interface | Indexers


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

Literal Keywords

C# has the following literal keywords:

  • null
  • true
  • false

See Also

C# Keywords


null

The null keyword is a literal that represents a null reference, one that does not refer to any object. null is the default value of reference-type variables.

See Also

C# Keywords | Literal keywords | Default Values Table


Main

The Main method is the entry point of your program, where the program control starts and ends. It is declared inside a class or struct. It must be public and static. It can either be void or return an int. The Main method is where you create objects and invoke other methods. The Main method can be written without parameters or with parameters. The latter form allows your program to read command-line arguments.

See the following topics for more information and examples on using the Main method:

  • Return Values
  • Command-line Arguments


Return Values

The Main method can be of the type void:

public static void Main() {
   ...
}

It can also return an int:

public static int Main() {
   ...
   return 0;
}

Example

In this example, the program contains two classes, Factorial and MainClass. The Main method, which resides in the MainClass class, is used to read a number from the keyboard, invoke the Fac() method from the Factorial class, and calculate and display the factorial of the input number.

// The Main method
using System;
   public class Factorial {
      public static long Fac(long i) {
         return ((i <= 1) ? 1 : (i * Fac(i-1)));
      }
   }
class MyClass {
   public static void Main() {
      // Read a string from the keyboard:
      Console.Write("Enter an integer: ");
      string s = Console.ReadLine();
      // Convert the string to long:
      long num = Int64.FromString(s);
      Console.WriteLine("The Factorial of {0} is {1}.",
                         num, Factorial.Fac(num));
   }
}

Sample Run

Enter an integer: 5
The Factorial of 5 is 120.

See Also

Command-line Arguments


Command-line Arguments

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

public static int Main(string[] args)
public static void Main(string[] args)

The parameter of the Main method is a string array that represents the command-line arguments. Usually you check for the existence of the arguments by testing the Length property, for example:

if (args.Length == 0) {
   Console.WriteLine("Please enter a numeric argument.");
   return 1;
}

You can also convert the string arguments to numeric types by using the Parse() method. For example, the following statement converts the string to a long number by using the Parse() method on the Int64 class (for more information on System classes, see the Reference section of the NGWS SDK):

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]);

Example

In this example, the program takes one argument at run time, converts the argument to a long number, 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.

// Command line arguments
using System;
public class Factorial {
   public static long Fac(long i) {
      return ((i <= 1) ? 1 : (i * Fac(i-1)));
   }
}
class MainClass {
   public static int Main(string[] args) {
      // Test if input arguments were supplied:
      if (args.Length == 0)  {
         Console.WriteLine("Please enter a numeric argument.");
         Console.WriteLine("Usage: Factorial <num>");
         return 1;
      }
      // Convert the input arguments to numbers:
      long num = long.Parse(args[0]);
      Console.WriteLine("The Factorial of {0} is {1}.",
                        num, Factorial.Fac(num));
      return 0;
   }
}

Sample Runs

The following are two sample runs of the program assuming that the program name is Factorial.exe.

Run #1:

Factorial 10
The Factorial of 10 is 3628800.

Run #2:

Factorial
Please enter a numeric argument.
Usage: Factorial <num>

For more examples on using command-line arguments, see the example in Creating and Using C# DLLs.

See Also

Return Values | General Structure of a C# Program


Method Parameters

If a parameter is declared for a method without ref or out, the parameter can have a value associated with it. That value can be changed in the method, but the changed value will not be retained when control passes back to the calling procedure. By using a method parameter keyword, you can change this behavior.

This section describes the keywords you can use when declaring method parameters:

  • params
  • ref
  • out

See Also

C# Keywords


params

The params keyword lets you specify a method parameter that takes an argument where the number of arguments is variable.

No additional parameters are permitted after the params keyword in a method declaration, and only one params keyword is permitted in a method declaration.

Example

using System;
public class MyClass {

   public static void UseParams(params int[] list) {
      for ( int i = 0 ; i < list.Length ; i++ )
         Console.WriteLine(list[i]);
      Console.WriteLine();
   }

   public static void UseParams2(params object[] list) {
      for ( int i = 0 ; i < list.Length ; i++ )
         Console.WriteLine((object)list[i]);
      Console.WriteLine();
   }

   public static void Main() {
      UseParams(1, 2, 3);
      UseParams2(1, 'a', "test");

      int[] myarray = new int[3] {10,11,12};
      UseParams(myarray);
   }
}

Output

1
2
3

1
a
test

10
11
12

See Also

C# Keywords | Grammar


ref

The ref method parameter keyword on a method parameter causes a method to refer to the same variable that was passed into the method. Any changes made to the parameter in the method will be reflected in that variable when control passes back to the calling method.

To use a ref parameter, the argument must explicitly be passed to the method as a ref argument. The value of a ref argument will be passed to the ref parameter.

An argument passed to a ref parameter must first be initialized. Compare this to an out parameter, whose argument does not have to be explicitly initialized before being passed to an out parameter.

An overload will occur if declarations of two methods differ only in their use of ref.

A property is not a variable and cannot be passed as a ref parameter.

For information on passing an array, see Passing Arrays Using ref and out.

Example

using System;
public class MyClass {
   public static void TestRef(ref char i) {
   // The value of i will be changed in the calling method
      i = 'b';
   }

   public static void TestNoRef(char i) {
   // The value of i will be unchanged in the calling method
      i = 'c';
   }

   public static void Main() {
   // This function passes a variable as a ref parameter; the value of the
   // variable is changed after control passes back to this function.
   // The same variable is passed as a value parameter; the value of the
   // variable is unchanged after control is passed back to this function.
      char i = 'a';    // variable must be initialized
      TestRef(ref i);  // the arg must be passed as ref
      Console.WriteLine(i);
      TestNoRef(i);
      Console.WriteLine(i);
   }
}

Output

b
b

See Also

C# Keywords | Grammar


out

The out method parameter keyword on a method parameter causes a method to refer to the same variable that was passed into the method. Any changes made to the parameter in the method will be reflected in that variable when control passes back to the calling method.

Declaring an out method is useful when you want a method to return multiple values. A method that uses an out parameter can still return a value. A method can have more than one out parameter.

To use an out parameter, the argument must explicitly be passed to the method as an out argument. The value of an out argument will not be passed to the out parameter.

A variable passed as an out argument need not be initialized. However, the out parameter must be assigned a value before the method returns.

An overload will occur if declarations of two methods differ only in their use of out.

A property is not a variable and cannot be passed as an out parameter.

For information on passing an array, see Passing Arrays Using ref and out.

Example

using System;
public class MyClass {
   public static int TestOut(out char i) {
      i = 'b';
      return -1;
   }

   public static void Main() {
      char i;   // variable need not be initialized
      Console.WriteLine(TestOut(out i));
      Console.WriteLine(i);
   }
}

Output

-1
b

See Also

C# Keywords | Grammar


Modifiers

Modifiers are used to modify declarations of types and type members. This section introduces the C# modifiers:

Modifier | Purpose
Access Modifiers
  • public
  • private
  • internal
  • protected
 Specify the declared accessibility of types and type members.
abstract | Indicate that a class is intended only to be a base class of other classes.
const | Specify that the value of the field or the local variable cannot be modified.
event
  • Declares an event.
extern | Indicate that the method is implemented externally.
override | Provide a new implementation of a virtual member inherited from a base class.
readonly | Declare a field that can only be assigned values as part of the declaration or in a constructor in the same class.
sealed | Specify that a class cannot be inherited.
static | Declare a member that belongs to the type itself rather than to a specific object.
virtual | Declare a method or an accessor whose implementation can be changed by an overriding member in a derived class.

See Also

C# Keywords


Access Modifiers

Access modifiers are keywords used to specify the declared accessibility of a member or a type. This section introduces the four access modifiers:

  • public
  • protected
  • internal
  • private

The following five accessibility levels can be specified using the access modifiers:

public protected internal internal protected private

This section also introduces the following topics:

  • Accessibility Levels
  • Accessibility Domain
  • Restrictions on Using Accessibility Levels

See Also

C# Keywords | Modifiers


Accessibility Levels

When access is allowed to a member, it said to be accessible. Otherwise, it is inaccessible. Use the access modifiers, public, protected, internal, or private, to specify one of the following declared accessibilities for members.

Declared accessibility | Meaning
public | Access is not restricted.
protected | Access is limited to the containing class or types derived from the containing class.
internal | Access is limited to the current project.
protected internal | Access is limited to the current project or types derived from the containing class.
private | Access is limited to the containing type.

Only one access modifier is allowed for a member or type, except for the protected internal combination.

Access modifiers are not allowed on namespaces. Namespaces have no access restrictions.

Depending on the context in which a member declaration takes place, only certain declared accessibilities are permitted. If no access modifier is specified in a member declaration, a default accessibility is used.

Top-level types, which not nested into other types, can only have internal or public accessibility. The default accessibility for these types is internal.

Nested types, which are members of other types, can have declared accessibilities as indicated in the following table.

Members of | Default member accessibility | Allowed declared accessibility of the member
enum | public | None
class | private | public protected internal private protected internal
interface | public | None
struct | private | public internal private

The accessibility of a nested type depends on its accessibility domain, which is determined by both the declared accessibility of the member and the accessibility domain of the immediately containing type. However, the accessibility domain of a nested type cannot exceed that of the containing type.

See Also

Access Modifiers | Accessibility Domain | Restrictions on Using Accessibility Levels


Accessibility Domain

The accessibility domain of a member specifies where, in the program sections, a member can be referenced. If the member is nested within another type, its accessibility domain is determined by both the accessibility level of the member and the accessibility domain of the immediately containing type.

The accessibility domain of a top-level type is at least the program text of the project in which it is declared. That is, the entire source files of this project. The accessibility domain of a nested type is at least the program text of the type in which it is declared. That is, the type body, including any nested types. The accessibility domain of a nested type never exceeds that of the containing type. These concepts are demonstrated in the following example.

For more information on accessibility domains, see §3.3.2 of the language reference.

Example

This example contains a top-level type, T1, and two nested classes, M1 and M2. The classes contain fields with different declared accessibilities. In the Main method, a comment follows each statement to indicate the accessibility domain of each member. Notice that the statements that attempt to reference the inaccessible members are commented out. If you want to see the compiler errors caused by referencing an inaccessible member, remove the comments one at a time.

using System;
namespace MyNameSpace {
   public class T1 {
      public static int myPublicInt;
      internal static int myInternalInt;
      private static int myPrivateInt;
      public class M1 {
         public static int myPublicInt;
         internal static int myInternalInt;
         private static int myPrivateInt;
      }
      private class M2 {
         public static int myPublicInt;
         internal static int myInternalInt;
         private static int myPrivateInt;
      }
   }
   public class MainClass {
      public static int Main() {
         // Access to T1 fields:
         T1.myPublicInt = 1;      // Access is unlimited
         T1.myInternalInt = 2;    // Accessible only in current project
         // T1.myPrivateInt = 3;  // Error: inaccessible outside T1

         // Access to the M1 fields:
         T1.M1.myPublicInt = 1;     // Access is unlimited
         T1.M1.myInternalInt = 2;   // Accessible only in current project
         // T1.M1.myPrivateInt = 3; // Error: inaccessible outside M1

         // Access to the M2 fields:
         // T1.M2.myPublicInt = 1;   // Error: inaccessible outside T1
         // T1.M2.myInternalInt = 2; // Error: inaccessible outside T1
         // T1.M2.myPrivateInt = 3;  // Error: inaccessible outside M2
            return 0;
         }
   }
}

See Also

Access Modifiers | Accessibility Levels | Restrictions on Using Accessibility Levels


Restrictions on Using Accessibility Levels

When you declare a type, it is essential to see if that type has to be at least as accessible as another member or type. For example, the direct base class must be at least as accessible as the derived class. The following declarations will result in a compiler error, because the base class BaseClass is less accessible than MyClass:

class BaseClass {...}
public class MyClass: BaseClass {...} // Error

The following table summarizes the restrictions on using declared accessibility levels.

Context | Remarks
Classes | The direct base class of a class type must be at least as accessible as the class type itself.
Interfaces
  • The explicit base interfaces of an interface type must be at least as accessible as the interface type itself.
Delegates | The return type and parameter types of a delegate type must be at least as accessible as the delegate type itself.
Constants | The type of a constant must be at least as accessible as the constant itself.
Fields | The type of a field must be at least as accessible as the field itself.
Methods | The return type and parameter types of a method must be at least as accessible as the method itself.
Properties | The type of a property must be at least as accessible as the property itself.
Events | The type of an event must be at least as accessible as the event itself.
Indexers | The type and parameter types of an indexer must be at least as accessible as the indexer itself.
Operators | The return type and parameter types of an operator must be at least as accessible as the operator itself.
Constructors | The parameter types of a constructor must be at least as accessible as the constructor itself.

Example

The following example contains erroneous declarations of different types. The comment following each declaration indicates the expected compiler error.

// Restrictions on using declared accessibility
using System;
// A delegate:
   delegate int MyDelegate();
   class B {
// A private method:
   int MyPrivateMethod() {
      return 0;
   }
}
   public class A {
// Fields:
   public B myField = new B();   // Error: The type B is less accessible
// than the field A.myField
// Constants:
   public readonly B myConst = new B();
// Error: The type B is less accessible than the constant A.myConst
// Methods:
   public B MyMethod() {
      return new B();
   }   // Error: The type B is less accessible than the method A.MyMethod
// Properties:
   public  B MyProp {
      set {}
   }   // Error: The type B is less accessible than the property A.MyProp
// Delegates:
   MyDelegate d = new MyDelegate(B.MyMethod);
// Error: The parameter B.MyMethod is not accessible due to
// protection level
// Operators:
   public static B operator + (A m1, B m2) {
      return new B();   // Error: The type B is less accessible than the
                        // operator A.operator +(A,B)
   }
}

See Also

Access Modifiers | Accessibility Domain | Accessibility Levels


internal

The internal keyword is an access modifier for types and type members. Internal members are accessible only within files in the same assembly. For more information on assemblies, see Components and Assemblies.

A common use of internal access is in component-based development because it enables a group of components to cooperate in a private manner without being exposed to the rest of the application code. For example, a framework for building graphical user interfaces could provide Control and Form classes that cooperate using members with internal access. Since these members are internal, they are not exposed to code that is using the framework.

It is an error to reference a member with internal access outside the assembly within which it was defined.

For a comparison of internal with the other access modifiers, see Accessibility Levels.

Example

This example contains two files, Assembly1.cs and Assembly2.cs. The first file contains an internal base class, BaseClass. In the second file, an attempt to access the member of the base class will produce an error.

File Assembly1.cs:

// compile with /target:library
internal class BaseClass {
   public static int IntM;
}

File Assembly2.cs

// compile with /target:exe /reference:Assembly1.dll
class TestAccess {
   public static void Main() {
      BaseClass myBase = new BaseClass();   // error, BaseClass not visible outside assembly
   }
}

See Also

C# Keywords | Access Modifiers | Accessibility Levels | Modifiers


private

The private keyword is a member access modifier. Private access is the least permissive access level. Private members are accessible only within the body of the class or the struct in which they are declared.

Nested types in the same body can also access those private members.

It is a compile-time error to reference a private member outside the class or the struct in which it is declared.

For a comparison of private with the other access modifiers, see Accessibility Levels.

Example

In this example, the Employee class contains a public member, Name, and a private member, Salary. The public member can be accessed directly, while the private member must be accessed through the public method AccessSalary().

// Private members
using System;
class Employee {
   public string name;
   double salary;   // private access by default
   public double AccessSalary() {
      return salary;
   }
}
class MainClass {
   public static void Main() {
      Employee e = new Employee();
// Accessing the public field:
      string n = e.name;
// Accessing the private field:
      double s = e.AccessSalary();
   }
}

In the preceding example, if you attempt to access the private members directly, by using a statement like this:

double s = e.salary;

you will get the error message:

'Employee.Salary' is inaccessible due to its protection level.

See Also

C# Keywords | Access Modifiers | Accessibility Levels | Modifiers


protected

The protected keyword is a member access modifier. A protected member is accessible from within the class in which it is declared, and from within any class derived from the class that declared this member.

A protected member of a base class is accessible in a derived class only if the access takes place through the derived class type. For example, consider the following code segment:

class A {
   protected int x = 123;
}
class B : A {
   void F() {
      A a = new A();
      B b = new B();
      a.x = 10;   // Error
      b.x = 10;   // OK
   }
}

The statement a.x =10 generates an error because A is not derived from B.

Struct members cannot be protected because the struct cannot be inherited.

It is an error to reference a protected member from a class, which is not derived from the protected member's class.

For more information on protected members, see Protected Access in the Language Reference.

For a comparison of protected with the other access modifiers, see Accessibility Levels.

Example

In this example, the class MyDerivedC is derived from MyClass; therefore, you can access the protected members of the base class directly from the derived class.

using System;
class MyClass {
   protected int x;
   protected int y;
}
class MyDerivedC: MyClass {
   public static void Main() {
      MyDerivedC mC = new MyDerivedC();
      // Direct access to protected members:
      mC.x = 10;
      mC.y = 15;
      Console.WriteLine("x = {0}, y = {1}", mC.x, mC.y);
   }
}

Output

x = 10, y = 15

If you change the access levels of x and y to private, the compiler will issue the error messages:

'MyClass.y' is inaccessible due to its protection level.
'MyClass.x' is inaccessible due to its protection level.

See Also

C# Keywords | Access Modifiers | Accessibility Levels | Modifiers


public

The public keyword is an access modifier for types and type members. Public access is the most permissive access level. There are no restrictions on accessing public members.

For a comparison of public with the other access modifiers, see Accessibility Levels.

Example

In the following example, two classes are declared, MyClass1 and MyClass2. The public members x and y of the MyClass1 are accessed directly from MyClass2.

// Public access
using System;
class MyClass1 {
   public int x;
   public int y;
}
class MyClass2 {
   public static void Main() {
      MyClass1 mC = new MyClass1();
      // Direct access to public members:
      mC.x = 10;
      mC.y = 15;
      Console.WriteLine("x = {0}, y = {1}", mC.x, mC.y);
   }
}

Output

x = 10, y = 15

If you change the public access level to private or protected, you will get the error message:

'MyClass1.y' is inaccessible due to its protection level.

See Also

C# Keywords | Access Modifiers | Accessibility Levels | Modifiers


abstract

The abstract modifier can be used with classes, methods, and properties.

Use the abstract modifier in a class declaration to indicate that a class is intended only to be a base class of other classes.

Abstract classes have the following features:

  • An abstract class cannot be instantiated.
  • An abstract class may contain abstract methods and accessors.
  • It is not possible to modify an abstract class with the sealed modifier, which means that the class cannot be inherited.
  • A non-abstract class derived from an abstract class must include actual implementations of all inherited abstract methods and accessors.

Use the abstract modifier in a method or property declaration to indicate that the method or property does not contain implementation.

  • Abstract methods have the following features:
    • An abstract method is implicitly a virtual method.
    • Abstract method declarations are only permitted in abstract classes.
    • Abstract methods do not contain the method implementation. The body of an abstract method is only a semicolon, for example:
    public abstract void MyMethod();
    • The implementation is provided by an overriding method, which is a member of a non-abstract class.
    • It is an error to use the static, virtual, or override modifiers in an abstract method declaration.

Abstract properties behave like abstract methods, except for the differences in declaration and invocation syntax.

  • It is an error to use the abstract modifier on a static property.
  • An abstract inherited property can be overridden in a derived class by including a property declaration that uses the override modifier.

An abstract class must provide implementation for all interface members.

An abstract class that implements an interface might map the interface methods onto abstract methods. For example:

interface I {
   void M();
}
abstract class C: I {
   public abstract void M();
}

For more information, see Abstract Classes and Abstract Methods in the Language Reference.

Example

In this example, the class MyDerivedC is derived from an abstract class MyBaseC. The abstract class contains an abstract method, MyMethod(), and two abstract properties, GetX() and GetY().

// Abstract Classes
using System;
abstract class MyBaseC {   // Abstract class
   protected int x = 100;
   protected int y = 150;
   public abstract void MyMethod();   // Abstract method
   public abstract int GetX {   // Abstract property
      get;
   }
   public abstract int GetY {   // Abstract property
      get;
   }
}
class MyDerivedC: MyBaseC{
   public override void MyMethod() {
      x++;
      y++;
   }
   public override int GetX {     // overriding property
      get {
         return x+10;
      }
   }
   public override int GetY {   // overriding property
      get {
         return y+10;
      }
   }
   public static void Main() {
      MyDerivedC mC = new MyDerivedC();
      mC.MyMethod();
      Console.WriteLine("x = {0}, y = {1}", mC.GetX, mC.GetY);
   }
}

Output

x = 111, y = 161

In the preceding example, if you attempt to instantiate the abstract class by using a statement like this:

MyBaseC mC1 = new MyBaseC();   // Error

you will get the following error message:

Cannot create an instance of the abstract class 'MyBaseC'.

See Also

virtual | override | C# Keywords | Modifiers


const

The keyword const is used to modify a declaration of a field or local variable. It specifies that the value of the field or the local variable cannot be modified. A constant declaration introduces one or more constants of a given type. The declaration takes the form:

[attributes] [modifiers] const type declarators;

where:

attributes (optional)
Optional declarative information. For more information on attributes and attribute classes, see C# Attributes.
modifiers (optional)
Optional modifiers that include the new modifier and one of the four access modifiers.
type
One of the types: byte, char, short, int, long, float, double, decimal, bool, string, an enum type, or a reference type.
declarators
A comma-separated list of declarators. A declarator takes the form:

identifier = constant-expression

The attributes and modifiers apply to all of the members declared by the constant declaration.

The type of a constant declaration specifies the type of the members introduced by the declaration. A constant expression must yield a value of the target type, or of a type that can be implicitly converted to the target type.

A constant expression is an expression that can be fully evaluated at compile time. Therefore, the only possible values for constants of reference types are string and null.

Remarks

The constant declaration can declare multiple constants, for example:

public const double x = 1.0, y = 2.0, z = 3.0;

The static modifier is not allowed in a constant declaration.

A constant can participate in a constant expression, for example:

public const int c1 = 5.0;
public const int c2 = c1 + 100;

Example

// Constants
using System;
public class ConstTest {
   class MyClass {
      public int x;
      public int y;
      public const int c1 = 5;
      public const int c2 = c1 + 5;
      public MyClass(int p1, int p2) {
         x = p1;
         y = p2;
      }
   }
   public static void Main() {
      MyClass mC = new MyClass(11, 22);
      Console.WriteLine("x = {0}, y = {1}", mC.x, mC.y);
      Console.WriteLine("c1 = {0}, c2 = {1}", MyClass.c1, MyClass.c2 );
   }
}

Output

x = 11, y = 22
c1 = 5, c2 = 10

Example

This example demonstrates using constants as local variables.

using System;
public class TestClass {
   public static void Main() {
      const int c = 707;
      Console.WriteLine("My local constant = {0}", c);
   }
}

Output

My local constant = 707

See Also

C# Keywords | Modifiers


event

Specifies an event.

[attributes] [modifiers] event type declarator;
[attributes] [modifiers] event type member-name {accessor-declarations};

where:

accessor-declarations
Declaration of the property accessors, which are used to read and write the property.
attributes (optional)
Optional declarative information. For more information on attributes and attribute classes, see C# Attributes.
declarator
The name of the event.
member-name
The name of the event property.
modifiers (optional)
Optional modifiers that include new, static, and one of the four access modifiers.
type
The delegate to which you want to associate this event.

Remarks

The event keyword lets you specify a delegate that will be called upon the occurrence of some "event" in your code. The delegate can have one or more associated methods that will be called when your code indicates that the event has occurred. An event in one program can be made available to other programs that target the NGWS runtime.

The following steps must be taken in order to create and use C# events:

  1. Create or identify a delegate. If you are defining your own event, you must also ensure that there is a delegate to use with the event keyword. If the event is predefined, in the base class library, for example, then consumers of the event need only know the name of the delegate.
  2. Create a class that contains:
    • An event created from the delegate.
    • (optional) A method that verifies that an instance of the delegate declared with the event keyword exists. Otherwise, this logic must be placed in the code that fires the event.
    • Methods that call the event. These methods can be overrides of some base class functionality.

    This class defines the event.

  3. Define one or more classes that connect methods to the event. Each of these classes will include:
    • A constructor declared to take a parameter of the type class that contains the event declaration. This constructor will contain the code to associate one or more methods with the event.
    • The definition of the method(s) that will be associated with the event.
  4. Use the event:
    • Create an object of the class that contains the event declaration.
    • Create an object of the class that contains the event definition, using the constructor that you defined.

For more information on events, see:

  • Section 10.7 in the C# Language Reference
  • Events Tutorial

Example

A basic example of defining and using events can be found in the Event Tutorial.

Example

It is also possible to declare an event in an interface and implement it in a class:

using System;
public delegate void MyDelegate();   // delegate declaration

public interface I {
   event MyDelegate MyEvent;
   void FireAway();
}

public class MyClass: I {
   public event MyDelegate MyEvent;

   public void FireAway() {
      if (MyEvent != null)
         MyEvent();
   }
}

public class MainClass {
   static private void f() {
      Console.WriteLine("This is called when the event fires.");
   }

   static public void Main () {
      I i = new MyClass();

      i.MyEvent += new MyDelegate(f);
      i.FireAway();
   }
}

Example

One use for event properties is for exposing a large number of properties without allocating a field for each event, but instead using a hash table to store the event instances. This is only useful if you have a very large number of events, but you expect most of the events will not be implemented.

using System;
using System.Collections;

public delegate void MyDelegate1(int i);
public delegate void MyDelegate2(string s);
public delegate void MyDelegate3(int i, object o);
public delegate void MyDelegate4();

public class PropertyEventsSample {
   private Hashtable eventTable = new Hashtable();

   public event MyDelegate1 Event1 {
      get {
         return (MyDelegate1) eventTable["Event1"];
      }
      set {
         eventTable["Event1"] = value;
      }
   }

   public event MyDelegate1 Event2 {
      get {
         return (MyDelegate1) eventTable["Event2"];
      }
      set {
         eventTable["Event2"] = value;
      }
   }

   public event MyDelegate2 Event3 {
      get {
         return (MyDelegate2) eventTable["Event3"];
      }
      set {
         eventTable["Event3"] = value;
      }
   }

   public event MyDelegate3 Event4 {
      get {
         return (MyDelegate3) eventTable["Event4"];
      }
      set {
         eventTable["Event4"] = value;
      }
   }

   public event MyDelegate3 Event5 {
      get {
         return (MyDelegate3) eventTable["Event5"];
      }
      set {
         eventTable["Event5"] = value;
      }
   }

   public event MyDelegate4 Event6 {
      get {
         return (MyDelegate4) eventTable["Event6"];
      }
      set {
         eventTable["Event6"] = value;
      }
   }
}
public class MyClass {
   public static void Main() {
   }
}

Example

Another use for event properties covers the situation where you are implementing two interfaces, each with an event of the same name. In such a case, you must use an explicit implementation event property:

using System;

public delegate void MyDelegate1();

public interface I1 {
   event MyDelegate1 MyEvent;
}

public delegate int MyDelegate2(string s);

public interface I2 {
   event MyDelegate2 MyEvent;
}

public class ExplicitEventsSample: I1, I2 {
   public event MyDelegate1 MyEvent;  // normal implementation of I1.MyEvent.

   event MyDelegate2 I2.MyEvent { // explicit implementation of I2.MyEvent
      get {
         return MyEvent2Storage;
      }
      set {
         MyEvent2Storage = value;
      }
   }

   private MyDelegate2 MyEvent2Storage;  // underlying storage for I2.MyEvent.

   private void FireEvents() {
      if (MyEvent != null)
         MyEvent();
      if (MyEvent2Storage != null)
         MyEvent2Storage("hello");
   }
}

public class MyClass {
   public static void Main() {
   }
}

See Also

C# Keywords | Modifiers


extern

Use the extern modifier in method declaration to indicate that the method is implemented externally. A common use of the extern modifier is with the dllimport attribute.

Remarks

It is an error to use the abstract and extern modifiers together to modify the same member. Using the extern modifier means that the method is implemented outside the C# code, while using the abstract modifier means that the method implementation is not provided in the class.

The body of the method with the extern modifier is only a semicolon, for example:

public static extern int MyMethod(int x);
Note The extern keyword is more limited in use than in C++. To compare with the C++ keyword, see extern in the C++ Language Reference.

For more information on external methods, see External Methods in the Language Reference.

For more information on attributes, see Attributes in the Language Reference.

Example

In this example, the program receives a string from the user and displays it inside a message box. The program uses the MessageBox() method exported from User32.DLL.

// The extern modifier
using System;
class MyClass {
   [sysimport(dll = "user32.dll")]
   public static extern int MessageBox(int h, string m, string c, int type);
   public static int Main() {
      string myString;
      Console.Write("Enter your message: ");
      myString = Console.ReadLine();
      return MessageBox(0, myString, "My Message Box", 0);
   }
}

Sample Run

Enter your message: Where do you want to go today?

After you have entered the previous text, a message box that contains this text will pop up on the screen.

Example

This example uses two files, CM.cs and Cmdll.c, to demonstrate extern. The C file is an external DLL, which is invoked from within the C# program.

File: CM.cs

using System;
public class MyClass {
   [sysimport(dll="cmdll",name="MyMethod")]
   public static extern int MyMethod(int x);
   public static void Main() {
      Console.WriteLine("MyMethod() returns {0}.",MyMethod(5));
   }
}

File: Cmdll.c

int __declspec(dllexport) MyMethod(int i) {
   return i*10;
}

Compilation

To build the project use the following steps:

  • Compile the file Cmdll.c to a DLL using the Visual C++ command line:
    cl /LD /MD cmdll.c
  • Compile the CM.cs file using the command line:
    csc CM.cs
  • This will create the executable file CM.exe. When you run this program, MyMethod will pass the value 5 to the DLL file, which returns the value multiplied by 10.

Output

MyMethod() returns 50.

See Also

C# Keywords | Modifiers


override

Use the override modifier to modify a method or a property. An override method provides a new implementation of a member inherited from a base class. The method overridden by an override declaration is known as the overridden base method. The overridden base method must have the same signature as the override method.

You cannot override a non-virtual or static method. The overridden base method must be virtual, abstract, or override.

An override declaration cannot change the accessibility of the virtual method. Both the override method and the virtual method must have the same access level modifier.

You cannot use the following modifiers to modify an override method:

new static virtual abstract

An overriding property declaration must specify the exact same access modifier, type, and name as the inherited property, and the overridden property must be virtual, abstract, or override.

For more information on accessing the base class members, see Base Access in the Language Reference.

For more information on overriding methods, see Overriding Methods in the Language Reference.

Example

See the example for the virtual keyword.

From within the derived class that has an override method, you still can access the overridden base method that has the same name by using the base keyword. For example, if you have a virtual method MyMethod(), and an override method on a derived class, you can access the virtual method from the derived class by using the call:

base.MyMethod()

Compare this to the C++ way, where you use the scope resolution operator (::) and the base class name, for example:

My_Base_Class_Name::MyMethod()

Example

In this example, there is a base class, Square, and a derived class, Cube. Because the area of a cube is the sum of the areas of six squares, it is possible to calculate it by calling the Area() method on the base class.

// Calling overriden methods from the base class
using System;
class TestClass {
   public class Square {
      public double x;
      // Constructor:
      public Square(double x) {
         this.x = x;
      }
      public virtual double Area() {
         return x*x;
      }
   }
   class Cube: Square {
      // Constructor:
      public Cube(double x): base(x) {}
      // Calling the Area base method:
      public override double Area() {
         return (6*(base.Area()));
      }
   }
   public static void Main()  {
      double x = 5.2;
      Square s = new Square(x);
      Square c = new Cube(x);
      Console.WriteLine("Area of Square = {0}",
               double.Format(s.Area(), "0.00"));
      Console.WriteLine("Area of Cube = {0}",
               double.Format(c.Area(), "0.00"));
   }
}

Output

Area of Square = 27.04
Area of Cube = 162.24

See Also

abstract | virtual | C# Keywords | Modifiers


readonly

The readonly keyword is a modifier that you can use on fields. When a field declaration includes a readonly modifier, assignments to the fields introduced by the declaration can only occur as part of the declaration or in a constructor in the same class.

Remarks

You can assign a value to a readonly field only in the following contexts:

  • When the variable is initialized in the declaration, for example:
    public readonly int y = 5;
  • For an instance field, in the instance constructors of the class that contains the field declaration, or for a static field, in the static constructor of the class that contains the field declaration. These are also the only contexts in which it is valid to pass a readonly field as an out or ref parameter.

Example

// Readonly fields
using System;
public class ReadOnlyTest {
   class MyClass {
      public int x;
      public readonly int y = 25; // Initialize a readonly field
      public readonly int z;
      public MyClass() {
         z = 24;   // Initialize a readonly instance field
      }
      public MyClass(int p1, int p2, int p3) {
         x = p1;
         y = p2;
         z = p3;
      }
   }
   public static void Main() {
      MyClass p1= new MyClass(11, 21, 32);   // OK
      Console.WriteLine("p1: x={0}, y={1}, z={2}" , p1.x, p1.y, p1.z);
      MyClass p2 = new MyClass();
      p2.x = 55;   // OK
      Console.WriteLine("p2: x={0}, y={1}, z={2}" , p2.x, p2.y, p2.z);
   }
}

Output

p1: x=11, y=21, z=32
p2: x=55, y=25, z=24

In the preceding example, if you use a statement like this:

p2.y = 66;        // Error

you will get the compiler error message:

The left-hand side of an assignment must be an l-value

which is the same error you get when you attempt to assign a value to a constant.

See Also

C# Keywords | Modifiers


sealed

A sealed class cannot be inherited. It is an error to use a sealed class as a base class. Use the sealed modifier in a class declaration to prevent accidental inheritance of the class.

Remarks

  • It is not permitted to use the abstract modifier with a sealed class.
  • Structs are implicitly sealed; therefore, they cannot be inherited.

Example

// Sealed classes
using System;
sealed class MyClass {
   public int x;
   public int y;
}
class MainClass {
   public static void Main() {
      MyClass mC = new MyClass();
      mC.x = 110;
      mC.y = 150;
      Console.WriteLine("x = {0}, y = {1}", mC.x, mC.y);
   }
}

Output

x = 110, y = 150

In the preceding example, if you attempt to inherit from the sealed class by using a statement like this:

class MyDerivedC: MyClass {}   // Error

you will get the error message:

'MyDerivedC' cannot inherit from sealed class 'MyBaseC'.

See Also

C# Keywords | Modifiers


static

Use the static modifier to declare a static member, which belongs to the type itself rather than to a specific object. The static modifier can be used with fields, methods, properties, operators, and constructors, but cannot be used with indexers, destructors, or types.

Remarks

  • A constant or type declaration is implicitly a static member.
  • A static member cannot be referenced through an instance. Instead, it is referenced through the type name. For example, consider the following class:
    public class MyBaseC {
   public struct MyStruct {
      public static int x = 100;
   }
...

    To refer to the static member x, use the fully qualified name (unless it is accessible from the same scope):

    MyBaseC.MyStruct.x
  • While an instance of a class contains a separate copy of all instance fields of the class, there is only one copy of each static field.
  • It is not possible to use this to reference static methods or property accessors.
    Note The static keyword is more limited in use than in C++. To compare with the C++ keyword, see static in the C++ Language Reference.

To demonstrate instance members, consider a class that represents a company employee. Assume that the class contains a method to count employees and a field to store the number of employees. Both the method and the field do not belong to any instance employee. Instead they belong to the company class. Therefore, they should be declared as static members of the class.

For more information on constructors, see Instance Constructors in the Language Reference.

Example

This example reads the name and ID of a new employee, increments the employee counter by one, and displays the information for the new employee as well as the new number of employees. For simplicity, this program reads the current number of employees from the keyboard. In a real application, this information should be read from a file.

// Static members
using System;
public class Employee {
   public string id;
   public string name;
   public Employee () {}
   public Employee (string name, string id) {
      this.name = name;
      this.id = id;
   }
   public static int employeeCounter;
   public static int AddEmployee() {
      return ++employeeCounter;
   }
}
class MainClass: Employee {
   public static void Main() {
      Console.Write("Enter the employee's name: ");
      string name = Console.ReadLine();
      Console.Write("Enter the employee's ID: ");
      string id = Console.ReadLine();
      // Create the employee object:
      Employee e = new Employee (name, id);
      Console.Write("Enter the current number of employees: ");
      string n = Console.ReadLine();
      Employee.employeeCounter = Int32.FromString(n);
      Employee.AddEmployee();
      // Display the new information:
      Console.WriteLine("Name: {0}", e.name);
      Console.WriteLine("ID:   {0}", e.id);
      Console.WriteLine("New Number of Employees: {0}",
                         Employee.employeeCounter);
   }
}

Sample Run

Enter the employee's name: Tara Strahan
Enter the employee's ID: AF643G
Enter the current number of employees: 15
Name: Tara Strahan
ID:   AF643G
New Number of Employees: 16

See Also

C# Keywords | Modifiers


virtual

The virtual keyword is used to modify a method or property declaration, in which case the method or the property is called a virtual member. The implementation of a virtual member can be changed by an overriding member in a derived class.

When a virtual method is invoked, the runtime type of the object is checked for an overriding member. The overriding member in the most derived class is called, which might be the original member, if no derived class has overridden the member. For more information, see runtime type and most derived implementation in the Language Reference.

By default, methods are non-virtual.

You cannot use the virtual modifier with the following modifiers:

static abstract override

Virtual properties behave like abstract methods, except for the differences in declaration and invocation syntax.

  • It is an error to use the virtual modifier on a static property.
  • A virtual inherited property can be overridden in a derived class by including a property declaration that uses the override modifier.

For more information on virtual methods, see Virtual Methods in the Language Reference.

Example

In this example, the class Dimensions contains the two coordinates x, y, and the Area() virtual method. Different shape classes such as Circle, Cylinder, and Sphere inherit the Dimensions class, and the surface area is calculated for each figure. Each derived class has it own override implementation of Area(). The program calculates and displays the proper area for each figure by invoking the proper implementation of Area() according to the object associated with the method.

// Virtual and override
using System;
class TestClass {
   public class Dimensions {
      public const double pi = Math.PI;
      protected double x, y;
      public Dimensions() {}
      public Dimensions (double x, double y) {
         this.x = x;
         this.y = y;
      }
      public virtual double Area() {
         return x*y;
      }
   }
   public class Circle: Dimensions {
      public Circle(double r): base(r, 0) {}
      public override double Area() { return pi * x * x; }
   }
   class Sphere: Dimensions {
      public Sphere(double r): base(r, 0) {}
         public override double Area() { return 4 * pi * x * x; }
   }
   class Cylinder: Dimensions {
      public Cylinder(double r, double h): base(r, h) {}
      public override double Area() { return 2*pi*x*x + 2*pi*x*y; }
   }
   public static void Main()  {
      double r = 3.0, h = 5.0;
      Dimensions c = new Circle(r);
      Dimensions s = new Sphere(r);
      Dimensions l = new Cylinder(r, h);
      // Display results:
      Console.WriteLine("Area of Circle   = {0}",
               double.Format(c.Area(), "0.00"));
      Console.WriteLine("Area of Sphere   = {0}",
               double.Format(s.Area(), "0.00"));
      Console.WriteLine("Area of Cylinder = {0}",
               double.Format(l.Area(), "0.00"));
   }
}

Output

Area of Circle   = 28.27
Area of Sphere   = 113.10
Area of Cylinder = 150.80

In the preceding example, notice that the inherited classes Circle, Sphere, and Cylinder are all using constructors that initialize the base class, for example:

public Cylinder(double r, double h): base(r, h) {}

This is analogous to the C++ initialization list.

See Also

abstract | override | C# Keywords | Modifiers


Namespaces

This section describes the keywords that are associated with using namespaces:

  • namespace
  • using

See Also

C# Keywords


namespace

The namespace keyword is used to declare a scope. This namespace scope lets you organize code and gives you a way to create globally-unique types.

namespace name[.name1] ...] {
type declarations
}

where:

name, name1
A namespace name can be any legal identifier. A namespace name can contain periods.
type declarations
Within a namespace, you can declare one or more of the following types:
  • another namespace
  • class
  • interface
  • struct
  • enum
  • delegate

Remarks

Even if you do not explicitly declare one, a default namespace is created. This unnamed namespace, sometimes called the global namespace, is present in every file. Any identifier in the global namespace is available for use in a named namespace.

Namespaces implicitly have public access and this is not modifiable.

Section 3.6 and chapter 9 of the language reference discuss namespaces in more detail.

See 7.3 Member lookup for information about how the compiler resolves names.

See Access Modifiers for a discussion on the access modifiers that you can assign to elements within a namespace.

It is possible to define a namespace in two or more declarations. For example the following sample defines both classes as part of namespace MyCompany.

namespace MyCompany.Proj1 {
   class MyClass {
   }
}

namespace MyCompany.Proj1 {
   class MyClass1 {
   }
}

Example

using System;
namespace SomeNameSpace {
   public class MyClass {
      public static void Main() {
         Nested.NestedNameSpaceClass.SayHello();
      }
   }
   namespace Nested {   // a nested namespace
      public class NestedNameSpaceClass {
         public static void SayHello() {
            Console.WriteLine("Hello");
         }
      }
   }
}

Output

Hello

See Also

C# Keywords | Grammar


using

The using keyword has two uses:

  • Create an alias for a namespace (a using alias).
  • Permit the use of types in a namespace, such that, you do not have to qualify the use of a type in that namespace (a using directive).
using [alias = ]class_or_namespace;

where:

alias
A user-defined symbol that you want to represent a namespace. You will then be able to use alias to represent the namespace name.
class_or_namespace
The namespace name that you want to either use or alias, or the class name that you want to alias.

Remarks

You would create a using alias to make it easier to qualify an identifier to a namespace or class.

You would create a using directive to use the types in a namespace without having to specify the namespace. A using directive does not give you access to any namespaces that may be nested in the namespace you specify.

Namespace come in two categories: user-defined and system-defined. User-defined namespaces are namespaces defined in your code. See the documentation for the base class library for a list of the system-defined namespaces.

Example

The following sample shows how to define and use a using alias for a namespace:

using MyAlias = MyCompany.Proj.Nested ;   // define an alias to represent a namespace

namespace MyCompany.Proj {
   public class MyClass {
      public static void DoNothing() {
      }
   }
   namespace Nested {   // a nested namespace
      public class ClassInNestedNameSpace {
         public static void SayHello() {
            System.Console.WriteLine("Hello");
         }
      }
   }

}

public class UnNestedClass {
   public static void Main() {
      MyAlias.ClassInNestedNameSpace.SayHello();   // using alias
   }
}

Output

Hello

Example

The following sample shows how to define a using directive and a using alias for a class:

using System;   // using directive
using AliasToMyClass = NameSpace1.MyClass;   // using alias for a class

namespace NameSpace1 {
   public class MyClass {
      public override string ToString() {
         return "You are in NameSpace1.MyClass";
      }
   }
}

namespace NameSpace2 {
   class MyClass {
   };
}

namespace NameSpace3 {
   using NameSpace1;   // using directive
   using NameSpace2;   // using directive

   class Test {
      public static void Main() {
         AliasToMyClass somevar = new AliasToMyClass();
         Console.WriteLine(somevar);
      }
   }
}

Output

You are in NameSpace1.MyClass

See Also

C# Keywords | Grammar


C# Operators

C# provides a large set of operators, which are symbols that specify which operations to perform in an expression. C# predefines the usual arithmetic and logical operators, as well as a variety of others as shown in the following table. In addition, many operators can be overloaded by the user, thus changing their meaning when applied to a user-defined type.

Operator category | Operators
Arithmetic | + - * / %
Logical (boolean and bitwise) | & | ^ ! ~ && || true false
String concatenation | +
Increment, decrement | ++ --
Shift | << >>
Relational | == != < > <= >=
Assignment | = += -= *= /= %= &= |= ^= <<= >>=
Member access | .
Indexing | []
Cast | ()
Conditional | ?:
Delegate concatenation and removal | + -
Object creation | new
Type information | is sizeof typeof
Overflow exception control | checked unchecked
Indirection and Address | * -> [] &

Arithmetic Overflow

The arithmetic operators (+, -, *, /) can produce results that are outside the range of possible values for the numeric type involved. You should refer to the C# Language Reference section on a particular operator for details, but in general:

  • Integer arithmetic overflow either throws an OverflowException or discards the most significant bits of the result (see below). Integer division by zero always throws a DivideByZeroException.
  • Floating-point arithmetic overflow or division by zero never throws an exception, because floating-point types are based on IEEE 754 and so have provisions for representing infinity and NaN (Not a Number).
  • Decimal arithmetic overflow always throws an OverflowException. Decimal division by zero always throws a DivideByZeroException.

When integer overflow occurs, what happens depends on the execution context, which can be checked or unchecked. In a checked context, an OverflowException is thrown. In an unchecked context, the most significant bits of the result are discarded and execution continues. Thus, C# gives you the choice of handling or ignoring overflow.

In addition to the arithmetic operators, integral-type to integral-type casts can cause overflow (for example, casting a long to an int) and are subject to checked or unchecked execution. Also note that bitwise operators and shift operators never cause overflow.

See Also

CLR 7.2.1 Operator precedence and associativity | Overloadable Operators | C# Keywords


Overloadable Operators

C# allows user-defined types to overload operators by defining static member functions using the operator keyword. Not all operators can be overloaded, however, and others have restrictions, as listed in this table:

Operators | Overloadability
+, -, !, ~, ++, --, true, false | These unary operators can be overloaded.
+, -, *, /, %, &, |, ^, <<, >> | These binary operators can be overloaded.
==, !=, <, >, <=, >= | The comparison operators can be overloaded (but see note below).
&&, || | The conditional logical operators cannot be overloaded, but they are evaluated using & and |, which can be overloaded; see CLR 7.11.2 User-defined conditional logical operators.
[] | The array indexing operator cannot be overloaded, but you can define indexers.
() | The cast operator cannot be overloaded, but you can define new conversion operators (see explicit and implicit).
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= | Assignment operators cannot be overloaded, but +=, for example, is evaluated using +, which can be overloaded.
=, ., ?:, ->, new, is, sizeof, typeof | These operators cannot be overloaded.
Note The comparison operators, if overloaded, must be overloaded in pairs; that is, if == is overloaded, != must also be overloaded. The reverse is also true, and similarly for < and >, and for <= and >=.

See Also

C# Operators


[] Operator

Square brackets ([]) are used for arrays, indexers, and attributes. They can also be used with pointers.

An array type is a type followed by []:

int[] fib; // fib is of type int[], "array of int"
fib = new int[100]; // create a 100-element int array

To access an element of an array, the index of the desired element is enclosed in brackets:

fib[0] = fib[1] = 1;
for( int i=2; i<100; ++i ) fib[i] = fib[i-1] + fib[i-2];

An exception is thrown if an array index is out of range.

The array indexing operator cannot be overloaded; however, types can define indexers, properties that take one or more parameters. Indexer parameters are enclosed in square brackets, just like array indices, but indexer parameters can be declared to be of any type (unlike array indices, which must be integral).

For example, the base class library defines a Hashtable type which associates keys and values of arbitrary type:

Collections.Hashtable h =  new Collections.Hashtable();
h["a"] = 123; // note: using a string as the index

Square brackets are also used to specify attributes:

[attribute(AllowMultiple=true)]
public class Attr {
...

You can use square brackets to index off a pointer.

unsafe fixed ( int* p = fib ) { // p points to fib from earlier example
   p[0] = p[1] = 1;
   for( int i=2; i<100; ++i ) p[i] = p[i-1] + p[i-2];
}

No bounds checking is performed.

See Also

C# Operators | Arrays | Indexers Unsafe and Fixed


() Operator

In addition to being used to specify the order of operations in an expression, parentheses are used to specify casts (type conversions):

( type ) expr
where:
type
The name of the type to which expr is to be converted.
expr
An expression.

Remarks

A cast explicitly invokes the conversion operator from expr's type to type; the cast will fail if no such conversion operator is defined. To define a conversion operator, see explicit and implicit.

Example

The following program casts a double to an int. The program won't compile without the cast.

using System;
class Test {
   public static void Main() {
      double x = 1234.7;
      int a;
      a = (int)x; // cast double to int
      Console.WriteLine(a);
   }
}

Output

1234

See Also

C# Operators | CLR 7.6.10 Cast expressions | explicit


. Operator

The dot operator is used for member access. For example, consider the following class:

class Simple {
   public int a;
   public void b() {...}
}
Simple s = new Simple();

The variable s has two members, a and b; to access them, use the dot operator:

s.a = 6; // set member variable a;
s.b(); // invoke member function b;

The dot is also used to form qualified names, names that specify the namespace or interface (for example) to which they belong.

System.Console.WriteLine("hello"); // class Console in namespace System

The using directive makes some name qualification optional:

using System;
...
System.Console.WriteLine("hello");
Console.WriteLine("hello"); // same thing

But when an identifier is ambiguous, it must be qualified:

using System;
using OtherSystem; // a namespace containing another Console class
...
System.Console.WriteLine( "hello" ); // must qualify Console

See Also

C# Operators | CLR 7.5.4 Member access


+ Operator

The + operator can function as either a unary or a binary operator.

Unary + operators are predefined for all numeric types. The result of a unary + operation on a numeric type is simply the value of the operand.

Binary + operators are predefined for numeric and string types. For numeric types, + computes the sum of its two operands. When one or both operands are of type string, + concatenates the string representations of the operands.

Delegate types also provide a binary + operator, which performs delegate concatenation.

User-defined types can overload the unary + and binary + operators (see operator).

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(+5);        // unary plus
      Console.WriteLine(5 + 5);     // addition
      Console.WriteLine(5 + .5);    // addition
      Console.WriteLine("5" + "5"); // string concatenation
      Console.WriteLine(5.0 + "5"); // string concatenation
// note automatic conversion from double to string
   }
}

Output

5
10
5.5
55
55

See Also

C# Operators | CLR 7.6.1 Unary plus operator | CLR 7.7.4 Addition operator


- Operator

The - operator can function as either a unary or a binary operator.

Unary - operators are predefined for all numeric types. The result of a unary - operation on a numeric type is the negation of the operand.

Binary - operators are predefined for all numeric and enumeration types to subtract the second operand from the first.

Delegate types also provide a binary - operator, which performs delegate removal.

User-defined types can overload the unary – and binary - operators (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 5;
      Console.WriteLine(-a);
      Console.WriteLine(a - 1);
      Console.WriteLine(a - .5);
   }
}

Output

-5
4
4.5

See Also

C# Operators | CLR 7.6.2 Unary minus operator | CLR 7.7.5 Subtraction operator


* Operator

The multiplication operator (*) computes the product of its operands. All numeric types have predefined multiplication operators.

The * operator is also used to declare pointer types and to dereference pointers.

User-defined types can overload the * operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(5 * 2);
      Console.WriteLine(-.5 * .2);
      Console.WriteLine(-.5m * .2m); // decimal type
   }
}

Output

10
-0.10000000000000001
-0.1

Note that the double type cannot represent the value -0.1 exactly, while the decimal type can.

See Also

C# Operators | CLR 7.7.1 Multiplication operator


/ Operator

The division operator (/) divides its first operand by its second. All numeric types have predefined division operators.

User-defined types can overload the / operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(-5/2);
      Console.WriteLine(-5.0/2);
   }
}

Output

-2
-2.5

See Also

C# Operators | CLR 7.7.2 Division operator


% Operator

The modulus operator (%) computes the remainder after dividing its first operand by its second. All numeric types have predefined modulus operators.

User-defined types can overload the % operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(5 % 2);      // int
      Console.WriteLine(-5 % 2);      // int
      Console.WriteLine(5.0 % 2.2);   // double
      Console.WriteLine(5.0m % 2.2m);// decimal
      Console.WriteLine(-5.2 % 2.0);   // double
   }
}

Output

1
-1
0.59999999999999964
0.6
-1.2000000000000002

Note the round-off errors associated with the double type.

See Also

C# Operators | CLR 7.7.3 Remainder operator


& Operator

The & operator can function as either a unary or a binary operator.

The unary & operator returns the address of its operand (requires unsafe context).

Binary & operators are predefined for the integral types and bool. For integral types, & computes the bitwise AND of its operands. For bool operands, & computes the logical AND of its operands; that is, the result is true if and only if both its operands are true.

User-defined types can overload the binary & operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(true & false); // logical and
      Console.WriteLine(true & true);  // logical and
      Console.WriteLine("0x{0:x}", 0xf8 & 0x3f);  // bitwise and
   }
}

Output

False
True
0x38

See Also

C# Operators | CLR 7.6.6 Address operator | CLR 7.10 Logical operators


| Operator

Binary | operators are predefined for the integral types and bool. For integral types, & computes the bitwise OR of its operands. For bool operands, | computes the logical OR of its operands; that is, the result is false if and only if both its operands are false.

User-defined types can overload the | operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(true | false);  // logical or
      Console.WriteLine(false | false); // logical or
      Console.WriteLine("0x{0:x}", 0xf8 | 0x3f);   // bitwise or
   }
}

Output

True
False
0xff

See Also

C# Operators | CLR 7.10 Logical operators


^ Operator

Binary ^ operators are predefined for the integral types and bool. For integral types, ^ computes the bitwise exclusive-OR of its operands. For bool operands, ^ computes the logical exclusive-or of its operands; that is, the result is true if and only if exactly one of its operands is true.

User-defined types can overload the ^ operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(true ^ false);  // logical exclusive-or
      Console.WriteLine(false ^ false); // logical exclusive-or
      Console.WriteLine("0x{0:x}", 0xf8 ^ 0x3f);   // bitwise exclusive-or
   }
}

Output

True
False
0xc7

See Also

C# Operators | CLR 7.10.1 Integer logical operators


! Operator

The logical negation operator (!) is a unary operator that negates its operand. It is defined for bool and returns true if and only if its operand is false.

User-defined types can overload the ! operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(!true);
      Console.WriteLine(!false);
   }
}

Output

False
True

See Also

C# Operators | CLR 7.6.3 Logical negation operator


~ Operator

The ~ operator performs a bitwise complement operation on its operand. Bitwise complement operators are predefined for int, uint, long, and ulong.

User-defined types can overload the ~ operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine("!0x{0:x8} = 0x{1:x8}", 8, ~8);
      Console.WriteLine("!0x{0:x8} = 0x{1:x8}", -8, ~-8);
   }
}

Output

!0x00000008 = 0xfffffff7
!0xfffffff8 = 0x00000007

See Also

C# Operators | CLR 7.6.4 Bitwise complement operator


= Operator

The assignment operator (=) stores the value of its right-hand operand in the storage location, property, or indexer denoted by its left-hand operand and returns the value as its result. The operands must be of the same type (or the right-hand operand must be implicitly convertible to the type of the left-hand operand).

The assignment operator cannot be overloaded.

Example

using System;
class Test {
   public static void Main() {
      double x;
      int i;
      i = 5; // int to int assignment
      x = i; // implicit conversion from int to double
      i = (int)x; // needs cast
      Console.WriteLine("i is {0}, x is {1}", i, x);
      object obj = i;
      Console.WriteLine("boxed value = {0}, type is {1}",
         obj, obj.GetType());
      i = (int)obj;
      Console.WriteLine("unboxed: {0}", i);
   }
}

Output

i is 5, x is 5
boxed value = 5, type is Int32
unboxed: 5

See Also

C# Operators | CLR 7.13 Assignment operators


< Operator

All numeric and enumeration types define a "less than" relational operator (<) that returns true if the first operand is less than the second, false otherwise.

User-defined types can overload the < operator (see operator). If < is overloaded, > must also be overloaded.

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(1 < 1.1);
      Console.WriteLine(1.1 < 1.1);
   }
}

Output

True
False

See Also

C# Operators | CLR 7.9 Relational operators


> Operator

All numeric and enumeration types define a "greater than" relational operator (>) that returns true if the first operand is greater than the second, false otherwise.

User-defined types can overload the > operator (see operator). If > is overloaded, < must also be overloaded.

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(1.1 > 1);
      Console.WriteLine(1.1 > 1.1);
   }
}

Output

True
False

See Also

C# Operators | CLR 7.9 Relational operators


?: Operator

The conditional operator (?:) returns one of two values depending on a third value. The conditional operator is used in an expression of the following form:

cond-expr ? expr1 : expr2
where:
cond-expr
An expression of type bool.
expr1
An expression.
expr2
An expression.

Remarks

If cond-expr is true, expr1 is evaluated and becomes the result; if cond-expr is false, expr2 is evaluated and becomes the result. Only one of expr1 and expr2 is ever evaluated.

Calculations that might otherwise require an if-else construction can be expressed more concisely and elegantly with the conditional operator. For example, to avoid a division by zero in the calculation of the sinc function you could write either

if(x != 0.0) s = Math.Sin(x)/x; else s = 1.0;

or, using the conditional operator,

s = x != 0.0 ? Math.Sin(x)/x : 1.0;

The conditional operator is right-associative, so an expression of the form

a ? b : c ? d : e

is evaluated as

a ? b : (c ? d : e)

not

(a ? b : c) ? d : e

The conditional operator cannot be overloaded.

Example

using System;
class Test {
   public static double sinc(double x) {
      return x != 0.0 ? Math.Sin(x)/x : 1.0;
   }
   public static void Main() {
      Console.WriteLine(sinc(0.2));
      Console.WriteLine(sinc(0.1));
      Console.WriteLine(sinc(0.0));
   }
}

Output

0.99334665397530608
0.99833416646828155
1

See Also

C# Operators | CLR 7.12 Conditional operator


++ Operator

The increment operator (++) increments its operand by 1. The increment operator can appear before or after its operand:

++ var
var ++
where:
var
An expression that denotes a storage location or a property or an indexer.

Remarks

The first form is a prefix increment operation. The result of the operation is the value of the operand after it has been incremented.

The second form is a postfix increment operation. The result of the operation is the value of the operand before it has been incremented.

Numeric and enumeration types have predefined increment operators. User-defined types can overload the ++ operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      double x;
      x = 1.5;
      Console.WriteLine(++x);
      x = 1.5;
      Console.WriteLine(x++);
      Console.WriteLine(x);
   }
}

Output

2.5
1.5
2.5

See Also

C# Operators | CLR 7.5.9 Postfix increment operator | CLR 7.6.7 Prefix increment operator


-- Operator

The decrement operator (--) decrements its operand by 1. The decrement operator can appear before or after its operand:

-- var
var --
where:
var
An expression that denotes a storage location or a property or an indexer.

Remarks

The first form is a prefix decrement operation. The result of the operation is the value of the operand after it has been decremented.

The second form is a postfix decrement operation. The result of the operation is the value of the operand before it has been decremented.

Numeric and enumeration types have predefined decrement operators. User-defined types can overload the -- operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      double x;
      x = 1.5;
      Console.WriteLine(--x);
      x = 1.5;
      Console.WriteLine(x--);
      Console.WriteLine(x);
   }
}

Output

0.5
1.5
0.5

See Also

C# Operators | CLR 7.5.10 Postfix decrement operator | CLR 7.6.8 Prefix decrement operator


&& Operator

The conditional-AND operator (&&) performs a logical-AND of its bool operands, but only evaluates its second operand if necessary. That is, the operation

x && y

corresponds to the operation

x & y

except that if x is false, y is not evaluated (because the result of the AND operation is false no matter what the value of y may be). This is known as "short-circuit" evaluation.

The conditional-AND operator cannot be overloaded, but overloads of the regular logical operators and operators true and false are, with certain restrictions, also considered overloads of the conditional logical operators (see CLR 7.11.2 User-defined conditional logical operators).

Example

In the following example, observe that the expression using && evaluates only the first operand.

using System;
class Test {
   static bool fn1() {
      Console.WriteLine("fn1 called");
      return false;
   }
   static bool fn2() {
      Console.WriteLine("fn2 called");
      return true;
   }
   public static void Main() {
      Console.WriteLine("regular AND:");
      Console.WriteLine("result is {0}", fn1() & fn2());
      Console.WriteLine("short-circuit AND:");
      Console.WriteLine("result is {0}", fn1() && fn2());
   }
}

Output

regular AND:
fn1 called
fn2 called
result is False
short-circuit AND:
fn1 called
result is False

See Also

C# Operators | CLR 7.11 Conditional logical operators


|| Operator

The conditional-OR operator (&&) performs a logical-OR of its bool operands, but only evaluates its second operand if necessary. That is, the operation

x || y

corresponds to the operation

x | y

except that if x is true, y is not evaluated (because the result of the OR operation is true no matter what the value of y might be). This is known as "short-circuit" evaluation.

The conditional-OR operator cannot be overloaded, but overloads of the regular logical operators and operators true and false are, with certain restrictions, also considered overloads of the conditional logical operators (see CLR 7.11.2 User-defined conditional logical operators).

Example

In the following example, observe that the expression using || evaluates only the first operand.

using System;
class Test {
   static bool fn1() {
      Console.WriteLine("fn1 called");
      return true;
   }
   static bool fn2() {
      Console.WriteLine("fn2 called");
      return false;
   }
   public static void Main() {
      Console.WriteLine("regular OR:");
      Console.WriteLine("result is {0}", fn1() | fn2());
      Console.WriteLine("short-circuit OR:");
      Console.WriteLine("result is {0}", fn1() || fn2());
   }
}

Output

regular OR:
fn1 called
fn2 called
result is True
short-circuit OR:
fn1 called
result is True

See Also

C# Operators | CLR 7.11 Conditional logical operators


<< Operator

The left-shift operator (<<) shifts its first operand left by the number of bits specified by its second operand.

expr << count
where:
expr
An expression of type int, uint, long, or ulong; the value to be shifted.
count
An expression of type int; the shift count.

Remarks

If expr is an int or uint (32-bit quantity), the shift count is given by the low-order five bits of count (count & 0x1f).

If expr is a long or ulong (64-bit quantity), the shift count is given by the low-order six bits of count (count & 0x3f).

The high-order bits of expr are discarded and the low-order empty bits are zero-filled. Shift operations never cause overflows.

User-defined types can overload the << operator (see operator); the type of the first operand must be the user-defined type, and the type of the second operand must be int.

Example

using System;
class Test {
   public static void Main() {
      int i = 1;
      long lg = 1;
      Console.WriteLine("0x{0:x}", i << 1);
      Console.WriteLine("0x{0:x}", i << 33);
      Console.WriteLine("0x{0:x}", lg << 33);
   }
}

Output

0x2
0x2
0x200000000

Note that i<<1 and i<<33 give the same result, because 1 and 33 have the same low-order five bits.

See Also

C# Operators | CLR 7.8 Shift operators | >> Operator


>> Operator

The right-shift operator (>>) shifts its first operand right by the number of bits specified by its second operand.

expr >> count
where:
expr
An expression of type int, uint, long, or ulong; the value to be shifted.
count
An expression of type int; the shift count.

Remarks

If expr is an int or uint (32-bit quantity), the shift count is given by the low-order five bits of count (count & 0x1f).

If expr is a long or ulong (64-bit quantity), the shift count is given by the low-order six bits of count (count & 0x3f).

If expr is an int or long, the right-shift is an arithmetic shift (high-order empty bits are set to the sign bit). If expr is of type uint or ulong, the right-shift is a logical shift (high-order bits are zero-filled).

User-defined types can overload the >> operator (see operator); the type of the first operand must be the user-defined type, and the type of the second operand must be int.

Example

using System;
class Test {
   public static void Main() {
      int i = -1000;
      Console.WriteLine(i >> 3);
   }
}

Output

-125

See Also

C# Operators | CLR 7.8 Shift operators | << Operator


== Operator

For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise. For reference types other than string, == returns true if its two operands refer to the same object. For the string type, == compares the values of the strings.

User-defined value types can overload the == operator (see operator). So can user-defined reference types, although by default == behaves as described above for both predefined and user-defined reference types. If == is overloaded, != must also be overloaded.

Example

using System;
class Test {
   public static void Main() {
   // 1. Numeric equality:
      Console.WriteLine((2 + 2) == 4);
   // 2. Reference equality: two objects, same boxed value
      object s = 1;
      object t = 1;
      Console.WriteLine(s == t);
   // 3. String equality: same string value, different string objects
      string a = "hello";
      string b = new string("hello");
   // compare string values
      Console.WriteLine(a == b);
   // compare string references
      Console.WriteLine((object)a == (object)b);
   }
}

Output

True
False
True
False

See Also

C# Operators | CLR 7.9 Relational operators | != Operator


!= Operator

The inequality operator (!=) returns false if its operands are equal, true otherwise. Inequality operators are predefined for all types, including string and object. User-defined types can overload the != operator.

For predefined value types, the inequality operator (!=) returns true if the values of its operands are different, false otherwise. For reference types other than string, != returns true if its two operands refer to different objects. For the string type, != compares the values of the strings.

User-defined value types can overload the != operator (see operator). So can user-defined reference types, although by default != behaves as described above for both predefined and user-defined reference types. If != is overloaded, == must also be overloaded.

Example

using System;
class Test {
   public static void Main() {
   // 1. Numeric inequality:
      Console.WriteLine((2 + 2) != 4);
   // 2. Reference equality: two objects, same boxed value
      object s = 1;
      object t = 1;
      Console.WriteLine(s != t);
   // 3. String equality: same string value, same string objects
      string a = "hello";
      string b = "hello";
   // compare string values
      Console.WriteLine(a != b);
   // compare string references
      Console.WriteLine((object)a != (object)b);
   }
}

Output

False
True
False
False

See Also

C# Operators | CLR 7.9 Relational operators | == Operator


<= Operator

All numeric and enumeration types define a "less than or equal" relational operator (<=) that returns true if the first operand is less than or equal to the second, false otherwise.

User-defined types can overload the <= operator (see operator). If <= is overloaded, >= must also be overloaded.

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(1 <= 1.1);
      Console.WriteLine(1.1 <= 1.1);
   }
}

Output

True
True

See Also

C# Operators | CLR 7.9 Relational operators


>= Operator

All numeric and enumeration types define a "greater than or equal" relational operator (>=) that returns true if the first operand is greater than or equal to the second, false otherwise.

User-defined types can overload the >= operator (see operator). If >= is overloaded, <= must also be overloaded.

Example

using System;
class Test {
   public static void Main() {
      Console.WriteLine(1.1 >= 1);
      Console.WriteLine(1.1 >= 1.1);
   }
}

Output

True
True

See Also

C# Operators | CLR 7.9 Relational operators


+= Operator

An expression using the += assignment operator, such as

x += y

is equivalent to

x = x + y

except that x is only evaluated once. The meaning of the + operator is dependent on the types of x and y (addition for numeric operands, concatenation for string operands, and so forth).

The += operator cannot be overloaded directly, but user-defined types can overload the + operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 5;
      a += 6;
      Console.WriteLine(a);
      string s = "Micro";
      s += "soft";
      Console.WriteLine(s);
   }
}

Output

11
Microsoft

See Also

C# Operators | CLR 7.13 Assignment operators


-= Operator

An expression using the -= assignment operator, such as

x -= y

is equivalent to

x = x - y

except that x is only evaluated once. The meaning of the - operator is dependent on the types of x and y (subtraction for numeric operands, delegate removal for delegate operands, and so forth).

The -= operator cannot be overloaded directly, but user-defined types can overload the - operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 5;
      a -= 6;
      Console.WriteLine(a);
   }
}

Output

-1

See Also

C# Operators | CLR 7.13 Assignment operators


*= Operator

An expression using the *= assignment operator, such as

x *= y

is equivalent to

x = x * y

except that x is only evaluated once. The * operator is predefined for numeric types to perform multiplication.

The *= operator cannot be overloaded directly, but user-defined types can overload the * operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 5;
      a *= 6;
      Console.WriteLine(a);
   }
}

Output

30

See Also

C# Operators | CLR 7.13 Assignment operators


/= Operator

An expression using the /= assignment operator, such as

x /= y

is equivalent to

x = x / y

except that x is only evaluated once. The / operator is predefined for numeric types to perform division.

The /= operator cannot be overloaded directly, but user-defined types can overload the / operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 5;
      a /= 6;
      Console.WriteLine(a);
      double b = 5;
      b /= 6;
      Console.WriteLine(b);
   }
}

Output

0
0.83333333333333337

See Also

C# Operators | CLR 7.13 Assignment operators


%= Operator

An expression using the %= assignment operator, such as

x %= y

is equivalent to

x = x % y

except that x is only evaluated once. The % operator is predefined for numeric types to compute the remainder after division.

The %= operator cannot be overloaded directly, but user-defined types can overload the % operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 5;
      a %= 3;
      Console.WriteLine(a);
   }
}

Output

2

See Also

C# Operators | CLR 7.13 Assignment operators


&= Operator

An expression using the &= assignment operator, such as

x &= y

is equivalent to

x = x & y

except that x is only evaluated once. The & operator performs a bitwise AND operation on integral operands and logical AND on bool operands.

The &= operator cannot be overloaded directly, but user-defined types can overload the & operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 0x0c;
      a &= 0x06;
      Console.WriteLine("0x{0:x8}", a);
      bool b = true;
      b &= false;
      Console.WriteLine(b);
   }
}

Output

0x00000004
False

See Also

C# Operators | CLR 7.13 Assignment operators


|= Operator

An expression using the |= assignment operator, such as

x |= y

is equivalent to

x = x | y

except that x is only evaluated once. The | operator performs a bitwise OR operation on integral operands and logical OR on bool operands.

The |= operator cannot be overloaded directly, but user-defined types can overload the | operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 0x0c;
      a |= 0x06;
      Console.WriteLine("0x{0:x8}", a);
      bool b = true;
      b |= false;
      Console.WriteLine(b);
   }
}

Output

0x0000000e
True

See Also

C# Operators | CLR 7.13 Assignment operators


^= Operator

An expression of the form

x ^= y

is evaluated as

x = x ^ y

except that x is only evaluated once. The ^ operator performs a bitwise exclusive-OR operation on integral operands and logical exclusive-OR on bool operands.

The |= operator cannot be overloaded directly, but user-defined types can overload the | operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 0x0c;
      a ^= 0x06;
      Console.WriteLine("0x{0:x8}", a);
      bool b = true;
      b ^= false;
      Console.WriteLine(b);
   }
}

Output

0x0000000a
True

See Also

C# Operators | CLR 7.13 Assignment operators


<<= Operator

An expression of the form

x <<= y

is evaluated as

x = x << y

except that x is only evaluated once. The << operator shifts x left by an amount specified by y.

The <<= operator cannot be overloaded directly, but user-defined types can overload the << operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 1000;
      a <<= 4;
      Console.WriteLine(a);
   }
}

Output

16000

See Also

C# Operators | CLR 7.13 Assignment operators


>>= Operator

An expression of the form

x >>= y

is evaluated as

x = x >> y

except that x is only evaluated once. The >> operator shifts x right by an amount specified by y.

The >>= operator cannot be overloaded directly, but user-defined types can overload the >> operator (see operator).

Example

using System;
class Test {
   public static void Main() {
      int a = 1000;
      a >>= 4;
      Console.WriteLine(a);
   }
}

Output

62

See Also

C# Operators | CLR 7.13 Assignment operators


-> Operator

The -> operator combines pointer dereferencing and member access.

An expression of the form

x->y

(where x is a pointer of type T* and y is a member of T) is equivalent to

(*x).y

The -> operator can be used only in unsafe code.

The -> operator cannot be overloaded.

Example

using System;
struct Point { public int x, y; }
class Test {
   public static void Main() {
      Point pt = new Point();
      unsafe {
         Point* pp = &pt;
         pp->x = 123;
         pp->y = 456;
      }
      Console.WriteLine ( "{0} {1}", pt.x, pt.y );
   }
}

Output

123 456

See Also

C# Operators


Operator Keywords

[This is preliminary documentation and subject to change]

This section introduces the following keywords:
  • as Convert an object to a compatible type.
  • is Check the run-time type of an object.
  • new
    • new operator Create objects.
    • new modifier Hide an inherited member.
  • sizeof Obtain the size of a type.
  • typeof Obtain the System.Type object for a type.
  • true Return the boolean value true.
  • false Return the boolean value false.
  • stackalloc Allocates a local array on the stack.

The following keywords, which can be used as operators and as statements, are covered in the Statements section:

  • checked Specify checked context.
  • unchecked Specify unchecked context.

See Also

C# Keywords


as

[This is preliminary documentation and subject to change]

The as operator is used to perform conversions between compatible types. The as operator is used in an expression of the form:

expression as type

where:

expression
An expression of a reference type.
type
A reference type.

Remarks

The as operator is like a cast except that it yields null on conversion failure instead of raising an exception. More formally, an expression of the form:

expression as type

is equivalent to:

expression is type ? (type)expression : (type)null

except that expression is evaluated only once.

Example

// The as operator
using System;
class MyClass1 {}
class MyClass2 {}
public class IsTest {
   public static void Main() {
      object [] myObjects = new object[6];
      myObjects[0] = new MyClass1();
      myObjects[1] = new MyClass2();
      myObjects[2] = "hello";
      myObjects[3] = 123;
      myObjects[4] = 123.4;
      myObjects[5] = null;
      for (int i=0; i<myObjects.Length; ++i) {
         string s = myObjects[i] as string;
         Console.Write ("{0}:", i);
         if (s != null)
            Console.WriteLine ( "'" + s + "'" );
         else
            Console.WriteLine ( "not a string" );
      }
   }
}

Output

0:not a string
1:not a string
2:'hello'
3:not a string
4:not a string
5:not a string

See Also

C# Keywords | is | ?: | Operator Keywords


is

[This is preliminary documentation and subject to change]

The is operator is used to check whether the run-time type of an object is compatible with a given type. The is operator is used in an expression of the form:

expression is type
where:
expression
An expression of a reference type.
type
A type.

Remarks

An is expression evaluates to true if both of the following conditions are met:

  • expression is not null.
  • expression can be cast to type. That is, a cast expression of the form (type)(expression) will complete without throwing an exception.

A compile-time warning will be issued if the expression expression is type is known to always be true or always be false.

The is operator cannot be overloaded.

Example

// The is operator
using System;
class Class1 {}
class Class2 {}
public class IsTest {
   public static void Test (object o) {
      Class1   a;
      Class2   b;

      if (o is Class1) {
         Console.WriteLine ("o is Class1");
         a = (Class1)o;
         // do something with a
      } else if (o is Class2) {
         Console.WriteLine ("o is Class2");
         b = (Class2)o;
         // do something with b
      } else {
         Console.WriteLine ("o is neither Class1 nor Class2.");
      }
   }
   public static void Main() {
      Class1 c1 = new Class1();
      Class2 c2 = new Class2();
      Test (c1);
      Test (c2);
      Test ("a string");
   }
}

Output

o is Class1
o is Class2
o is neither Class1 nor Class2.

See Also

C# Keywords | typeof | as | Operator Keywords


new

[This is preliminary documentation and subject to change]

In C#, the new keyword can be used as an operator or as a modifier.
  • new operator Used to create objects on the heap and invoke constructors.
  • new modifier Used to hide an inherited member from a base class member.

new Operator

The new operator is used to create objects and invoke constructors, for example:

Class1 MyClass  = new Class1();

It is also used to invoke the default constructor for value types, for example:

int myInt = new int();

Remarks

Remember that it is an error to declare a default constructor for a struct, because every value type implicitly has a public default constructor. It is possible though to declare parameterized constructors on a struct type.

Value-type objects such as structs are created on the stack, while reference-type objects such as classes are created on the heap.

The new operator cannot be overloaded.

Example

In the following example, a struct object and a class object are created and initialized by using the new operator and then assigned values. The default and the assigned values are displayed

// The new operator
using System;
class NewTest {
   struct MyStruct {
      public int x;
      public int y;
      public MyStruct (int x, int y) {
         this.x = x;
         this.y = y;
      }
   }
   class MyClass {
      public string name;
      public int id;
      public MyClass () {}
      public MyClass (int id, string name) {
         this.id = id;
         this.name = name;
      }
   }
   public static void Main() {
      // Create objects using default constructors:
      MyStruct Location1 = new MyStruct();
      MyClass Employee1 = new MyClass();
      // Display values:
      Console.WriteLine("Default values:");
      Console.WriteLine("\tStruct members: {0}, {1}",
         Location1.x, Location1.y);
      Console.WriteLine("\tClass members: {0}, {1}",
         Employee1.name, Employee1.id);
      // Create objects using parameterized constructors::
      MyStruct Location2 = new MyStruct(10, 20);
      MyClass Employee2 = new MyClass(1234, "John Martin Smith");
      // Display values:
      Console.WriteLine("Assigned values:");
      Console.WriteLine("\tStruct members: {0}, {1}",
         Location2.x, Location2.y);
      Console.WriteLine("\tClass members: {0}, {1}",
         Employee2.name, Employee2.id);
   }
}

Output

Default values:
        Struct members: 0, 0
        Class members: , 0
Assigned values:
        Struct members: 10, 20
        Class members: John Martin Smith, 1234

Notice in the example, that the default value of a string is null. Therefore, it was not displayed.

new Modifier

Use the new modifier to explicitly hide a member inherited from a base class. To hide an inherited member, declare it in the derived class using the same name, and modify it with the new modifier.

Consider the following class:

public class MyBaseC {
   public int x;
   public void Invoke();
}

Declaring a member with the name M in a derived class, for example:

public class MyDerivedC : MyBaseC {
   new public void Invoke();
}

will hide the method Invoke() in the base class. However, the field x will not be affected because it is not hidden by a similar name.

Name hiding through inheritance takes one of the following forms:

  • A constant, field, property, or type introduced in a class or struct hides all base class members with the same name.
  • A method introduced in a class or struct hides properties, fields, and types, with the same name, in the base class. It also hides all base class methods with the same signature.
  • An indexer introduced in a class or struct hides all base class indexers with the same signature.
  • It is an error to use both new and override on the same member.
  • Using new and virtual together guarantees a new point of specialization.
  • Using the new modifier in a declaration that does not hide an inherited member generates a warning.

For more information on hiding names, see Name Hiding in the Language Reference.

For more information on fully qualified names, see Fully Qualified Names in the Language Reference.

Example

In this example a base class, MyBaseC, and a derived class, MyDerivedC, use the same field name x, thus hiding the value of the inherited field. The example demonstrates the use of the new modifier. It also demonstrates how to access the hidden members of the base class by using the fully qualified names.

// The new modifier
using System;
public class MyBaseC {
   public static int x = 55;
   public static int y = 22;
}
public class MyDerivedC : MyBaseC {
   new public static int x = 100;   // Name hiding
   public static void Main() {
      // Display the overlapping value of x:
      Console.WriteLine(x);
      // Access the hidden value of x:
      Console.WriteLine(MyBaseC.x);
      // Display the unhidden member y:
      Console.WriteLine(y);
   }
}

Output

100
55
22

If you remove the new modifier, the program will still compile and run, but you will get the warning:

SC0108: The keyword new is required on 'MyDerivedC.x' because it hides inherited member 'MyBaseC.x'.

You can also use the new modifier to modify a nested type if the nested type is hiding another type, as demonstrated in the following example.

Example

In this example, a nested class, MyClass, hides a class with the same name in the base class. The example demonstrates using the new modifier to eliminate the warning message, as well as accessing the hidden class members by using the fully qualified names.

// Using the new modifier with nested types
using System;
public class MyBaseC {
   public class MyClass {
      public int x = 200;
      public int y;
   }
}
public class MyDerivedC : MyBaseC {
   new public class MyClass {   // nested type hiding the base type members
      public int x = 100;
      public int y;
      public int z;
   }
   public static void Main() {
      // Creating object from the overlapping class:
      MyClass S1  = new MyClass();
      // Creating object from the hidden class:
      MyBaseC.MyClass S2 = new MyBaseC.MyClass();
      Console.WriteLine(S1.x);
      Console.WriteLine(S2.x);
   }
}

Output

100
200

See Also

C# Keywords | Operator Keywords | Modifiers


sizeof

[This is preliminary documentation and subject to change]

The sizeof operator is used to obtain the size in bytes for a value type. A sizeof expression takes the form:

sizeof(type)

where:

type
The value type for which the size is obtained.

Remarks

The sizeof operator can be applied only to value types, not reference types.

The sizeof operator can only be used in the unsafe mode. That is, in an unsafe member or inside an unsafe block.

The sizeof operator cannot be overloaded.

Example

// Using the sizeof operator
using System;
class SizeClass {
   // Notice the unsafe declaration of the method:
   unsafe public static void SizesOf() {
      Console.WriteLine("The size of short is {0}.", sizeof(short));
      Console.WriteLine("The size of int is {0}.", sizeof(int));
      Console.WriteLine("The size of long is {0}.", sizeof(long));
   }
}
class MainClass {
   public static void Main() {
   SizeClass.SizesOf();
   }
}

Output

The size of short is 2.
The size of int is 4.
The size of long is 8.

See Also

C# Keywords | Operator Keywords


typeof

[This is preliminary documentation and subject to change]

The typeof operator is used to obtain the System.Type object for a type. A typeof expression takes the form:

typeof(type)

where:

type
The type for which the System.Type object is obtained.

Remarks

The typeof operator cannot be overloaded.

To obtain the run-time type of an expression, you can use the NGWS Framework method GetType.

Example

using System;
using System.Reflection;

public class MyClass {
   public int intI;
   public void MyMeth() {}

   public static void Main() {
      Type t = typeof(MyClass);

      // alternatively, you could use
      // MyClass t1 = new MyClass();
      // Type t = t1.GetType();

      MethodInfo[] x = t.GetMethods();
      foreach (MethodInfo xtemp in x) {
         Console.WriteLine(xtemp.ToString());
      }

      Console.WriteLine();

      MemberInfo[] x2 = t.GetMembers();
      foreach (MemberInfo xtemp2 in x2) {
         Console.WriteLine(xtemp2.ToString());
      }
   }
}

Output

Int32 GetHashCode ()
Boolean Equals (System.Object)
System.String ToString ()
Void MyMeth ()
Void Main ()
System.Type GetType ()

Int32 intI
Int32 GetHashCode ()
Boolean Equals (System.Object)
System.String ToString ()
Void MyMeth ()
Void Main ()
System.Type GetType ()
Void .ctor ()

Example

// Using GetType method
using System;
class GetTypeTest {
   public static void Main() {
      int radius = 3;
      Console.WriteLine("Area = {0} ", radius*radius*Math.PI);
      Console.WriteLine("The type is {0}",
               (radius*radius*Math.PI).GetType());
   }
}

Output

Area = 28.274333882308138
The type is Double

See Also

C# Keywords | is | Operator Keywords


true

[This is preliminary documentation and subject to change]

In C#, the true keyword can be used as an overloaded operator or as a literal:
  • true Operator
  • true Literal

true Operator

User-defined types can define a true operator that returns the bool value true to indicate true and returns false otherwise. This is useful for types that represent true, false, and null (neither true nor false), as used in databases.

Such types can be used for the controlling expression in if, do, while, and for statements and in conditional expressions.

If a type defines operator true, it must also define operator false.

A type cannot directly overload the conditional logical operators (&& and ||), but an equivalent effect can be achieved by overloading the regular logical operators and operators true and false (see CLR 7.11.2 User-defined conditional logical operators).

Example

See the example in CLR 11.3.2 Database boolean type.

true Literal

The true keyword is a literal of type bool representing the boolean value true.

Example

using System;
class test {
   public static void Main() {
      bool a = true;
      Console.WriteLine( a ? "yes" : "no" );
   }
}

Output

yes

See Also

C# Keywords | C# Operators | false


false

[This is preliminary documentation and subject to change]

In C#, the false keyword can be used as an overloaded operator or as a literal:
  • false Operator
  • false Literal

false Operator

User-defined types can define a false operator that returns the bool value true to indicate false and returns false otherwise. This is useful for types that represent true, false, and null (neither true nor false), as used in databases.

Such types can be used for the controlling expression in if, do, while, and for statements and in conditional expressions.

If a type defines operator false, it must also define operator true.

A type cannot directly overload the conditional logical operators (&& and ||), but an equivalent effect can be achieved by overloading the regular logical operators and operators true and false (see CLR 7.11.2 User-defined conditional logical operators).

Example

See the example in CLR 11.3.2 Database boolean type.

false Literal

The false keyword is a literal of type bool representing the boolean value false.

Example

using System;
class test {
   public static void Main() {
      bool a = false;
      Console.WriteLine( a ? "yes" : "no" );
   }
}

Output

no

See Also

C# Keywords | C# Operators | true


stackalloc

[This is preliminary documentation and subject to change]

Allocates a local array on the stack.

type * ptr = stackalloc type [ expr ];

where:

type
An unmanaged type.
ptr
A pointer name.
expr
An integral expression.

Remarks

An array of expr elements of type type is allocated on the stack, not the heap; the address of the array is stored in pointer ptr. Such an array is not subject to garbage collection and therefore does not have to be pinned (via fixed). The lifetime of the array is limited to the lifetime of the function member in which it is defined.

stackalloc is only valid in local variable initializers.

Because pointers are involved, stackalloc requires unsafe context.

stackalloc is similar to _alloca in the C run-time library.

Example

using System;   // compile with /unsafe option
class Test {
   public static unsafe void Main() {
      int* fib = stackalloc int[100];
      int* p = fib;
      *p++ = *p++ = 1;
      for (int i=2; i<100; ++i, ++p) *p = p[-1] + p[-2];
      for (int i=0; i<10; ++i) Console.WriteLine (fib[i]);
   }
}

Output

1
1
2
3
5
8
13
21
34
55

See Also

C# Keywords | Operator Keywords


C# Preprocessor Directives

This section discusses the C# language's preprocessor directives:

  • #if
  • #else
  • #elif
  • #endif
  • #define
  • #undef
  • #warning
  • #error
  • #line
  • #region
  • #endregion

While the compiler does not have a separate preprocessor, the directives described in this section are processed as if there was one; these directives are used to aid in conditional compilation. Unlike C and C++ directives, you cannot use these directives to create macros.

A preprocessor directive must be the only instruction on a line.

For more information, see Preprocessing in the language reference.

See Also

C# Programmer's Reference


#if

#if lets you begin a conditional directive, testing a symbol or symbols to see if they evaluate to true. If they do evaluate to true, the compiler evaluates all the code between the #if and the next directive.

#if symbol [operator symbol]...

where:

symbol
The name of the symbol you want to test. You can also use true and false. symbol can be prefaced with the negation operator. For example, !true will evaluate to false.
operator (Optional)
You can use the following operators to evaluate multiple symbols:

== (equality)

!= (inequality)

&& (and)

|| (or)

You can group symbols and operators with parentheses.

Remarks

#if, along with the #else, #elif, #endif, #define, and #undef directives, lets you include or exclude code based on the condition of one or more symbols. This can be most useful when compiling code for a debug build or when compiling for a specific configuration.

A conditional directive beginning with a #if directive must explicitly be terminated with a #endif directive.

Example

#define DEBUG
#define VC_V6
using System;
public class MyClass {
   public static void Main() {

      #if (DEBUG && !VC_V6)
         Console.WriteLine("DEBUG is defined");
      #elif (!DEBUG && VC_V6)
         Console.WriteLine("VC_V6 is defined");
      #elif (DEBUG && VC_V6)
         Console.WriteLine("DEBUG and VC_V6 are defined");
      #else
         Console.WriteLine("DEBUG and VC_V6 are not defined");
      #endif
   }
}

Output

DEBUG and VC_V6 are defined

See Also

C# Preprocessor Directives


#else

#else lets you create a compound conditional directive, such that, if none of the expressions in the preceding #if or (optional) #elif directives did not evaluate to true, the compiler will evaluate all code between #else and the subsequent #endif.

#else

Remarks

#endif must be the next preprocessor directive after #else.

Example

See #if for an example of how to use #else.

See Also

C# Preprocessor Directives


#elif

#elif lets you create a compound conditional directive. The #elif expression will be evaluated if neither the preceding #if nor any preceding (optional) #elif directive expressions evaluate to true. If a #elif expression evaluates to true, the compiler evaluates all the code between the #elif and the next directive.

#elif symbol [operator symbol]...

where:

symbol
The name of the symbol you want to test. You can also use true and false. symbol can be prefaced with the negation operator. For example, !true will evaluate to false.
operator (Optional)
You can use the following operators to evaluate multiple symbols:

== (equality)

!= (inequality)

&& (and)

|| (or)

You can group symbols and operators with parentheses.

Remarks

#elif is equivalent to using:

#else
#if

But using #elif is simpler because each #if requires a #endif, whereas a #elif can be used without a matching #endif.

Example

See #if for an example of how to use #elif.

See Also

C# Preprocessor Directives


#endif

#endif specifies the end of a conditional directive, which began with the #if directive.

#endif

Remarks

A conditional directive, beginning with a #if directive, must explicitly be terminated with a #endif directive.

Example

See #if for an example of how to use #endif.

See Also

C# Preprocessor Directives


#define

#define lets you define a symbol, such that, by using the symbol as the expression passed to the #if directive, the expression will evaluate to true.

#define symbol

where:

symbol
The name of the symbol to define.

Remarks

Symbols can be used to specify conditions for compilation. You can test for the symbol with either #if or #elif. You can also use the conditional attribute to perform conditional compilation.

You can define a symbol, but you cannot assign a value to a symbol. The #define directive must appear in the file before you use any instructions that are not also directives.

You can also define a symbol with the /define compiler option. You can undefine a symbol with #undef.

A symbol that you define with /define or with #define does not conflict with a variable of the same name. That is, a variable name should not be passed to a preprocessor directive and a symbol can only be evaluated by a preprocessor directive.

The scope of a symbol created with #define is the file in which it was defined.

Example

See #if for an example of how to use #define.

See Also

C# Preprocessor Directives


#undef

#undef lets you undefine a symbol, such that, by using the symbol as the expression in a #if directive, the expression will evaluate to false.

#undef symbol

where:

symbol
The name of the symbol you want to undefine.

Remarks

A symbol can be defined either with the #define directive or the /define compiler option. The #undef directive must appear in the file before you use any statements that are not also directives.

Example

// compile with /D:DEBUG
#undef DEBUG
using System;
public class MyClass {
   public static void Main() {
      #if DEBUG
         Console.WriteLine("DEBUG is defined");
      #else
         Console.WriteLine("DEBUG is not defined");
      #endif
   }
}

Output

DEBUG is not defined

See Also

C# Preprocessor Directives


#warning

#warning lets you generate a level one warning from a specific location in your code.

#warning text

where:

text
The text of the warning that should appear in the compiler's output.

Remarks

A common use of #warning is in a conditional directive. It is also possible to generate a user-defined error with #error.

Example

#define DEBUG
public class MyClass {
   public static void Main() {
      #if DEBUG
      #warning DEBUG is defined
   #endif
   }
}

See Also

C# Preprocessor Directives


#error

#error lets you generate an error from a specific location in your code.

#error text

where:

text
The text of the error that should appear in the compiler's output.

Remarks

A common use of #error is in a conditional directive. It is also possible to generate a user-defined warning with #warning.

Example

#define DEBUG
public class MyClass {
   public static void Main() {
      #if DEBUG
      #error DEBUG is defined
   #endif
   }
}

See Also

C# Preprocessor Directives


#line

#line lets you modify the compiler’s line number and (optionally) the file name output for errors and warnings.

#line number "file_name"

where:

number
The number you want to specify for a line in a source code file.
"file_name" (optional)
The file name you want to appear in the compiler output. By default, the actual name of the source code file is used. The file name must be in quotes.

Remarks

#line might be used by an automated, intermediate step in the build process. For example, if the intermediate step removed lines from the original source code file, but if you still wanted the compiler to generate output based on the original line numbering in the file, you could remove lines and then simulate the original line numbering with #line.

A source code file can have any number of #line directives.

Example

using System;
public class MyClass {
   public static void Main() {
      #line 100 "abc.sc"   // change file name in the compiler output
      intt i;   // error will be reported on line 101
   }
}

See Also

C# Preprocessor Directives


#region

#region lets you specify a block of code that you can expand or collapse when using the outlining feature of the Visual Studio Code Editor.

#region name

where:

name
The name you want to give to the region, which will appear in the Visual Studio Code Editor.

Remarks

A #region block must be terminated with a #endregion directive.

A #region block cannot overlap with a #if block. However, a #region block can be nested in a #if block, and a #if block can be nested in a #region block.

Example

#region MyClass definition
public class MyClass {
   public static void Main() {
   }
}
#endregion

See Also

C# Preprocessor Directives


#endregion

#endregion marks the end of a #region block.

#endregion

Example

See #region for an example of how to use #endregion.

See Also

C# Preprocessor Directives


Properties

Properties are named members of classes, structs, and interfaces. They provide a flexible mechanism to read, write, or compute the values of private fields through accessors.

In this section, the following topics are introduced:

  • Property Declaration
  • Accessors
  • Interface Properties

See Also

Indexers


Property Declaration

Properties are an extension of fields and are accessed using the same syntax. Unlike fields, properties do not designate storage locations. Instead, properties have accessors that read, write, or compute their values.

Property declaration takes one of the following forms:

[attributes] [modifiers] type identifier {accessor declaration}
[attributes] [modifiers] type interface-type.identifier {accessor declaration}

where:

attributes (Optional)
Additional declarative information. For more information on attributes and attribute classes, see C# Attributes.
modifiers (Optional)
The allowed modifiers are new, static, virtual, abstract, override, and a valid combination of the four access modifiers.
type
The property type, which must be at least as accessible as the property itself. For more information on accessibility levels, see Access Modifiers.
identifier
The property name. For more information on interface member implementation, see interface.
accessor-declaration
Declaration of the property accessors, which are used to read and write the property.
interface-type
The interface in a fully-qualified property name. See Interface Properties.

Remarks

  • Unlike fields, properties are not classified as variables. Therefore, it is not possible to pass a property as a ref or out parameter.
  • A property declared using the static modifier is classified as a static property; otherwise, it is classified as an instance property. Like other static members, a static property is not associated with a specific instance and cannot be referenced through an instance. Instead, it is associated with the type and can only be referenced through the type name. For example, in the following statements:
    Button okButton = new Button();
// Using an instance property:
string s = okButton.Caption;

    the Caption property is associated with the instance okButton. If Caption is declared as a static property, the class name (Button) must be used instead:

    // Using a static property:
string s = Button.Caption;
  • An instance of a class can be accessed using this in the accessors of an instance property, but it is an error to use this in the accessors of a static property.
  • It is an error to use a virtual, abstract, or override modifier on an accessor of a static property.

Example

This example demonstrates instance, static, and read-only properties. It accepts the name of the employee from the keyboard, increments numberOfEmployees by 1, and displays the Employee name and number.

// Properties
using System;
public class Employee {
   public static int numberOfEmployees;
   private static int counter;
   private string name;
   // A read-write instance property:
   public string Name {
      get {
         return name;
      }
      set {
         name = value;
      }
   }
   // A read-only static property:
   public static int Counter {
      get {
         return counter;
      }
   }
   // Constructor:
   public Employee() {
      // Calculate the employee’s number:
      counter = ++counter + numberOfEmployees;
   }
}
public class MainClass {
   public static void Main() {
      Employee.numberOfEmployees = 100;
      Employee e1 = new Employee();
      e1.Name = "Claude Vige";
      Console.WriteLine("Employee number: {0}", Employee.Counter);
      Console.WriteLine("Employee name: {0}", e1.Name);
   }
}

Output

Employee number: 101
Employee name: Claude Vige

See Also

Accessors | Indexers | Properties


Accessors

The accessor of a property contains the executable statements associated with getting (reading or computing) or setting (writing) the property. The accessor declarations can contain a get accessor, a set accessor, or both. The declarations take the following forms:

set {accessor-body}
get {accessor-body}

where:

accessor-body
The block that contains the statements to be executed when the accessor is invoked.

The get Accessor

The body of the get accessor is similar to that of a method. It must return a value of the property type. The execution of the get accessor is equivalent to reading the value of the field. The following is a get accessor that returns the value of a private field name:

private string name;  // the name field
public string Name {  // the Name property
   get {
      return name;
   }
}

When you reference the property, except as the target of an assignment, the get accessor is invoked to read the value of the property. For example:

Employee e1 = new Employee();
...
Console.Write(e1.Name);   // The get accessor is invoked here

The get accessor must terminate in a return or throw statement, and control cannot flow off the accessor body.

The set Accessor

The set accessor is similar to a method that returns void. It uses an implicit parameter called value, whose type is the type of the property. In the following example, a set accessor is added to the Name property:

public string Name {
   get {
      return name;
   }
   set {
      name = value;
   }
}

When you assign a value to the property, the set accessor is invoked with an argument that provides the new value. For example:

e1.Name = "Joe";   // The set accessor is invoked here

It is an error to use the implicit parameter name (value) for a local variable declaration in a set accessor.

Remarks

A property is classified according to the accessors used as follows:

  • A property with a get accessor only is called a read-only property. You cannot assign a value to a read-only property.
  • A property with a set accessor only is called a write-only property. You cannot reference a write-only property except as a target of an assignment.
  • A property with both get and set accessors is a read-write property.

In a property declaration, both the get and set accessors must be declared inside the body of the property.

It is a bad programming style to change the state of the object by using the get accessor. For example, the following accessor produces the side effect of changing the state of the object each time the number field is accessed.

public int Number {
   get {
      return number++;   // Don't do this
   }
}

The get accessor can either be used to return the field value or to compute it and return it. For example:

public string Name {
   get {
      return name != null ? name : "NA";
   }
}

In the preceding code segment, if you don't assign a value to the Name property, it will return the value NA.

Example

This example demonstrates how to access a property in a base class that is hidden by another property with the same name in a derived class.

// Property hiding
using System;
public class BaseClass {
   private string name;
   public string Name {
      get {
         return name;
      }
      set {
         name = value;
      }
   }
}
public class DerivedClass : BaseClass {
   private string name;
   public new string Name {   // Notice the use of the new modifier
      get {
         return name;
      }
      set {
         name = value;
      }
   }
}
public class MainClass {
   public static void Main() {
      DerivedClass d1 = new DerivedClass();
      d1.Name = "John";  // Derived class property
      Console.WriteLine("Name in the derived class is: {0}",d1.Name);
      ((BaseClass)d1).Name = "Mary"; // Base class property
      Console.WriteLine("Name in the base class is: {0}",
         ((BaseClass)d1).Name);
   }
}

Output

Name in the derived class is: John
Name in the base class is: Mary

The following are important points shown in the preceding example:

The property Name in the derived class hides the property Name in the base class. In such a case, the new modifier is used in the declaration of the property in the derived class:

   public new string Name {
   ...

The cast (BaseClass) is used to access the hidden property in the base class:

   ((BaseClass)d1).Name = "Mary";

For more information on hiding members, see the new modifier.

Example

In this example two classes, Cube and Square, implement an abstract class, Shape, and override its abstract Area property. Note the use of the override modifier on the properties. The program accepts the side as an input and calculates the areas for the square and cube. It also accepts the area as an input and calculates the corresponding side for the square and cube.

// Overriding properties
using System;
abstract class Shape {
   public abstract double Area {
      get;
      set;
   }
}
class Square: Shape {
      public double side;
      // Constructor:
      public Square(double s) {
         side = s;
      }
   // The Area property
   public override double Area {
      get {
         return side*side ;
      }
      set {
         // Given the area, compute the side
         side = Math.Sqrt(value);
      }
   }
}
class Cube: Shape {
      public double side;
      // Constructor:
      public Cube(double s) {
         side = s;
      }
      // The Area property
      public override double Area {
      get {
         return 6*side*side;
      }
      set {
         // Given the area, compute the side
         side = Math.Sqrt(value/6);
      }
   }
}
public class MainClass {
   public static void Main()  {
   // Input the side:
   Console.Write("Enter the side: ");
   string sideString = Console.ReadLine();
   double side = double.FromString(sideString);
   // Compute areas:
   Square s = new Square(side);
   Cube c = new Cube(side);
   // Display results:
   Console.WriteLine("Area of a square = {0}", double.Format(s.Area, "0.00"));
   Console.WriteLine("Area of a cube = {0}", double.Format(c.Area, "0.00"));
   // Input the area:
   Console.Write("Enter the area: ");
   string areaString = Console.ReadLine();
   double area = double.FromString(areaString);
   // Compute areas:
   s.Area = area;
   c.Area = area;
   // Display results:
   Console.WriteLine("Side of a square = {0}", double.Format(s.side, "0.00"));
   Console.WriteLine("Side of a cube = {0}", double.Format(c.side, "0.00"));
   }
}

Sample Run

Enter the side: 4
Area of a square = 16.00
Area of a cube = 96.00
Enter the area: 24
Side of a square = 4.90
Side of a cube = 2.00

See Also

Properties | virtual | override | abstract | base | Indexers | new modifier


Interface Properties

Properties can be declared on interfaces. The declaration takes the following form:

[attributes] [new] type identifier {interface-accessors}

where:

attributes (Optional)
Same as properties on classes.
type
Same as properties on classes.
identifier
The property name.
interface-accessors
The property accessors. See the Remarks below.

Remarks

The body of the accessor of an interface property consists of a semicolon.

Thus, the purpose of the accessors is to indicate whether the property is read-write, read-only, or write-only.

The following is an example of an interface indexer accessor:

public interface IMyInterface {
   // Property declaration:
   string Name {
      get;
      set;
   }
}

Example

In this example, the interface IEmployee has a read-write property, Name, and a read-only property, Counter. The class Employee implements the IEmployee interface and uses these two properties. The program reads the name of a new employee and the current number of employees and displays the employee name and the computed employee number.

// Interface Properties
using System;
interface IEmployee {
   string Name {
      get;
      set;
   }
   int Counter {
      get;
   }
}
public class Employee: IEmployee {
   public static int numberOfEmployees;
   private int counter;
   private string name;
   // Read-write instance property:
   public string Name {
      get {
         return name;
      }
      set {
         name = value;
      }
   }
   // Read-only instance property:
   public int Counter {
      get {
         return counter;
      }
   }
   // Constructor:
   public Employee() {
      counter = ++counter + numberOfEmployees;
   }
}
public class MainClass {
   public static void Main() {
      Console.Write("Enter number of employees: ");
      string s = Console.ReadLine();
      Employee.numberOfEmployees = int.FromString(s);
      Employee e1 = new Employee();
      Console.Write("Enter the name of the new employee: ");
      e1.Name = Console.ReadLine();
      Console.WriteLine("The employee information:");
      Console.WriteLine("Employee number: {0}", e1.Counter);
      Console.WriteLine("Employee name: {0}", e1.Name);
   }
}

Sample Run

Enter number of employees: 201
Enter the name of the new employee: Vivian Nicola
The employee information:
Employee number: 202
Employee name: Vivian Nicola

In the preceding example, you could use the fully qualified name of the property, which references the interface in which the member is declared. For example:

   public string IEmployee.Name {...}

However, the fully qualified name is only needed to avoid ambiguity when the class is implementing more than one interface with the same property signature. For example, if the class Employee is implementing two interfaces ICitizen and IEmployee and both interfaces have the Name property, the explicit interface member implementation will be necessary. That is, the following property declaration:

   public string IEmployee.Name {...}

implements the Name property on the IEmployee interface, while the following declaration:

   public string ICitizen.Name {...}

implements the Name property on the ICitizen interface.

For more information on Explicit Interface Member Implementation, see § 13.4.1 in the language reference.

See Also

Properties | Accessors | Comparison Between Properties and Indexers


Statements

Statements are program instructions. Except as described, statements are executed in sequence. C# has the following categories of statements.

Category | C# keywords
Selection statements | if, else, switch, case
Iteration statements | do, for, foreach, in, while
Jump statements | break, continue, default, goto, return
Exception handling statements | throw, try-catch, try-finally
Checked and unchecked | checked, unchecked
Unsafe and Fixed | fixed, unsafe
lock Statement | lock

See Also

C# Keywords


Selection Statements

A selection statement causes the program control to be transferred to a specific flow based upon whether a certain condition is true or not.

The following keywords are used in selection statements:

  • if
  • else
  • switch
  • case

See Also

C# Keywords | Statements


if-else

The if statement is a control statement that executes a block of code if an expression evaluates to true. It takes the form:

if (expression)
   statement1
[else
   statement2]
where:
expression
An expression that can be implicitly converted to bool or a type that contains overloading of the true and false operators.
statement1
The embedded statement(s) to be executed if expression is true.
statement2
The embedded statement(s) to be executed if expression is false.

Remarks

If expression is true, statement1 is executed. If the optional else clause exists and expression evaluates to false, statement2 is executed. After executing the if statement, control is transferred to the next statement.

If any of the two results of the if statement (true or false) results in executing more than one statement, multiple statements can be conditionally executed by including them into blocks.

The statement(s) to be executed upon testing the condition can be of any kind, including another if statement nested into the original if statement. In nested if statements, the else clause belongs to the last if that does not have a corresponding else. For example:

if (x > 10)
   if (y > 20)
      Console.Write("Statement_1");
   else
      Console.Write("Statement_2");

In this example, Statement_2 will be displayed if the condition (y > 20) evaluates to false. However, if you want to associate Statement_2 with the condition (x >10), use braces:

if (x > 10) {
   if (y > 20)
      Console.Write("Statement_1");
}
else
   Console.Write("Statement_2");

In this case, Statement_2 will be displayed if the condition (x > 10) evaluates to false

Example

In this example, you enter a character from the keyboard and the program checks if the input character is an alphabetic character. If so, it checks if it is lowercase or uppercase. In each case, the proper message is displayed.

// if-else example
using System;
public class IfTest {
   public static void Main() {
      Console.Write("Enter a character: ");
      char c = (char) Console.Read();
      if (Char.IsLetter(c))
         if (Char.IsLower(c))
            Console.WriteLine("The character is lowercase.");
         else
            Console.WriteLine("The character is uppercase.");
      else
         Console.WriteLine("The character is not an alphabetic character.");
   }
}

Sample Runs

Run #1:

Enter a character: 2
The character is not an alphabetic character.

Run #2:

Enter a character: A
The character is uppercase.

Run #3:

Enter a character: h
The character is lowercase.

It is also possible to extend the if statement to handle multiple conditions using the following else-if arrangement:

if (Condition_1)
   Statement_1;
else if (Condition_2)
   Statement_2;
else if (Condition_3)
   Statement_3;
...
else
   Statement_n;

Example

This example checks if the input character is lowercase, uppercase, or a number. Otherwise, it is not an alphanumeric character. The program makes use of the else-if ladder.

// else-if
using System;
public class IfTest {
   public static void Main() {
      Console.Write("Enter a character: ");
      char c = (char) Console.Read();
      if (Char.IsUpper(c))
         Console.WriteLine("The character is uppercase.");
      else if (Char.IsLower(c))
         Console.WriteLine("The character is lowercase.");
      else if (Char.IsDigit(c))
         Console.WriteLine("The character is a number.");
      else
         Console.WriteLine("The character is not alphanumeric.");
   }
}

Sample Runs

Run #1:

Enter a character: E
The character is uppercase.

Run #2:

Enter a character: e
The character is lowercase.

Run #3:

Enter a character: 4
The character is a number.

Run #4:

Enter a character: $
The character is not alphanumeric.

See Also

C# Keywords | Compare to C++ | switch | Grammar


switch

The switch statement is a control statement that handles multiple selections by passing control to one of the case statements within its body. The switch statement takes the form:

switch (expression)
{
   case constant-expression:
      statement
      jump-statement
   [default: statement]
}
where:
expression
An integral or string type expression.
statement
The embedded statement(s) to be executed if control is transferred to the case or the default.
jump-statement
A jump statement that transfers control out of the case body.
constant-expression
Control is transferred to a specific case according to the value of this expression.

Remarks

Control is transferred to the case statement whose constant-expression matches expression. The switch statement can include any number of case instances, but no two case constants within the same switch statement can have the same value. Execution of the statement body begins at the selected statement and proceeds until the jump-statement transfers control out of the case body. Only the last block does not require a jump-statement whether this block is a case, or a default statement.

Unlike the C++ switch statement, C# does not support an explicit fall through from one case label to another. If you want, you can use goto a switch-case, or goto default.

If expression does not match any constant-expression, control is transferred to the statement(s) that follow the optional default label. If there is no default label, control is transferred outside the switch.

Example

using System;
using System.IO;
class SwitchTest {
   public static void Main()  {
      Console.WriteLine("Coffee sizes: 1=Small 2=Medium 3=Large");
      Console.Write("Please enter your selection: ");
      string s = Console.ReadLine();
      int n = int.Parse(s);
      switch(n) {
         case 0:
            goto case 1;
         case 1:
            Console.WriteLine("You selected small size. Insert 50 cents.");
            break;
         case 2:
            Console.WriteLine("You selected medium size. Insert 75 cents.");
            break;
         case 3:
            Console.WriteLine("You selected large size. Insert $1.00.");
            break;
         default:
            Console.WriteLine("Invalid selection. Please select 1, 2, or 3.");
      }
      Console.WriteLine("Thank you for your business.");
   }
}

Sample Run

Coffee sizes: 1=Small 2=Medium 3=Large
Please enter your selection: 2
You selected medium size. Insert 75 cents.
Thank you for your business.

In the preceding example, an integral type variable, n, was used for the switch cases. Notice that you can also use the string variable, s, directly. In this case, you can use switch cases like these:

switch(s) {
   case "1":
      ...
   case "2":
      ...
}

See Also

C# Keywords | Compare to C++ | if | Grammar


Iteration Statements

You can create loops by using the iteration statements. Iteration statements cause embedded statements to be executed a number of times, subject to the loop termination criteria. These statements are executed in order, except when a jump statement is encountered.

The following keywords are used in iteration statements:

  • do
  • for
  • foreach
  • in
  • while

See Also

C# Keywords | Statements


do

The do statement executes a statement or a block of statements repeatedly until a specified expression evaluates to false. It takes the following form:

do statement while (expression);

where:

expression
An expression that can be implicitly converted to bool or a type that contains overloading of the true and false operators. The expression is used to test the loop-termination criteria.
statement
The embedded statement(s) to be executed.

Remarks

Unlike the while statement, the body loop of the do statement is executed at least once regardless of the value of the expression.

Example

using System;
public class TestDoWhile {
   public static void Main () {
   int x;
   int y = 0;
   do {
      x = y++;
      Console.WriteLine(x);}
   while(y < 5);
   }
}

Output

0
1
2
3
4

Example

Notice in this example that although the condition evaluates to false, the loop will be executed once.

using System;
class DoTest {
   public static void Main() {
      int n = 10;
      do {
         Console.WriteLine("Current value of n is {0}", n);
         n++;
      } while (n < 6);
   }
}

Output

Current value of n is 10

See Also

C# Keywords | Compare to C++ | Iteration Statements | Grammar


for

The for loop executes a statement or a block of statements repeatedly until a specified expression evaluates to false. It takes the following form:

for ([initializers]; [expression]; [iterators]) statement

where:

initializers
A comma separated list of expressions or assignment statements to initialize the loop counters.
expression
An expression that can be implicitly converted to bool or a type that contains overloading of the true and false operators. The expression is used to test the loop-termination criteria.
iterators
Expression statement(s) to increment or decrement the loop counters.
statement
The embedded statement(s) to executed.

Remarks

The for statement executes the statement repeatedly as follows:

  • First, the initializers are evaluated.
  • Then, while the expression evaluates to true, the statement(s) are executed and the iterators are evaluated.
  • When the expression becomes false, control is transferred outside the loop.

Because the test of expression takes place before the execution of the loop, a for statement executes zero or more times.

All the expressions of the for statement are optional, for example, the following statement is used to write an infinite loop:

for (;;) {
   ...
}

Example

// for loop
using System;
public class ForLoopTest {
   public static void Main() {
      for (int i = 1; i <= 5; i++)
         Console.WriteLine(i);
   }
}

Output

1
2
3
4
5

See Also

C# Keywords | Compare to C++ | Iteration Statements | Grammar


foreach, in

The foreach statement repeats a group of embedded statements for each element in an array or an object collection. The foreach statement is used to iterate through the collection to get the desired information, but should not be used to change the contents of the collection to avoid unpredictable side effects. The statement takes the following form:

foreach (type identifier in expression) statement

where:

type
The type of identifier.
identifier
The iteration variable that represents the collection element.
expression
Object collection or array expression. The type of the collection element must be convertible to the identifier type.
statement
The embedded statement(s) to be executed.

Remarks

The embedded statements continue to execute for each element in the array or collection. After the iteration has been completed for all the elements in the collection, control is transferred to the next statement following the foreach block.

In this section, the following topics are discussed in detail:

  • Using foreach with Arrays
  • Using foreach with Collections

Using foreach with Arrays

When used with an array, the foreach statement repeats the embedded statement(s) for each element in the array.

Example

In this example, an array of integers is searched for even and odd numbers. A counter for each type of number stores the number of occurrences.

// Using foreach with arrays
using System;
class MainClass {
   public static void Main() {
      int odd = 0, even = 0;
      int[] arr = new int [] {0,1,2,5,7,8,11};
      foreach (int i in arr) {
         if (i%2 == 0)
            even++;
         else
            odd++;
      }
      Console.WriteLine("Found {0} Odd Numbers, and {1} Even Numbers.",
                        odd, even) ;
   }
}

Output

Found 4 Odd Numbers, and 3 Even Numbers.

Using foreach with Collections

To iterate through a collection, the collection must meet specific requirements. For example, in the following foreach statement:

   foreach (ItemType item in myCollection)

myCollection must meet the following requirements:

  • The collection type:
    • Must be one of the types: interface, class, or struct.
    • Must include an instance method named GetEnumerator that returns a type, for example, Enumerator (explained below).
  • The type Enumerator (a class or struct) must contain:
    • A property named Current that returns ItemType or a type that can be converted to it. The property accessor returns the current element of the collection.
    • A bool method, named MoveNext, that increments the item counter and returns true if there are more items in the collection.

There are three approaches to using collections:

  1. Create a collection using the above instructions. This collection can only be used in a C# program.
  2. Create a generic collection using the above instructions, in addition to implementing the IEnumerable interface. This collection can be used from other languages such as Visual Basic.
  3. Use one of the predefined collections in the collection classes.

The following examples demonstrate the three approaches.

Example | Demonstrates | Comment
Example 1 | A C#-specific collection. | This example creates a collection by using the instructions above.
Example 2 | A generic collection. | This example creates a collection by using the instructions above and implements the IEnumerable and IEnumerator interfaces.
Example 3 | Using one of the predefined collection classes. | This example creates a Hashtable instance and uses the Hashtable class members to manipulate the collection. The Hashtable class represents a dictionary of associated keys and values, implemented as a hash table.

For more information on the interfaces IEnumerator and IEnumerable, and the Hashtable class, see the System.Collections section in the NGWS SDK.

Example 1

// Using foreach with C#-specific collections:
using System;
// Declare the collection:
public class MyCollection {
   int[] items;
   public MyCollection() {
      items = new int[5] {12, 44, 33, 2, 50};
   }
   public MyEnumerator GetEnumerator() {
      return new MyEnumerator(this);
   }
   // Declare the enumerator class:
   public class MyEnumerator {
      int nIndex;
      MyCollection collection;
      public MyEnumerator(MyCollection coll) {
         collection = coll;
         nIndex = -1;
      }
      public bool MoveNext() {
         nIndex++;
         return(nIndex < collection.items.GetLength(0));
      }
      public int Current {
         get {
            return(collection.items[nIndex]);
         }
      }
   }
}
public class MainClass {
   public static void Main() {
      MyCollection col = new MyCollection();
      Console.WriteLine("Values in the collection are:");
      // Display collection items:
      foreach (int i in col) {
         Console.WriteLine(i);
      }
   }
}

Output

Values in the collection are:
12
44
33
2
50

Example 2

This example repeats the same algorithm of Example 1, but uses a generic collection that can be enumerated from other languages such as Visual Basic. This type of collection must implement the IEnumerable interface from the System.Collections namespace.

// Using foreach with a generic collection
using System;
using System.Collections;
// Declare the collection and implement the IEnumerable interface:
public class MyCollection: IEnumerable {
   int[] items;
   public MyCollection() {
      items = new int[5] {12, 44, 33, 2, 50};
   }
   public MyEnumerator GetEnumerator() {
      return new MyEnumerator(this);
   }
   // Implement the GetEnumerator() method:
   IEnumerator IEnumerable.GetEnumerator() {
      return GetEnumerator();
   }
   // Declare the enumerator and implement the IEnumerator interface:
   public class MyEnumerator: IEnumerator {
      int nIndex;
      MyCollection collection;
      public MyEnumerator(MyCollection coll) {
         collection = coll;
         nIndex = -1;
      }
    public void Reset() {
      nIndex = -1;
    }
      public bool MoveNext() {
         nIndex++;
         return(nIndex < collection.items.GetLength(0));
      }
      public int Current {
         get {
            return(collection.items[nIndex]);
         }
      }
      // The current property on the IEnumerator interface:
      object IEnumerator.Current {
         get {
            return(Current);
         }
      }
   }
}
public class MainClass {
   public static void Main(string [] args) {
      MyCollection col = new MyCollection();
      Console.WriteLine("Values in the collection are:");
      // Display collection items:
      foreach (int i in col) {
         Console.WriteLine(i);
      }
   }
}

Output

Values in the collection are:
12
44
33
2
50

Example 3

In this example, the predefined Hashtable collection class is used. By using the System.Collections namespace in your program, you can have access to the Hashtable class and its members. To add entries to the Hashtable object, use the Add method.

// Using the Hashtable collection class
using System;
using System.Collections;
public class MainClass {
   public static void Main(string [] args) {
      // Declare a Hashtable object:
      Hashtable ziphash = new Hashtable();
      // Add entries using the Add() method:
      ziphash.Add("98008", "Bellevue");
      ziphash.Add("98052", "Redmond");
      ziphash.Add("98201", "Everett");
      ziphash.Add("98101", "Seattle");
      ziphash.Add("98371", "Puyallup");
      // Display contents:
      Console.WriteLine("Zip code\tCity");
      foreach (string zip in ziphash.Keys) {
         Console.WriteLine(zip + "\t\t" + ziphash[zip]);
      }
   }
}

Output

Zip code        City
98201           Everett
98052           Redmond
98101           Seattle
98008           Bellevue
98371           Puyallup

See Also

C# Keywords | Iteration Statements | Grammar | Arrays


while

The while statement executes a statement or a block of statements until a specified expression evaluates to false. It takes the form:

while (expression) statement

where:

expression
An expression that can be implicitly converted to bool or a type that contains overloading of the true and false operators. The expression is used to test the loop-termination criteria.
statement
The embedded statement(s) to be executed.

Remarks

Because the test of expression takes place before each execution of the loop, a while loop executes zero or more times.

A while loop can be terminated when a break, goto, return, or throw statement transfers control outside the loop. To pass control to the next iteration without exiting the loop, use the continue statement.

Example

using System;
class WhileTest {
   public static void Main() {
      int n = 1;
      while (n < 6) {
         Console.WriteLine("Current value of n is {0}", n);
         n++;
      }
   }
}

Output

Current value of n is 1
Current value of n is 2
Current value of n is 3
Current value of n is 4
Current value of n is 5

See Also

C# Keywords | Compare to C++ | Iteration Statements | Grammar


Jump Statements

Branching is performed using jump statements, which cause an immediate transfer of the program control. The following keywords are used in jump statements:

  • break
  • continue
  • default
  • goto
  • return

See Also

C# Keywords | Statements


break

The break statement terminates the closest enclosing loop or conditional statement in which it appears. Control is passed to the statement that follows the terminated statement, if any. The break statement takes the form:

break;

Example

In this example, the conditional statement contains a counter that is supposed to count from 1 to 100; however, the break statement terminates the loop after 4 counts.

using System;
class BreakTest {
   public static void Main() {
      for (int i = 1; i <= 100; i++) {
         if (i == 5)
            break;
         Console.WriteLine(i);
      }
   }
}

Output

1
2
3
4

Example

This example demonstrates the use of break in a switch statement.

// break and switch
using System;
using System.IO;
class Switch {
   public static void Main() {
      Console.Write("Enter your selection (1, 2, or 3): ");
      string s = Console.ReadLine();
      int n = Int32.FromString(s);
      switch(n) {
      case 1:
         Console.WriteLine("Current value is {0}", 1);
         break;
      case 2:
         Console.WriteLine("Current value is {0}", 2);
          break;
      case 3:
         Console.WriteLine("Current value is {0}", 3);
          break;
      default:
         Console.WriteLine("Sorry, invalid selection.");
      }
   }
}

Sample Runs

Run #1:

Enter your selection (1, 2, or 3): 2
Current value is 2

Run #2:

Enter your selection (1, 2, or 3): 4
Sorry, invalid selection.

See Also

C# Keywords | Compare to C++ | switch | Jump Statements | Grammar


continue

The continue statement passes control to the next iteration of the enclosing iteration statement in which it appears. It takes the form:

continue;

Example

In this example, a counter is initialized to count from 1 to 10. By using the continue statement in conjunction with the expression (i < 9), the statements between continue and the end of the for body are skipped.

using System;
class ContinueTest {
   public static void Main() {
      for (int i = 1; i <= 10; i++) {
         if (i < 9)
            continue;
         Console.WriteLine(i);
      }
   }
}

Output

9
10

See Also

C# Keywords | Compare to C++ | Jump Statements | Grammar


goto

The goto statement transfers the program control directly to a labeled statement. It takes one of the following forms:

goto identifier;
goto case constant-expression;
goto default;

where:

identifier
A label.
constant-expression
A switch-case label.

Remarks

In the first form, the identifier indicates a label located in the current body, the same lexical scope, or an enclosing scope of the goto statement.

A common use of goto is to transfer control to a specific switch-case label or the default label in a switch statement.

The goto statement is also useful to get out of deeply nested loops.

A warning message may be issued if the label has never been referenced in the program. For more information on labels, see §3.1 Declarations

Example

For an example of using goto to transfer control to a specific switch-case label, see the switch example.

Example

The following example demonstrates using goto to break out from nested loops.

// Nested search loops
using System;
using System.IO;
public class GotoTest1 {
   public static void Main() {
      int x = 200, y = 4;
      int[,] myArray = new int[x,y];
      // initialize the array:
      for (int i = 0; i < x; i++)
         for (int j = 0; j < y; j++)
            myArray[i,j] = ++i;
      // Read input:
      Console.Write("Enter the number to search for: ");
      string s = Console.ReadLine();   // input a char
      int myNumber = Int32.FromString(s);
      // Search:
      for (int i = 0; i < x; i++)
         for (int j = 0; j < y; j++)
            if (myArray[i,j] == myNumber)
               goto Found;
   NotFound:
      Console.WriteLine("The number {0} was not found.", myNumber);
      goto Finish;
   Found:
      Console.WriteLine("The number {0} is found.", myNumber);
   Finish:
      Console.WriteLine("End of search.");
   }
}

Sample Run

Enter the number to search for: 44
The number 44 is found.
End of search.

Example

// Labels outside the scope
using System;
class UnreachableCode {
   public static void Main() {
      int x = 55;
      Console.WriteLine("x = {0}", x);
      if (x == 55) {
         x = 135;
         goto A;   // Error
      }
         x = x + 1;
      for (int i=1; i<=5; i++) {
   A:      Console.WriteLine(i);
      }
      Console.WriteLine("x = {0}", x);
   }
}

In the preceding example, the goto statement is referencing a label A outside its scope. The compiler will issue the error message 0159:

No such label 'A' within the scope of the goto statement

A warning message may also be issued because the label has never been referenced.

If you move the label A to the beginning of the for loop, the program would compile and run normally, that is:

A:      for (int i=1; i<=5; i++) {   // Now the program compiles.

See Also

C# Keywords | Compare to C++ | Jump Statements | Grammar


return

The return statement terminates execution of the method in which it appears and returns control to the calling method. It can also return the value of the optional expression. If the method is of the type void, the return statement can be omitted. The return statement takes the form:

return [expression];

where:

expression
The value returned by a method. The expression is not used with methods of the type void.

Example

In the following example, the method A() returns the variable Area as a double value.

using System;

class ReturnTest {

static double CalculateArea(int r) {

double area;

area = r*r*Math.PI;

return area;

}

public static void Main() {

int radius = 5;

Console.WriteLine("The area is {0:0.00}", CalculateArea(radius));

}

}

Output

The area is 78.54

See Also

C# Keywords | Compare to C++ | Jump Statements | Grammar


Exception Handling Statements

C# provides built-in support for handling anomalous situations, known as exceptions, which may occur during the execution of your program. These exceptions are handled by code that is outside the normal flow of control. The try, throw, catch, and finally keywords implement exception handling.

The following exception handling topics are explained in this section:

  • throw
  • try-catch
  • try-finally
  • try-catch-finally

See Also

C# Keywords | Statements


throw

The throw statement is used to signal the occurrence of an anomalous situation (exception) during the program execution. The throw statement takes the form:

throw [expression];

where:

expression
The exception object. This is omitted when re-throwing the current exception object in a catch clause.

Remarks

The thrown exception is an object whose class is derived from System.Exception, for example:

class MyException : System.Exception {}
throw new MyException();

Usually the throw statement is used with try-catch or try-finally statements. When an exception is thrown, the program looks for the catch statement that handles this exception.

Example

This example demonstrates how to throw an exception using the throw statement.

// throw example
using System;
public class ThrowTest {
   public static void Main() {
      string s = null;
      if (s == null) {
         throw(new ArgumentNullException());
      }
      Console.Write("The string s is null"); // not executed
   }
}

Output

Caught exception: System.ArgumentNullException: argument can't be null at ThrowTest.Main()

Example

See the try-catch, try-finally and try-catch-finally examples.

See Also

Compare to C++ | C# Keywords | Exception Handling Statements | Grammar


try-catch

The try-catch statement consists of a try block followed by one or more catch clauses, which specify handlers for different exceptions. The try-catch statement takes one of the forms:

try try-block catch (exception-declaration-1) catch-block-1 catch (exception-declaration-2) catch-block-2 ...
try try-block catch catch-block

where:

try-block
Contains the code segment expected to raise the exception.
exception-declaration
The exception object declaration.
catch-block
Contains the exception handler.

Remarks

The try-block contains the guarded code block that may cause the exception. The block is executed until an exception is thrown or it is completed successfully. For example, the following attempt to cast a null object raises the NullReferenceException exception:

object o2 = null;
try {
   int i2 = (int) o2;   // Error
}

The catch clause can be used without arguments, in which case it catches any type of exception, and referred to as the general catch clause. It can also take an object argument derived from System.Exception, in which case it handles a specific exception. For example:

catch (InvalidCastException e) {
   ...
}

It is possible to use more than one specific catch clause in the same try-catch statement. In this case, the order of the catch clauses is important because the catch clauses are examined in order. Catch the more specific exceptions before the less specific ones.

A throw statement can be used in the catch block to rethrow the exception, which has been caught by the catch statement. For example:

catch (InvalidCastException e) {
   ...
   throw (e);   // Rethrowing exception e
}

If you want to rethrow the exception currently handled by a parameterless catch clause, use the throw statement without arguments. For example:

catch {
   ...
   throw;
}

For more information on catch, see try-catch-finally.

Example

In this example, the try block contains a call to the method MyFn() that may cause an exception. The catch clause contains the exception handler that simply displays a message on the screen. When the throw statement is called from inside MyFn(), the system looks for the catch statement and displays the message Exception caught.

// Rethrowing exceptions:
using System;
class MyClass {
   public static void Main() {
      MyClass x = new MyClass();
      try {
         string s = null;
         x.MyFn(s);
      }
      catch (Exception e) {
         Console.WriteLine("Exception caught.");
      }
   }
   public void MyFn(string s) {
      if (s == null)
         throw(new ArgumentNullException());
   }
}

Output

Exception caught.

Example

In this example, two catch statements are used. The most specific exception, which comes first, is caught.

// Ordering catch clauses
using System;
class MyClass {
   public static void Main() {
      MyClass x = new MyClass();
      try {
         string s = null;
         x.MyFn(s);
      }
      // Most specific:
      catch (ArgumentNullException e) {
         Console.WriteLine("First exception caught.");
      }
      // Least specific:
      catch (Exception e) {
         Console.WriteLine("Second exception caught.");
      }
   }
   public void MyFn(string s) {
      if (s == null)
         throw new ArgumentNullException();
   }
}

Output

First exception caught.

In the preceding example, if you start with the least specific catch clause, you will get the error message:

A previous catch clause already catches all exceptions of this or a super type ('System.Exception')

However, to catch the least specific exception, replace the throw statement by the following one:

throw new Exception();

See Also

C# Keywords | Compare to C++ | Exception Handling Statements | throw | try-finally | Grammar


try-finally

The finally block is useful for cleaning up any resources allocated in the try block. Control is always passed to the finally block regardless of how the try block exits. The try-finally statement takes the form:

try try-block finally finally-block

where:

try-block
Contains the code segment expected to raise the exception.
finally-block
Contains the exception handler and the cleanup code.

Remarks

Whereas catch is used to handle exceptions that occur in a statement block, finally is used to guarantee a statement block of code executes regardless of how the preceding try block is exited.

Example

In this example, there is one illegal conversion statement that causes an exception. When you run the program, you get a run-time error message, but the finally clause will still be executed and display the output.

// try-finally
using System;
public class TestTryFinally {
   public static void Main() {
      int i = 123;
      string s = "Some string";
      object o = s;
      try {
         i = (int) o;   // Illegal conversion:
                        // o contains a string not an int
      }
      finally {
         Console.Write("i = {0}", i);
      }
   }
}

Output

i = 123

Caught exception: System.InvalidCastException

at TestTryFinally.Main()

Although an exception was caught, the output statement included in the finally block was executed.

For more information on finally, see try-catch-finally.

See Also

C# Keywords | Compare to C++ | Exception Handling Statements | throw | try-catch | Grammar


try-catch-finally

A common usage of catch and finally together is to obtain and use resources in a try block, deal with exceptional circumstances in a catch block, and release the resources in the finally block.

Example

// try-catch-finally
using System;
public class EHClass {
   public static void Main () {
      try {
         Console.WriteLine("Executing the try statement.");
         throw new NullReferenceException();
      }
      catch(NullReferenceException e) {
         Console.WriteLine("Caught exception #1.");
      }
      catch {
         Console.WriteLine("Caught exception #2.");
      }
      finally {
         Console.WriteLine("Executing finally block.");
      }
   }
}

Output

Executing the try statement.
Caught exception #1.
Executing finally block.

See Also

C# Keywords | Compare to C++ | Exception Handling Statements | throw | Grammar


Checked and Unchecked

C# statements can execute in either checked or unchecked context. In a checked context, arithmetic overflow raises an exception. In an unchecked context, arithmetic overflow is ignored and the result is truncated.

  • checked Specify checked context.
  • unchecked Specify unchecked context.

If neither checked nor unchecked is specified, the default context depends on external factors such as compiler options.

The following operations are affected by the overflow checking:

  • Expressions using the following predefined operators on integral types:

    ++ -- - (unary) + - * /

  • Explicit numeric conversions between integral types.

The /checked compiler option lets you specify checked or unchecked context for all integer arithmetic statements that are not explicitly in the scope of a checked or unchecked keyword.

See Also

C# Keywords | Statements


checked

The checked keyword is used to control the overflow-checking context for integral-type arithmetic operations and conversions. It can be used as an operator or a statement according to the following forms.

The checked statement:

checked block

The checked operator:

checked (expression)

where:

block
The statement block that contains the expressions to be evaluated in a checked context.
expression
The expression to be evaluated in a checked context. Notice that the expression must be in parentheses ( ).

Remarks

In a checked context, if an expression produces a value that is outside the range of the destination type, the result depends on whether the expression is constant or non-constant. Constant expressions cause compile time errors, while non-constant expressions are evaluated at run time and raise exceptions.

If neither checked nor unchecked is used, a constant expression uses the default overflow checking at compile time, which is checked. Otherwise, if the expression is non-constant, the run-time overflow checking depends on other factors such as compiler options and environment configuration.

The following three examples demonstrate the checked and unchecked operators on non-constant expressions. All use the same algorithm, but different checking contexts. The overflow checking is evaluated at run time.

  • Example 1: Using a checked expression.
  • Example 2: Using an unchecked expression.
  • Example 3: Using default overflow checking.

Only the first example throws an overflow exception at run time, in which case, you have the option to go to the debugging mode, or abort the program execution. The other two examples produce truncated values.

See also the unchecked examples on using the checked and unchecked statements.

Example 1

// Using checked expressions
// The overflow of non-constant expressions is checked at run time
using System;
class OverFlowTest {
   static short x = 32767;   // Max short value
   static short y = 32767;
// Using a checked expression
   public static int myMethodCh() {
      int z =  checked((short)(x + y));
      return z;   // Throws the exception OverflowException
   }
   public static void Main() {
      Console.WriteLine("Checked output value is: {0}", myMethodCh());
   }
}

Output

When you run the program, it throws the exception OverflowException. You can debug the program or abort execution.

Example 2

// Using unchecked expressions
// The overflow of non-constant expressions is checked at run time
using System;
class OverFlowTest {
   static short x = 32767;   // Max short value
   static short y = 32767;
   public static int myMethodUnch() {
      int z =  unchecked((short)(x + y));
      return z;   // Returns -2
   }
   public static void Main() {
      Console.WriteLine("Unchecked value is: {0}", myMethodUnch());
   }
}

Output

Unchecked value is: -2

Example 3

// Using default overflow checking
// The overflow of non-constant expressions is checked at run time
using System;
class OverFlowTest {
   static short x = 32767;   // Max short value
   static short y = 32767;
   public static int myMethodUnch() {
      int z =  (short)(x + y);
      return z;   // Returns -2
   }
   public static void Main() {
      Console.WriteLine("Default checking ouput value is: {0}", myMethodUnch());
   }
}

Output

Default checking ouput value is: -2

See Also

C# Keywords | Checked and Unchecked | unchecked


unchecked

The unchecked keyword is used to control the overflow-checking context for integral-type arithmetic operations and conversions. It can be used as an operator or a statement according to the following forms.

The unchecked statement:

unchecked block

The unchecked operator:

unchecked (expression)

where:

block
The statement block that contains the expressions to be evaluated in an unchecked context.
expression
The expression to be evaluated in an unchecked context. Notice that the expression must be in parentheses ( ).

Remarks

In an unchecked context, if an expression produces a value that is outside the range of the destination type, the result is truncated.

If neither checked nor unchecked is used, a constant expression uses the default overflow checking at compile time, which is checked. Otherwise, if the expression is non-constant, the run-time overflow checking depends on other factors such as compiler options and environment configuration.

The following are three examples demonstrating the checked and unchecked statements. All use the same algorithm, but different checking contexts. The three examples are using constant expressions, for which the overflow checking is evaluated at compile time.

  • Example 1: Using the unchecked statement.
  • Example 2: Using default overflow checking.
  • Example 3: Using the checked statement.

Only the unchecked statement produces a truncated value. The other two statements produce compile-time errors.

See also the checked examples on using the checked and unchecked operators.

Example 1

// Using the unchecked statement with constant expressions
// Overflow is checked at compile time
using System;
class TestClass {
   const int x = 2147483647;   // Max int
   const int y = 2;
   public int MethodUnCh() {
      // Unchecked statement:
      unchecked {
         int z = x * y;
         return z;
      }   // Returns -2
   }
   public static void Main() {
      TestClass myObject = new TestClass();
      Console.WriteLine("Unchecked output value: {0}",
         myObject.MethodUnCh());
   }
}

Output

Unchecked output value: -2

Example 2

// Using default overflow checking with constant expressions
// Overflow is checked at compile time
using System;
class TestClass {
   const int x = 2147483647;   // Max int
   const int y = 2;
   public int MethodDef() {
      // Using default overflow checking:
      int z = x * y;
      return z;   // Compiler error
   }
public static void Main() {
      TestClass myObject = new TestClass();
      Console.WriteLine("Default checking value: {0}",
         myObject.MethodDef());
   }
}

Output

The compilation error: The operation overflows at compile time in checked mode.

Example 3

// Using the checked statement with constant expressions
// Overflow is checked at compile time
using System;
class TestClass {
   const int x = 2147483647;   // Max int
   const int y = 2;
   public int MethodCh() {
      // Checked statement:
      checked {
         int z = (x * y);
         return z;
      }   // Compiler error
   }
   public static void Main() {
      TestClass myObject = new TestClass();
      Console.WriteLine("Checked value: {0}", myObject.MethodCh());
}
}

Output

The compilation error: The operation overflows at compile time in checked mode.

See Also

C# Keywords | Checked and Unchecked | checked


Unsafe and Fixed

C# statements execute in either a safe or an unsafe context. Safe context is the default, but any code using pointers requires unsafe context.

  • unsafe Specifies unsafe context.

Once you have a pointer to a variable, you must ensure that the variable isn't moved in memory by the garbage collector. In C# you can "pin" a variable using the fixed statement.

  • fixed Prevents relocation of a variable.

See Also

C# Keywords | Statements


fixed

Prevents relocation of a variable by the garbage collector.

fixed ( type* ptr = expr ) statement

where:

type
An unmanaged type or void.
ptr
A pointer name.
expr
An expression that is implicitly convertible to type*.
statement
Executable statement or block.

Remarks

The fixed statement is only permitted in an unsafe context.

The fixed statement sets a pointer to a managed variable and "pins" that variable during the execution of statement. Without fixed, pointers to managed variables would be of little use since garbage collection could relocate the variables unpredictably. (In fact, the C# compiler will not allow you to set a pointer to a managed variable except in a fixed statement.)

// assume class Point { public int x, y; }
Point pt = new Point();    // pt is a managed variable, subject to g.c.
fixed ( int* p = &pt.x ){  // must use fixed to get address of pt.x and
    *p = 1;                //   pin pt in place while we use the pointer
}

You can initialize a pointer with the address of an array or a string:

fixed (int* p = arr) ...  // equivalent to p = &arr[0]
fixed (char* p = str) ... // equivalent to p = &str[0]

You can initialize multiple pointers, as long as they are all of the same type:

fixed (byte* ps = srcarray, pd = dstarray) {...}

To initialize pointers of different type, simply nest fixed statements:

fixed (int* p1 = &p.x)
   fixed (double* p2 = &array[5])
// do something with p1 and p2

Pointers initialized in fixed statements cannot be modified.

After statement is executed, any pinned variables are unpinned and subject to garbage collection, so do not save pointers to those variables outside the fixed statement.

Example

using System;
class Point { public int x, y; }
class FixedTest {
   // unsafe method: takes pointer to int
   unsafe static void SquarePtrParam (int* p) {
      *p *= *p;
   }
   public static void Main() {
      Point pt = new Point();
      pt.x = 5;
      pt.y = 6;
      unsafe {
         // pin pt in place:
         fixed (int* p = &pt.x) {
            SquarePtrParam (p);
         }
         // pt now unpinned
      }
      Console.WriteLine ("{0} {1}", pt.x, pt.y);
   }
}

Output

25 6

See Also

C# Keywords | Unsafe and Fixed | Unsafe Code Tutorial


unsafe

The unsafe keyword denotes an unsafe context. Unsafe context is required for any operation involving pointers.

unsafe can be applied as a modifier in the declaration of callable members such as methods, properties, constructors, and extenders (but not static constructors).

static unsafe void FastCopy ( byte[] src, byte[] dst, int count )
{
   // unsafe context: can use pointers here
}

The scope of the unsafe context extends from the parameter list to the end of the function, so pointers can also be used in the parameter list:

static unsafe void FastCopy ( byte* ps, byte* pd, int count ) {...}

Alternatively, unsafe can be used to specify an unsafe statement—a statement or block that is an unsafe context:

unsafe *p++ = *q++;    // unsafe assignment statement
unsafe { *p++ = 1; *q++ = 2; }  // unsafe block
unsafe fixed (byte* p = src, q = dst) {...}  // unsafe fixed statement

To compile unsafe code you must specify the /unsafe compiler option. Unsafe code is not verifiable by the execution engine.

Example

using System;
class UnsafeTest {
   // unsafe method: takes pointer to int
   unsafe static void SquarePtrParam (int* p) {
      *p *= *p;
   }
   public static void Main() {
      int i = 5;
      // unsafe statement for address-of operator
      unsafe SquarePtrParam (&i);
      Console.WriteLine (i);
   }
}

Output

25

See Also

C# Keywords | Unsafe and Fixed | Unsafe Code Tutorial


lock Statement

The lock keyword marks a statement block as a critical section.

lock(expression) statement_block

where:

expression
Specifies the object that you want to lock on. expression must be a reference type.

Typically, expression will either be this, if you want to protect an instance variable, or typeof (class), if you want to protect a static variable (or if the critical section occurs in a static method in the given class).

statement_block
The statements of the critical section.

Remarks

lock ensures that one thread does not enter a critical section while another thread is in the critical section of code.

Section 8.12 of the C# Language Reference discusses lock.

Example

The following sample shows a simple use of threads in C#.

using System;
using System.Threading;

class ThreadTest {
   public void runme() {
      Console.WriteLine("Runme Called");
   }

   public static void Main() {
      ThreadTest b = new ThreadTest();
      Thread t = new Thread(new ThreadStart(b.runme));
      t.Start();
   }
}

Program Output

Runme Called

Example

The following sample uses threads and lock. As long as the lock statement is present, the statement block is a critical section and balance will never become a negative number.

using System;
using System.Threading;

internal class Account {
   int balance;

   Random r = new Random();

   internal Account(int initial) {
      balance = initial;
   }

   internal int Withdraw(int amount) {

      if (balance < 0) {
         throw new Exception("Negative Balance");
      }

      // Comment out the next line to see the effect of leaving out the lock keyword
      lock (this)
      {
         if (balance >= amount) {
            Thread.Sleep(5);
            balance = balance - amount;
            return amount;
         } else {
            return 0; // transaction rejected
         }
      }
   }

   internal void DoTransactions() {
      for (int i = 0; i < 100; i++) {
         Withdraw(r.Next(-50, 100));
      }
   }
}

internal class Test {

   static internal Thread[] threads = new Thread[10];

   public static void Main() {
      Account acc = new Account (0);

      for (int i = 0; i < 10; i++) {
         Thread t = new Thread(new ThreadStart(acc.DoTransactions));
         threads[i] = t;
      }

      for (int i = 0; i < 10; i++) {
         threads[i].Start();
      }
   }
}

See Also

C# Keywords | Statements


Types

The C# typing system contains the following categories:

  • Value types
  • Reference types
  • Pointer types

Variables of the value types store data, while those of the reference types store references to the actual data. Reference types are also referred to as objects. Pointer types can be used only in unsafe.

It is possible to convert a value type to a reference type and vice versa, by using boxing and unboxing.

This section also introduces the void type.

See Also

C# Keywords | Types Reference Tables


Value Types

The value types consist of two main categories:

  • Struct type
  • Enumeration type

The struct types contain the user-defined struct types and the following built-in simple types:

  • Numeric types
    • Integral types
    • Floating-point types
    • decimal
  • bool

Remarks

A variable of a value type always contains a value of that type. The assignment to a variable of a value type creates a copy of the assigned value, while the assignment to a variable of a reference type creates a copy of the reference but not of the referenced object.

All value types are derived implicitly from the Object class.

Unlike reference types, it is not possible to derive a new type from a value type. However, like reference types, structs can implement interfaces.

Unlike reference types, it is not possible for a value type to contain the null value.

Each value type has an implicit default constructor that initializes the default value of that type. For information on default values of value types, see Default Values Table.

Following are the main features of simple types:

  • All the simple types are aliases of the NGWS System types. For example, int is an alias of System.Int32. For a complete list of aliases, see Built-in Types Table.
  • Constant expressions, whose operands are all simple type constants, are evaluated at compilation time. For more information on constant expressions, see §7 in the language reference.
  • Simple types can be initialized using literals. For example 'A' is a literal of the type char and 2000 is a literal of the type int.

See Also

C# Keywords | Types | Types Reference Tables


bool

The bool keyword is an alias of System.Boolean. It is used to declare variables to store the Boolean values, true and false.

You can assign a Boolean value to a bool variable, for example:

bool MyVar = true;

You can also assign an expression that evaluates to bool to a bool variable, for example:

bool Alphabetic = (c > 64 && c < 123);

In C++, a value of type bool can be converted to a value of type int; in other words, false is equivalent to zero and true is equivalent to nonzero values. In C#, there is no conversion between the bool type and other types. For example, the following if statement is illegal in C#, while it is legal in C++:

int x = 123;
if (x) {      // Illegal in C#
   printf("The value of x is nonzero.");
}

To test a variable of the type int, you have to explicitly compare it to a value (for example, zero), that is:

int x = 123;
if (x != 0) {      // The C# way
   Console.Write("The value of x is nonzero.");
}

Example

In this example, you enter a character from the keyboard and the program checks if the input character is letter. If so, it checks if it is lowercase or uppercase. In each case, the proper message is displayed.

// Character Tester
using System;
public class BoolTest {
   public static void Main() {
      Console.Write("Enter a character: ");
      char c = (char) Console.Read();
      if (Char.IsLetter(c))
         if (Char.IsLower(c))
            Console.WriteLine("The character is lowercase.");
         else
            Console.WriteLine("The character is uppercase.");
      else
         Console.WriteLine("The character is not an alphabetic character.");
   }
}

Sample Runs

Enter a character: X
The character is uppercase.

Enter a character: x
The character is lowercase.

Enter a character: 2
The character is not an alphabetic character.

See Also

C# Keywords | Default Values Table | Built-in Types Table


byte

The byte keyword denotes an integral type that stores values as indicated in the following table.

Type | Range | Size | NGWS Type
byte | 0 to 255 | Unsigned 8-bit integer | System.Byte

Literals

You can declare and initialize a byte variable like this example:

byte myByte = 255;

In the preceding declaration, the integer literal 255 is implicitly converted from int to byte. If the integer literal exceeds the range of byte, a compilation error will occur.

Conversions

There is a predefined implicit conversion from byte to short, ushort, int, uint, long, ulong, float, double, or decimal.

You cannot implicitly convert nonliteral numeric types of larger storage size to byte (see Integral Types Table for the storage sizes of integral types). Consider, for example, the following two byte variables x and y:

byte x = 10, y = 20;

The following assignment statement will produce a compilation error, because the arithmetic expression on the right-hand side of the assignment operator evaluates to int by default.

byte z = x + y;   // Error: conversion from int to byte

To fix this problem, use a cast:

byte z = (byte)(x + y);   // OK: explicit conversion

It is possible though to use the following statements, where the destination variable has the same storage size or a larger storage size:

int x = 10, y = 20;
int m = x + y;
long n = x + y;

Also, there is no implicit conversion from floating-point types to byte. For example, the following statement generates a compiler error unless an explicit cast is used:

byte x = 3.0;         // Error: no implicit conversion from double
byte y = (byte)3.0;   // OK: explicit conversion

When calling overloaded methods, a cast must be used. Consider, for example, the following overloaded methods that use byte and int parameters:

public static void MyMethod(int i) {}
public static void MyMethod(byte b) {}

Using the byte cast guarantees that the correct type is called, for example:

MyMethod(5);        // Calling the method with the int parameter
MyMethod((byte)5);  // Calling the method with the byte parameter

For information on arithmetic expressions with mixed floating-point types and integral types, see float and double.

For more information on implicit numeric conversion rules, see the Implicit Numeric Conversions Table.

See Also

C# Keywords | Integral Types Table | Built-in Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


char

The char keyword is used to declare a Unicode character in the range indicated in the following table. Unicode characters are 16-bit characters used to represent most of the known written languages throughout the world.

Type | Range | Size | NGWS Type
char | U+0000 to U+ffff | Unicode 16-bit character | System.Char

Literals

Constants of the char type can be written as character literals, hexadecimal escape sequence, or Unicode representation. You can also cast the integral character codes. All of the following statements declare a char variable and initialize it with the character X:

char MyChar = 'X';        // Character literal
char MyChar = '\x0058';   // Hexadecimal
char MyChar = (char)88;   // Cast from integral type
char MyChar = '\u0058';   // Unicode

Conversions

A char can be implicitly converted to ushort, int, uint, long, ulong, float, double, or decimal. However, there are no implicit conversions from other types to the char type.

See Also

C# Keywords | Integral Types Table | Built-in Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


decimal

The decimal keyword denotes a 128-bit data type. Compared to floating-point types, the decimal type has a greater precision and a smaller range, which makes it suitable for financial and monetary calculations. The approximate range and precision for the decimal type are shown in the following table.

Type | Approximate Range | Precision | NGWS Type
decimal | 1.0 × 10-28 to 7.9 × 1028 | 28-29 significant digits | System.Decimal

Literals

If you want a numeric real literal to be treated as decimal, use the suffix m or M, for example:

decimal myMoney = 300.5m;

Without the suffix m, the number is treated as a double, thus generating a compiler error.

Conversions

The integral types are implicitly converted to decimal and the result evaluates to decimal. Therefore you can initialize a decimal variable using an integer literal, without the suffix, for example:

decimal myMoney = 300;

There is no implicit conversion between floating-point types and the decimal type; therefore, a cast must be used to convert between these two types. For example:

decimal myMoney = 99.9m;
double x = (double) myMoney;
myMoney = (decimal) x;

You can also mix decimal and numeric integral types in the same expression. However, mixing decimal and floating-point types without a cast results in a compilation error.

For more information on implicit numeric conversions, see Implicit Numeric Conversions Table.

For more information on explicit numeric conversions, see Explicit Numeric Conversions Table.

Example

In this example, a decimal and an int are mixed in the same expression. The result evaluates to the decimal type.

// decimal conversion
using System;
public class TestDecimal {
   public static void Main () {
      decimal d = 9.1m;
      double  x = 9.0;
      int y = 3;
      Console.WriteLine(d + y);   // Result converted to decimal
   }
}

Output

12.1

In the preceding example, if you attempt to add the double and decimal variables by using a statement like this:

Console.WriteLine(d + x);   // error

you will get the following error:

Operator '+' cannot be applied to operands of type 'double' and 'decimal'

Formatting Decimal Output

You can format the results using the Format method of the runtime library of the NGWS Framework, as shown in the following example.

Example

In this example, the Format method is used to display the results. Notice that x is rounded because the decimal places exceed $0.99. y, which represents the maximum exact digits, is displayed exactly in the proper format.

// Decimal type formatting
using System;
public class TestDecimalFormat {
   public static void Main () {
      decimal x = 0.999m;
      decimal y = 9999999999999999999999999999m;
      Console.Write("My amount = ");
      Console.WriteLine(decimal.Format(x, "C"));
      Console.Write("Your amount = ");
      Console.WriteLine(decimal.Format(y, "C"));
   }
}

Output

My amount = $1.00
Your amount = $9,999,999,999,999,999,999,999,999,999.00

For more information on the Format method, see the Format Class. For more information on the decimal type precision, see §4.1.6 in the language reference.

See Also

C# Keywords | Default Values Table | Built-in Types Table | Floating-point Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


double

The double keyword denotes a simple type that stores 64-bit floating-point values. The following table shows the precision and approximate range for the double type.

Type | Approximate range | Precision | NGWS Type
double | ±5.0 × 10-324 to ±1.7 × 10308 | 15-16 digits | System.Double

Literals

By default, a real numeric literal on the right-hand side of the assignment operator is treated as double. However, if you want an integer number to be treated as double, use the suffix d or D, for example:

double x = 3D;

Conversions

You can mix numeric integral types and floating-point types in an expression. In this case, the integral types are converted to floating-point types. The evaluation of the expression is performed according to the following rules:

  • If one of the floating-point types is double, the expression evaluates to double (or bool in the case of relational or Boolean expressions).
  • If there is no double type in the expression, it evaluates to float (or bool in the case of relational or Boolean expressions).

A floating-point expression can contain the following sets of values:

  • Positive and negative zero
  • Positive and negative infinity
  • Not-a-Number value (NaN)
  • The finite set of nonzero values

For more information on these values, refer to IEEE Standard for Binary Floating-Point Arithmetic, available on the Web site http://www.ieee.org/.

For more information on floating-point value sets, see §4.1.5 in the language reference.

Example

In the following example, an int, a short, a float, and a double are added together giving a double result.

// Mixing types in expressions
using System;
class MixedTypes {
   public static void Main()  {
      int x = 3;
      float y = 4.5f;
      short z = 5;
      double w = 1.7E+3;
      Console.WriteLine("The sum is {0}", x+y+z+w);   // double result
   }
}

Output

The sum is 1712.5

See Also

C# Keywords | Default Values Table | Built-in Types Table | Floating-point Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


enum

The enum keyword is used to declare an enumeration, a distinct type consisting of a set of named constants called the enumerator list. Every enumeration type has an underlying type, which can be any integral type except char. The declaration of an enum variable takes the form:

[attributes] [modifiers] enum identifier [:base-type] {enumerator-list};

where:

attributes (Optional)
Additional declarative information. For more information on attributes and attribute classes, see §17 in the language reference.
modifiers (Optional)
The allowed modifiers are new and the four access modifiers.
identifier
The enum name.
base-type (Optional)
One of the integral types except char. The default is int.
enumerator-list
The enumerators' identifiers separated by commas, optionally including a value assignment.

Remarks

The default type of the enumeration elements is int. By default, the first enumerator has the value 0, and the value of each successive enumerator is increased by one. For example:

enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};

In this enumeration, Sat is 0, Sun is 1, Mon is 2, and so forth. Enumerators can have initializers to override the default values. For example:

enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

In this enumeration the sequence of elements is forced to start from 1 instead of 0.

The default value of a enum E is the value produced by the expression (E)0.

For more information on enumeration types, see §14 in the language reference.

Example

// enum initialization:
using System;
public class EnumTest {
   enum Days {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};
   public static void Main() {
      int x = (int) Days.Sun;
      int y = (int) Days.Fri;
      Console.WriteLine("Sun = {0}", x);
      Console.WriteLine("Fri = {0}", y);
   }
}

Output

Sun = 2
Fri = 7

However, if you remove the initializer from Sat=1, the result will be:

Sun = 1
Fri = 6

Example

In this example, the base-type option is used to declare an enum whose members are of the type long.

// Using long enumerators
using System;
public class EnumTest {
   enum Range :long {Max = 2147483648L, Min = 255L};
   public static void Main() {
      long x = (long) Range.Max;
      long y = (long) Range.Min;
      Console.WriteLine("Max = {0}", x);
      Console.WriteLine("Min = {0}", y);
   }
}

Output

Max = 2147483648
Min = 255

See Also

C# Keywords | Default Values Table | Built-in Types Table | Types Categories | Value Types


float

The float keyword denotes a simple type that stores 32-bit floating-point values. The following table shows the precision and approximate range for the float type.

Type | Approximate range | Precision | NGWS Type
float | ±1.5 × 10-45 to ±3.4 × 1038 | 7 digits | System.Single

Literals

By default, a real numeric literal on the right-hand side of the assignment operator is treated as double. Therefore, to initialize a float variable use the suffix f or F, for example:

float x = 3.5F;

If you don't use the suffix in the previous declaration, you will get a compilation error because you are attempting to store a double value into a float variable.

Conversions

You can mix numeric integral types and floating-point types in an expression. In this case, the integral types are converted to floating-point types. The evaluation of the expression is performed according to the following rules:

  • If one of the floating-point types is double, the expression evaluates to double (or bool in the case of relational or Boolean expressions).
  • If there is no double type in the expression, the expression evaluates to float (or bool in the case of relational or Boolean expressions).

A floating-point expression can contain the following sets of values:

  • Positive and negative zero
  • Positive and negative infinity
  • Not-a-Number value (NaN)
  • The finite set of nonzero values

For more information on these values, refer to IEEE Standard for Binary Floating-Point Arithmetic, available on the Web site http://www.ieee.org/.

For more information on floating-point value sets, see §4.1.5 in the language reference.

Example

In the following example, an int, a short, and a float are included in a mathematical expression giving a float result (notice that there is no double in the expression).

// Mixing types in expressions
using System;
class MixedTypes {
   public static void Main() {
      int x = 3;
      float y = 4.5f;
      short z = 5;
      Console.WriteLine("The result is {0}", x*y/z);
   }
}

Output

The result is 2.70000005

See Also

C# Keywords | Default Values Table | Built-in Types Table | Floating-point Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


int

The int keyword denotes an integral type that stores values according to the size and range shown in the following table.

Type | Range | Size | NGWS Type
int | -2,147,483,648 to 2,147,483,647 | Signed 32-bit integer | System.Int32

Literals

You can declare and initialize a variable of the type int like this example:

int myInt = 123;

When an integer literal has no suffix, its type is the first of these types in which its value can be represented: int, uint, long, ulong. In this example, it is of the type int.

Conversions

There is a predefined implicit conversion from int to long, float, double, or decimal. For example:

float myFloat = 123;   // OK: implicit conversion to float

There is a predefined implicit conversion from sbyte, byte, short, ushort, or char to int. For example, if you have a long variable, myLong, the following assignment statement will produce a compilation error without a cast:

long myLong = 22;
int myInt = myLong;       // Error: no implicit conversion from long
int myInt = (int)myLong;  // OK: explicit conversion

Notice also that there is no implicit conversion from floating-point types to int. For example, the following statement generates a compiler error unless an explicit cast is used:

int x = 3.0;         // Error: no implicit conversion from double
int y = (int)3.0;    // OK: explicit conversion

For information on arithmetic expressions with mixed floating-point types and integral types, see float and double.

See Also

C# Keywords | Integral Types Table | Built-in Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


long

The long keyword denotes an integral type that stores values according to the size and range shown in the following table.

Type | Range | Size | NGWS Type
long | –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | Signed 64-bit integer | System.Int64

Literals

You can declare and initialize a long variable like this example:

long myLong = 4294967296;

When an integer literal has no suffix, its type is the first of these types in which its value can be represented: int, uint, long, ulong. In the preceding example, it is of the type long because it exceeds the range of uint (see Integral Types Table for the storage sizes of integral types).

You can also use the suffix L or l with the long type like this:

long myLong = 4294967296L;

When you use the suffix L or l, the type of the literal integer is determined to be either long or ulong according to its size. In the case it is long because it less than the range of ulong.

A common use of the suffix is with calling overloaded methods. Consider, for example, the following overloaded methods that use long and int parameters:

public static void MyMethod(int i) {}
public static void MyMethod(long l) {}

Using the suffix L or l guarantees that the correct type is called, for example:

MyMethod(5);    // Calling the method with the int parameter
MyMethod(5L);   // Calling the method with the long parameter

You can use the long type with other numeric integral types in the same expression, in which case the expression is evaluated as long (or bool in the case of relational or Boolean expressions). For example, the following expression evaluates as long:

898L + 88

For information on arithmetic expressions with mixed floating-point types and integral types, see float and double.

Conversions

There is a predefined implicit conversion from long to float, double, or decimal. Otherwise a cast must be used. For example, the following statement will produce a compilation error without an explicit cast:

int x = 8L;        // Error: no implicit conversion from long to int
int x = (int)8L;   // OK: explicit conversion to int

There is a predefined implicit conversion from sbyte, byte, short, ushort, int, uint, or char to long.

Notice also that there is no implicit conversion from floating-point types to long. For example, the following statement generates a compiler error unless an explicit cast is used:

long x = 3.0;         // Error: no implicit conversion from double
long y = (long)3.0;   // OK: explicit conversion

See Also

C# Keywords | Integral Types Table | Built-in Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


sbyte

The sbyte keyword denotes an integral type that stores values according to the size and range shown in the following table.

Type | Range | Size | NGWS Type
sbyte | -128 to 127 | Signed 8-bit integer | System.SByte

Literals

You can declare and initialize an sbyte variable like this example:

sbyte mySbyte = 127;

In the preceding declaration, the integer literal 127 is implicitly converted from int to sbyte. If the integer literal exceeds the range of sbyte, a compilation error will occur.

A cast must be used when calling overloaded methods. Consider, for example, the following overloaded methods that use sbyte and int parameters:

public static void MyMethod(int i) {}
public static void MyMethod(sbyte b) {}

Using the sbyte cast guarantees that the correct type is called, for example:

MyMethod(5);         // Calling the method with the int parameter
MyMethod((sbyte)5);  // Calling the method with the sbyte parameter

Conversions

There is a predefined implicit conversion from sbyte to short, int, long, float, double, or decimal.

You cannot implicitly convert nonliteral numeric types of larger storage size to sbyte (see Integral Types Table for the storage sizes of integral types). Consider, for example, the following two sbyte variables x and y:

sbyte x = 10, y = 20;

The following assignment statement will produce a compilation error, because the arithmetic expression on the right-hand side of the assignment operator evaluates to int by default.

sbyte z = x + y;   // Error: conversion from int to sbyte

To fix this problem, use a cast:

sbyte z = (byte)(x + y);   // OK: explicit conversion

It is possible though to use the following statements, where the destination variable has the same storage size or a larger storage size:

sbyte x = 10, y = 20;
int m = x + y;
long n = x + y;

Notice also that there is no implicit conversion from floating-point types to sbyte. For example, the following statement generates a compiler error unless an explicit cast is used:

sbyte x = 3.0;         // Error: no implicit conversion from double
sbyte y = (sbyte)3.0;  // OK: explicit conversion

For information on arithmetic expressions with mixed floating-point types and integral types, see float and double.

For more information on implicit numeric conversion rules, see the Implicit Numeric Conversions Table.

See Also

C# Keywords | Integral Types Table | Built-in Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


short

The short keyword denotes an integral data type that stores values according to the size and range shown in the following table.

Type | Range | Size | NGWS Type
short | -32,768 to 32,767 | Signed 16-bit integer | System.Int16

Literals

You can declare and initialize a short variable like this example:

short x = 32767;

In the preceding declaration, the integer literal 32767 is implicitly converted from int to short. If the integer literal does not fit into a short storage location, a compilation error will occur.

A cast must be used when calling overloaded methods. Consider, for example, the following overloaded methods that use short and int parameters:

public static void MyMethod(int i) {}
public static void MyMethod(short s) {}

Using the short cast guarantees that the correct type is called, for example:

MyMethod(5);         // Calling the method with the int parameter
MyMethod((short)5);  // Calling the method with the short parameter

Conversions

There is a predefined implicit conversion from short to int, long, float, double, or decimal.

You cannot implicitly convert nonliteral numeric types of larger storage size to short (see Integral Types Table for the storage sizes of integral types). Consider, for example, the following two short variables x and y:

short x = 5, y = 12;

The following assignment statement will produce a compilation error, because the arithmetic expression on the right-hand side of the assignment operator evaluates to int by default.

short z = x + y;   // Error: no conversion from int to short

To fix this problem, use a cast:

short z = (short)(x + y);   // OK: explicit conversion

It is possible though to use the following statements, where the destination variable has the same storage size or a larger storage size:

int m = x + y;
long n = x + y;

There is no implicit conversion from floating-point types to short. For example, the following statement generates a compiler error unless an explicit cast is used:

short x = 3.0;          // Error: no implicit conversion from double
short y = (short)3.0;   // OK: explicit conversion

For information on arithmetic expressions with mixed floating-point types and integral types, see float and double.

For more information on implicit numeric conversion rules, see the Implicit Numeric Conversions Table.

See Also

C# Keywords | Integral Types Table | Built-in Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


struct

A struct type is a value type that can contain constructors, constants, fields, methods, properties, indexers, operators, and nested types. The declaration of a struct takes the following form:

[attributes] [modifiers] struct identifier [:interfaces] body [;]
where:
attributes (Optional)
Additional declarative information. For more information on attributes and attribute classes, see §17 in the language reference.
modifiers (Optional)
The allowed modifiers are new and the four access modifiers.
identifier
The struct name.
interfaces (Optional)
Interfaces implemented by the struct.
body
The struct body that contains member declarations.

Remarks

The struct type is suitable for representing lightweight objects such as Point, Rectangle, and Color. Although it is possible to represent a point as a class, a struct is more efficient in some scenarios. For example, if you declare an array of 1000 Point objects, you will allocate additional memory for referencing each object. In this case, the struct is less expensive.

It is an error to declare a default (parameterless) constructor for a struct.

It is an error to initialize an instance field in a struct.

Unlike C++, you cannot declare a class using the keyword struct. In C#, classes and structs are semantically different. A struct is a value type, while a class is a reference type. For more information on features of value types, see Value Types.

For more information on struct, see Structs in the language reference.

Example

This example demonstrates struct initialization using both default and parameterized constructors.

// struct declaration and initialization
using System;
public struct Point {
   public int x, y;
   public Point(int p1, int p2) {
      x = p1;
      y = p2;
   }
}
class MainClass {
   public static void Main()  {
      // Initialize:
      Point myPoint = new Point();   // Default constructor
      Point yourPoint = new Point(10,10);   // Parameterized constructor
      // Display results:
      Console.Write("My Point: ");
      Console.WriteLine("x={0}, y={1}", myPoint.x, myPoint.y);
      Console.Write("Your Point: ");
      Console.WriteLine("x={0}, y={1}", yourPoint.x, yourPoint.y);
   }
}

Output

My Point: x=0, y=0
Your Point: x=10, y=10

Example

This example demonstrates a feature that is unique to structs. It creates a Point object without using the new operator. If you replace the word struct with the word class, the program won't compile.

// Declare a struct object without "new"
using System;
public struct Point {
   public int x, y;
   public Point(int x, int y) {
      this.x = x;
      this.y = y;
   }
}
class MainClass {
   public static void Main() {
      // Declare an object:
      Point myPoint;
      // Initialize:
      myPoint.x = 10;
      myPoint.y = 20;
      // Display results:
      Console.WriteLine("My Point:");
      Console.WriteLine("x = {0}, y = {1}", myPoint.x, myPoint.y);
   }
}

Output

My Point:
x = 10, y = 20

See Also

C# Keywords | Default Values Table | Built-in Types Table | Types Categories | Value Types


uint

The uint keyword denotes an integral type that stores values according to the size and range shown in the following table.

Type | Range | Size | NGWS Type
uint | 0 to 4,294,967,295 | Unsigned 32-bit integer | System.UInt32

Literals

You can declare and initialize a variable of the type uint like this example:

uint myUint = 4294967290;

When an integer literal has no suffix, its type is the first of these types in which its value can be represented: int, uint, long, ulong. In this example, it is uint.

You can also use the suffix u or U, like this:

uint myUint = 123U;

When you use the suffix U or u, the literal type is determined to be either uint or ulong according to its size. In this example, it is uint.

Conversions

There is a predefined implicit conversion from uint to long, ulong, float, double, or decimal. For example:

float myFloat = 4294967290;   // OK: implicit conversion to float

There is a predefined implicit conversion from byte, ushort, or char to uint. Otherwise you must use a cast. For example, if you have a long variable, myLong, the following assignment statement will produce a compilation error without a cast:

long myLong = 22;
uint myUint = myLong;        // Error: no implicit conversion from long
uint myUint = (uint)myLong;  // OK: explicit conversion

Notice also that there is no implicit conversion from floating-point types to uint. For example, the following statement generates a compiler error unless an explicit cast is used:

uint x = 3.0;         // Error: no implicit conversion from double
uint y = (uint)3.0;   // OK: explicit conversion

For information on arithmetic expressions with mixed floating-point types and integral types, see float and double.

For more information on implicit numeric conversion rules, see the Implicit Numeric Conversions Table.

See Also

C# Keywords | Integral Types Table | Built-in Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


ulong

The ulong keyword denotes an integral type that stores values according to the size and range shown in the following table.

Type | Range | Size | NGWS Type
ulong | 0 to 18,446,744,073,709,551,615 | Unsigned 64-bit integer | System.UInt64

Literals

You can declare and initialize a ulong variable like this example:

ulong myUlong = 9223372036854775808;

When an integer literal has no suffix, its type is the first of these types in which its value can be represented: int, uint, long, ulong. In this example, it is of the type ulong.

You can also use suffixes to specify the type of the literal according to the following rules:

  • If you use L or l, the type of the literal integer will be either long or ulong according to its size.
  • If you use U or u, the type of the literal integer will be either uint or ulong according to its size.
  • If you use UL, ul, Ul, uL, LU, lu, Lu, or lU, the type of the literal integer will be ulong.

    For example, the output of the following three statements will be the system type UInt64, which corresponds to the alias ulong:

    Console.WriteLine(9223372036854775808L.GetType());
Console.WriteLine(123UL.GetType());
Console.WriteLine((123UL + 456).GetType());

A common use of the suffix is with calling overloaded methods. Consider, for example, the following overloaded methods that use ulong and int parameters:

public static void MyMethod(int i) {}
public static void MyMethod(ulong l) {}

Using a suffix with the ulong parameter guarantees that the correct type is called, for example:

MyMethod(5);    // Calling the method with the int parameter
MyMethod(5UL);  // Calling the method with the ulong parameter

Conversions

There is a predefined implicit conversion from ulong to float, double, or decimal.

There is no implicit conversion from ulong to any integral type. For example, the following statement will produce a compilation error without an explicit cast:

long myLong = 8UL;   // Error: no implicit conversion from ulong

There is a predefined implicit conversion from byte, ushort, uint, or char to ulong.

Also, there is no implicit conversion from floating-point types to ulong. For example, the following statement generates a compiler error unless an explicit cast is used:

ulong x = 3.0;         // Error: no implicit conversion from double
ulong y = (ulong)3.0;  // OK: explicit conversion

For information on arithmetic expressions with mixed floating-point types and integral types, see float and double.

For more information on implicit numeric conversion rules, see the Implicit Numeric Conversions Table.

See Also

C# Keywords | Integral Types Table | Built-in Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


ushort

The ushort keyword denotes an integral data type that stores values according to the size and range shown in the following table.

Type | Range | Size | NGWS Type
ushort | 0 to 65,535 | Unsigned 16-bit integer | System.UInt16

Literals

You can declare and initialize a ushort variable like this example:

ushort myShort = 65535;

In the preceding declaration, the integer literal 65535 is implicitly converted from int to ushort. If the integer literal exceeds the range of ushort, a compilation error will occur.

A cast must be used when calling overloaded methods. Consider, for example, the following overloaded methods that use ushort and int parameters:

public static void MyMethod(int i) {}
public static void MyMethod(ushort s) {}

Using the ushort cast guarantees that the correct type is called, for example:

MyMethod(5);          // Calling the method with the int parameter
MyMethod((ushort)5);  // Calling the method with the ushort parameter

Conversions

There is a predefined implicit conversion from ushort to int, uint, long, ulong, float, double, or decimal.

There is a predefined implicit conversion from byte or char to ushort. Otherwise a cast must be used to perform an explicit conversion. Consider for example, the following two ushort variables x and y:

ushort x = 5, y = 12;

The following assignment statement will produce a compilation error, because the arithmetic expression on the right-hand side of the assignment operator evaluates to int by default.

ushort z = x + y;   // Error: conversion from int to ushort

To fix this problem, use a cast:

ushort z = (ushort)(x + y);   // OK: explicit conversion

It is possible though to use the following statements, where the destination variable has the same storage size or a larger storage size:

int m = x + y;
long n = x + y;

Notice also that there is no implicit conversion from floating-point types to ushort. For example, the following statement generates a compiler error unless an explicit cast is used:

ushort x = 3.0;          // Error: no implicit conversion from double
ushort y = (ushort)3.0;  // OK: explicit conversion

For information on arithmetic expressions with mixed floating-point types and integral types, see float and double.

For more information on implicit numeric conversion rules, see the Implicit Numeric Conversions Table.

See Also

C# Keywords | Integral Types Table | Built-in Types Table | Implicit Numeric Conversions Table | Explicit Numeric Conversions Table


Boxing and Unboxing

Boxing and unboxing enable value types to be treated as objects. Value types, including both struct types and built-in types, such as int, can be converted to and from the type object.

See Also

Reference Types | Value Types


Boxing Conversion

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 of a value allocates an object instance and copies the value into the new object.

Consider the following declaration of a value-type variable:

int i = 123;

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

object o = i;

The result of this statement is creating an object 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.

Boxing Conversion

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

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

Example

This example converts an integer variable i to an object o via boxing. Then the value stored in the variable i is changed from 123 to 456. The example shows that the object keeps the original copy of the contents, 123.

// Boxing an integer variable
using System;
class TestBoxing  {
   public static void Main() {
      int i = 123;
      object o = i; // Implicit boxing
      i = 456;      // Change the contents of i
      Console.WriteLine("The value-type value = {0}", i);
      Console.WriteLine("The object-type value = {0}", o);
   }
}

Output

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

See Also

Boxing and Unboxing | Unboxing Conversion


Unboxing Conversion

Unboxing conversion 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 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:

int i = 123;          // A value type
object box = i;       // Boxing
int j = (int)box;     // Unboxing

The following figure demonstrates the result of the preceding statements.

Unboxing Conversion

For an unboxing conversion to a given value type to succeed at run time, the value of the source argument must be a reference to an object that was previously created by boxing a value of that value type. If the source argument is null or a reference to an incompatible object, an InvalidCastException is thrown.

Example

The following example demonstrates a case of invalid unboxing. By using try and catch, an error message is displayed when the error occurs.

// Example to show how incorrect unboxing leads to InvalidCastException
using System;
public class UnboxingTest {
   public static void Main() {
      int intI = 123;
      //Boxing
      object o = intI;
      // Reference to incompatible object produces InvalidCastException
      try {
         int intJ = (short) o;
         Console.WriteLine("Unboxing OK.");
      }
      catch (InvalidCastException e) {
         Console.WriteLine("Error: Incorrect unboxing.");
      }
   }
}

Output

Error: Incorrect unboxing.

If you change the statement

int intJ = (short) o;

to:

int intJ = (int) o;

the conversion will be performed, and you will get the output Unboxing OK.

See Also

Boxing and Unboxing | Boxing Conversion


Reference Types

Variables of reference types, referred to as objects, store references to the actual data. This section introduces the following keywords used to declare reference types:

  • class
  • interface
  • delegate

This section also introduces the following built-in reference types:

  • object
  • string

See Also

C# Keywords | Types Categories | Value Types


class

Classes are declared using the keyword class. The declaration takes the form:

[attributes] [modifiers] class identifier [:base-list] { class-body }[;]

where:

attributes (Optional)
Additional declarative information. For more information on attributes and attribute classes, see §17 in the language reference.
modifiers (Optional)
The allowed modifiers are new, abstract, sealed, and the four access modifiers.
identifier
The class name.
base-list (Optional)
A list that contains the one base class and any implemented interfaces, all separated by commas.
class-body
Declarations of the class members.

Remarks

Unlike C++, only single inheritance is allowed in C#. In other words, a class can inherit implementation from one base class only. However, a class can implement more that one interface.

The access levels protected and private are only allowed on nested classes.

A class can contain declarations of the following members:

  • Constructors
  • Destructors
  • Constants
  • Fields
  • Methods
  • Properties
  • Indexers
  • Operators

Example

The following example demonstrates declaring class fields, constructors, and methods. It also demonstrates object instantiation and printing instance data. In this example, two classes are declared, the Kid class, which contains two private fields (name and age) and two public methods. The second class is MainClass, used for data processing.

// class example
using System;
public class Kid {
   private int age;
   private string name;
   // Default constructor
   public Kid() {
      name = "N/A";
   }
   // Constructor:
   public Kid(string name, int age) {
      this.name = name;
      this.age = age;
   }
   // Printing method:
   public void PrintKid() {
      Console.WriteLine("{0}, {1} years old.", name, age);
   }
}
public class MainClass {
   public static void Main() {
      // Create objects
      // Objects must be created using the new operator:
      Kid kid1 = new Kid("Craig", 11);
      Kid kid2 = new Kid("Sally", 10);
      // Create an object using the default constructor:
      Kid kid3 = new Kid();
      // Display results:
      Console.Write("Kid #1: ");
      kid1.PrintKid();
      Console.Write("Kid #2: ");
      kid2.PrintKid();
      Console.Write("Kid #3: ");
      kid3.PrintKid();
   }
}

Output

Kid #1: Craig, 11 years old.
Kid #2: Sally, 10 years old.
Kid #3: N/A, 0 years old.

Notice, in the preceding example, that the private fields (name and age) can only be accessed through the public methods of the Kid class. For example, you cannot print the kid's name, from the Main method, using a statement like this:

Console.Write(kid1.name);   // Error

However, it is possible to do that if the Main method is a member of the Kid class.

Notice also that the private modifier is the default modifier for class members. If you remove the modifier, you still get private members. For more information on access level modifiers, see Access Modifiers.

Notice also that for the object created using the default constructor (kid3), the age field was initialized to zero.

For more information on classes, see §10 in the language reference.

See Also

C# Keywords | Reference Types | Grammar


delegate

A delegate declaration defines a reference type that can be used to encapsulate a method with a specific signature. A delegate instance encapsulates a static or an instance method. Delegates are roughly similar to function pointers in C++; however, delegates are type-safe and secure.

The delegate declaration takes the form:

[attributes] [modifiers] delegate result-type identifier ([formal-parameters]);

where:

attributes (Optional)
Additional declarative information. For more information on attributes and attribute classes, see §17 in the language reference.
modifiers (Optional)
The allowed modifiers are new and the four access modifiers.
result-type
The result type, which matches the return type of the method.
identifier
The delegate name.
formal-parameters (Optional)
Parameter list.

For more information on delegates, see §15 in the language reference.

Example

In the following example, one delegate is mapped to both static and instance methods and returns specific information from each.

// Calling both static and instance methods from delegates
using System;
delegate int MyDelegate();   // delegate declaration
public class MyClass {
   public int InstanceMethod () {
      Console.WriteLine("A message from the instance method.");
      return 0;
   }
   static public int StaticMethod () {
      Console.WriteLine("A message from the static method.");
      return 0;
   }
}
public class MainClass {
   static public void Main () {
      MyClass p = new MyClass();
      // Map the delegate to the instance method:
      MyDelegate d = new MyDelegate(p.InstanceMethod);
      // Call the instance method:
      d();
      // Map to the static method:
      d = new MyDelegate(MyClass.StaticMethod);
      // Call the static method:
      d();
   }
}

Output

A message from the instance method.
A message from the static method.

See Also

C# Keywords | Reference Types | Grammar


interface

The interface keyword declares a reference type that has abstract members. The interface declaration takes the form:

[attributes] [modifiers] interface identifier [:base-list] {interface-body}[;]

where:

attributes (Optional)
Additional declarative information. For more information on attributes and attribute classes, see §17 in the language reference.
modifiers (Optional)
The allowed modifiers are new and the four access modifiers.
identifier
The interface name.
base-list (Optional)
A list that contains one or more explicit base interfaces separated by commas.
interface-body
Declarations of the interface members.

Remarks

An interface can be a member of a namespace or a class and can contain signatures of the following members:

  • Methods
  • Properties
  • Indexers

An interface can inherit from one or more base interfaces. In the following example, the interface IMyInterface inherits from two base interfaces, IBase1 and IBase2:

interface IMyInterface: IBase1, IBase2 {
   void MethodA();
   void MethodB();
}

Interfaces can be implemented by classes. The identifier of the implemented interface appears in the class base list. For example:

class Class1: Iface1, Iface2 {
   // class members
}

When a class base list contains a base class and interfaces, the base class comes first in the list. For example:

class ClassA: BaseClass, Iface1, Iface2 {
   // class members
}

For more information on interfaces, see §13 in the language reference.

For more information on properties and indexers, see Property Declaration and Indexer Declaration.

Example

The following example demonstrates interface implementation. In this example, the interface IPoint contains the property declaration, which is responsible for setting and getting the values of the fields. The class MyPoint contains the property implementation.

// Interface implementation
using System;
interface IPoint {
   // Property signatures:
   int x {
      get;
      set;
   }
   int y {
      get;
      set;
   }
}
class MyPoint : IPoint {
   // Fields:
   private int myX;
   private int myY;
   // Constructor:
   public MyPoint(int x, int y) {
      myX = x;
      myY = y;
   }
   // Property implementation:
   public int x {
      get {
         return myX;
      }
      set {
         myX = value;
      }
   }
   public int y {
      get {
         return myY;
      }
      set {
         myY = value;
      }
   }
}
class MainClass {
   private static void PrintPoint(IPoint p) {
      Console.WriteLine("x={0}, y={1}", p.x, p.y);
   }
   public static void Main() {
      MyPoint p = new MyPoint(2,3);
      Console.Write("My Point: ");
      PrintPoint(p);
   }
}

Output

My Point: x=2, y=3

See Also

C# Keywords | Reference Types | Properties | Indexers | Grammar


object

The object type is based on System.Object in the NGWS Framework. You can assign values of any type to variables of type object.

All data types, predefined and user-defined, inherit from the System.Object class. The object data type is the type to and from which objects are boxed.

Example

The following sample shows how variables of type object can accept values of any data type and how variables of type object can use methods on System.Object from the NGWS Framework.

using System;
public class MyClass1 {
   public int i = 10;
}
public class MyClass2 {
   public static void Main() {
      object a;
      a = 1;   // an example of boxing
      Console.WriteLine(a);
      Console.WriteLine(a.GetType());
      Console.WriteLine(a.ToString());
      Console.WriteLine();

      a = new MyClass1 ();
      MyClass1 ref_MyClass1;
      ref_MyClass1 = (MyClass1)a;
      Console.WriteLine(ref_MyClass1.i);
   }
}

Output

1
Type: Int32
1

10

See Also

C# Keywords | Reference Types | Value Types


string

The string type represents a string of Unicode characters. string is an alias for System.String in the NGWS Framework.

Although string is a reference type, the equality operators (== and !=) are defined to compare the values of string objects, not references (§ 7.9.7 in the language reference). This makes testing for string equality more intuitive.

string a = "hello";
string b = new string( "hello" );
Console.WriteLine( a == b );  // output: True -- same value
Console.WriteLine( (object)a == b ); //  False -- different objects

The + operator concatenates strings:

string a = "good " + "morning";

The [] operator accesses individual characters of a string:

char x = "test"[2];  // x = 's';

String literals are of type string and can be written in two forms, quoted and @-quoted. Quoted string literals are enclosed in double quotation marks ("):

"good morning"   // a string literal

and can contain any character literal, including escape sequences:

string a = "\\\u0066\n";  // backslash, letter f, new line

@-quoted string literals start with @ and are enclosed in double quotation marks. For example:

@"good morning"  // a string literal

The advantage of @-quoting is that escape sequences are not processed, which makes it easy to write, for example, a fully qualified file name:

@"c:\Docs\Source\a.txt"  // rather than "c:\\Docs\\Source\\a.txt"

To include a double quotation mark in an @-quoted string, double it:

@"""Ahoy!" cried the captain." // "Ahoy!" cried the captain.

Example

using System;
class test {
   public static void Main( String[] args ) {
      string a = "\u0068owdy ";
      string b = "doody";
      Console.WriteLine( a + b );
      Console.WriteLine( a + b == "howdy doody" );
   }
}

Output

howdy doody
True

See Also

C# Keywords | Reference Types | Value Types


void

When used as the return type for a method, void specifies that the method does not return a value.

void is not allowed in a method’s parameter list. A method that takes no parameters and returns no value is declared as follows:

void MyMethod();

See Also

C# Keywords | Reference Types | Value Types


Types Reference Tables

The following reference tables summarize the C# types:

  • Built-in types
  • Integral types
  • Floating-point types
  • Default values
  • Value types
  • Implicit numeric conversions
  • Explicit numeric conversions

See Also

Reference Types | Value Types | Pointer Types


Built-in Types Table

The following table shows the keywords for built-in C# types, which are aliases of predefined types in the System namespace.

C# Type | NGWS Type
bool | System.Boolean
byte | System.Byte
sbyte | System.SByte
char | System.Char
decimal | System.Decimal
double | System.Double
float | System.Single
int | System.Int32
uint | System.UInt32
long | System.Int64
ulong | System.UInt64
object | System.Object
short | System.Int16
ushort | System.UInt16
string | System.String

Remarks

All the types in the table, except object and string, are referred to as simple types.

The C# type keywords and their aliases are interchangeable. For example, you can declare an integer variable by using either of the following declarations:

int x = 123;
System.Int32 x = 123;

To display the actual type for any C# type use the system method GetType(). For example, the following statement displays the system alias that represents the type of myVariable:

Console.WriteLine(myVariable.GetType());

You can also use the method typeof.

See Also

C# Keywords | Value Types | Default Values Table | Types Reference Tables


Integral Types Table

The following table shows the sizes and ranges of the integral types, which constitute a subset of simple types.

Type | Range | Size
sbyte | -128 to 127 | Signed 8-bit integer
byte | 0 to 255 | Unsigned 8-bit integer
char | U+0000 to U+ffff | Unicode 16-bit character
short | -32,768 to 32,767 | Signed 16-bit integer
ushort | 0 to 65,535 | Unsigned 16-bit integer
int | -2,147,483,648 to 2,147,483,647 | Signed 32-bit integer
uint | 0 to 4,294,967,295 | Unsigned 32-bit integer
long | –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 | Signed 64-bit integer
ulong | 0 to 18,446,744,073,709,551,615 | Unsigned 64-bit integer

Remarks

If the value represented by an integer literal exceeds the range of ulong, a compilation error will occur.

See Also

C# Keywords | Built-in Types Table | Floating-point Types Table | Default Values Table | Types Reference Tables


Floating-point Types Table

The following table shows the precision and approximate ranges for the floating-point types.

Type | Approximate range | Precision
float | ±1.5 × 10-45 to ±3.4 × 1038 | 7 digits
double | ±5.0 × 10-324 to ±1.7 × 10308 | 15-16 digits

See Also

Default Values Table | Built-in Types Table | Integral Types Table | Types Reference Tables | decimal


Default Values Table

The following table shows the default values of value types returned by the default constructors. Default constructors are invoked by using the new operator.

Value type | Default value
bool | false
byte | 0
char | '\0'
decimal | 0.0M
double | 0.0D
enum | The value produced by the expression (E)0, where E is the enum identifier.
float | 0.0F
int | 0
long | 0L
sbyte | 0
short | 0
struct | The value produced by setting all value-type fields to their default values and all reference-type fields to null.
uint | 0
ulong | 0
ushort | 0

See Also

Value Types | Built-in Types Table | Types Reference Tables


Value Types Table

The following table lists the C# value types by category.

Value type | Category
bool | Boolean
byte | Unsigned, numeric, integral
char | Unsigned, numeric, integral
decimal | Numeric, decimal
double | Numeric, floating-point
enum | Enumeration
float | Numeric, floating-point
int | Signed, numeric, integral
long | Signed, numeric, integral
sbyte | Signed, numeric, integral
short | Signed, numeric, integral
struct | User-defined structure
uint | Unsigned, numeric, integral
ulong | Unsigned, numeric, integral
ushort | Unsigned, numeric, integral

See Also

Value Types | Types Reference Tables


Implicit Numeric Conversions Table

The following table shows the predefined implicit numeric conversions. Implicit conversions might occur in many situations, including method invoking and assignment statements.

From | To
sbyte | short, int, long, float, double, or decimal
byte | short, ushort, int, uint, long, ulong, float, double, or decimal
short | int, long, float, double, or decimal
ushort | int, uint, long, ulong, float, double, or decimal
int | long, float, double, or decimal
uint | long, ulong, float, double, or decimal
long | float, double, or decimal
char | ushort, int, uint, long, ulong, float, double, or decimal
float | double
ulong | float, double, or decimal

Remarks

  • The conversions from int, uint, or long to float and from long to double may cause a loss of precision, but not a loss of magnitude.
  • There are no implicit conversions to the char type.
  • There are no implicit conversions between floating-point types and the decimal type.
  • A constant expression of type int can be converted to sbyte, byte, short, ushort, uint, or ulong, provided the value of the constant expression is within the range of the destination type.

For more information on implicit conversion, see the language reference, §6.1.

For more information on constant expressions, see the language reference, §7.15.

See Also

Integral Types Table | Built-in Types Table | Explicit Numeric Conversions Table


Explicit Numeric Conversions Table

Explicit numeric conversion is used to convert any numeric type to any other numeric type, for which there is no implicit conversion, by using a cast expression. The following table shows these conversions.

From | To
sbyte | byte, ushort, uint, ulong, or char
byte | sbyte or char
short | sbyte, byte, ushort, uint, ulong, or char
ushort | sbyte, byte, short, or char
int | sbyte, byte, short, ushort, uint, ulong, or char
uint | sbyte, byte, short, ushort, int, or char
long | sbyte, byte, short, ushort, int, uint, ulong, or char
ulong | sbyte, byte, short, ushort, int, uint, long, or char
char | sbyte, byte, or short
float | sbyte, byte, short, ushort, int, uint, long, ulong, char, or decimal
double | sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or decimal
decimal | sbyte, byte, short, ushort, int, uint, long, ulong, char, float, or double

Remarks

  • The explicit numeric conversion may cause loss of precision or result in throwing exceptions.
  • When you convert a float, double, or decimal value to an integral type, this value is rounded towards zero to the nearest integral value. If the resulting integral value is outside the range of the destination type, an InvalidCastException is thrown.
  • When you convert double to float, the double value is rounded to the nearest float value. If the double value is too small or too large to fit into the destination type, the result will be zero or infinity.
  • When you convert float or double to decimal, the source value is converted to decimal representation and rounded to the nearest number after the 28th decimal place if required. Depending on the value of the source value, one of the following results may occur:
    • If the source value is too small to be represented as a decimal, the result becomes zero.
    • If the source value is NaN (not a number), infinity, or too large to be represented as a decimal, an InvalidCastException is thrown.
  • When you convert decimal to float or double, the decimal value is rounded to the nearest double or float value.

For more information on explicit conversion, see the language reference, §6.2.

See Also

Integral Types Table | Built-in Types Table | Implicit Numeric Conversions Table


Topic Not Found

No matching topic was found. The link may be out of date, incorrectly authored, or the topic may not be available in this release.

You may be able to find the topic by using the index or search tab instead.


Copyright

NGWS SDK Documentation

This is a preliminary document and may be changed substantially prior to final commercial release. This document is provided for informational purposes only and Microsoft makes no warranties, either express or implied, in this document. Information in this document, including URL and other Internet Web site references, is subject to change without notice. The entire risk of the use or the results of the use of this document remains with the user. Unless otherwise noted, the example companies, organizations, products, people and events depicted herein are fictitious and no association with any real company, organization, product, person or event is intended or should be inferred. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation.

Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property.

Unpublished work. © 1999 - 2000 Microsoft Corporation. All rights reserved.

Microsoft, MS-DOS, Windows, Windows NT, Active Directory, ActiveX, Authenticode, FrontPage, IntelliSense, JScript , Outlook, Visual Basic, Visual Basic Scripting Edition (VBScript), Visual C++, Visual Studio, and Win32 are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.

The names of actual companies and products mentioned herein may be the trademarks of their respective owners.


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

Stubbed out topic that will go into Glue and the build after the PDC docs ship. Will go in the how to section of the C# docs. David has seen this.

XML Documentation

In C# you can document the code you write using XML. C# is the only programming language in Visual Studio 7.0 with this feature. For details on creating an XML file with documentation comments, see the following topics.

For information about | See
The compiler option to use. | /doc (Process Documentation Comments)
Tags you can use to provide commonly used functionality in documentation. | Tags for Documentation Comments
The ID strings that the compiler produces to identify the constructs in your code. | Processing the XML File
How to document a type. | XML Documentation Tutorial

See Also

EnterLinks EnterLinks


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

Tags for Documentation Comments

move here from csharpopt.doc with the tag topics as H3 heads under this topic

See Also

EnterLinks EnterLinks


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

Processing the XML File

move here from csharpopt.doc

See Also

EnterLinks EnterLinks


NGWS SDK Documentation

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

XML Documentation Tutorial

park the tutorial here

See Also

EnterLinks EnterLinks