This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

7 Basic concepts

7.1 Application startup

A program may be compiled either as a class library to be used as part of other applications, or as an application that may be started directly. The mechanism for determining this mode of compilation is implementation-specific and external to this specification.

A program compiled as an application shall contain at least one method qualifying as an entry point by satisfying the following requirements:

Note : Methods with the async modifier must have exactly one of the two return types specified above in order to qualify as an entry point. An async void method, or an async method returning a different awaitable type such as ValueTask or ValueTask<int> does not qualify as an entry point. end note

If more than one method qualifying as an entry point is declared within a program, an external mechanism may be used to specify which method is deemed to be the actual entry point for the application. If a qualifying method having a return type of int or void is found, any qualifying method having a return type of System.Threading.Tasks.Task or System.Threading.Tasks.Task<int> is not considered an entry point method. It is a compile-time error for a program to be compiled as an application without exactly one entry point. A program compiled as a class library may contain methods that would qualify as application entry points, but the resulting library has no entry point.

Ordinarily, the declared accessibility ( §7.5.2 ) of a method is determined by the access modifiers ( §14.3.6 ) specified in its declaration, and similarly the declared accessibility of a type is determined by the access modifiers specified in its declaration. In order for a given method of a given type to be callable, both the type and the member shall be accessible. However, the application entry point is a special case. Specifically, the execution environment can access the application’s entry point regardless of its declared accessibility and regardless of the declared accessibility of its enclosing type declarations.

When the entry point method has a return type of System.Threading.Tasks.Task or System.Threading.Tasks.Task<int> , the compiler synthesizes a synchronous entry-point method that calls the corresponding Main method. The synthesized method has parameters and return types based on the Main method:

Execution of the synthesized method proceeds as follows:

The effective entry point of an application is the entry point declared within the program, or the synthesized method if one is required as described above. The return type of the effective entry point is therefore always void or int .

When an application is run, a new application domain is created. Several different instantiations of an application may exist on the same machine at the same time, and each has its own application domain. An application domain enables application isolation by acting as a container for application state. An application domain acts as a container and boundary for the types defined in the application and the class libraries it uses. Types loaded into one application domain are distinct from the same types loaded into another application domain, and instances of objects are not directly shared between application domains. For instance, each application domain has its own copy of static variables for these types, and a static constructor for a type is run at most once per application domain. Implementations are free to provide implementation-specific policy or mechanisms for the creation and destruction of application domains.

Application startup occurs when the execution environment calls the application’s effective entry point. If the effective entry point declares a parameter, then during application startup, the implementation shall ensure that the initial value of that parameter is a non-null reference to a string array. This array shall consist of non-null references to strings, called application parameters , which are given implementation-defined values by the host environment prior to application startup. The intent is to supply to the application information determined prior to application startup from elsewhere in the hosted environment.

Note : On systems supporting a command line, application parameters correspond to what are generally known as command-line arguments. end note

If the effective entry point’s return type is int , the return value from the method invocation by the execution environment is used in application termination ( §7.2 ).

Other than the situations listed above, entry point methods behave like those that are not entry points in every respect. In particular, if the entry point is invoked at any other point during the application’s lifetime, such as by regular method invocation, there is no special handling of the method: if there is a parameter, it may have an initial value of null , or a non- null value referring to an array that contains null references. Likewise, the return value of the entry point has no special significance other than in the invocation from the execution environment.

7.2 Application termination

Application termination returns control to the execution environment.

If the return type of the application’s effective entry point method is int and execution completes without resulting in an exception, the value of the int returned serves as the application’s termination status code . The purpose of this code is to allow communication of success or failure to the execution environment. If the return type of the effective entry point method is void and execution completes without resulting in an exception, the termination status code is  0 .

If the effective entry point method terminates due to an exception ( §20.4 ), the exit code is implementation-specific. Additionally, the implementation may provide alternative APIs for specifying the exit code.

Whether or not finalizers ( §14.13 ) are run as part of application termination is implementation-specific.

Note : The .NET Framework implementation makes every reasonable effort to call finalizers ( §14.13 ) for all of its objects that have not yet been garbage collected, unless such cleanup has been suppressed (by a call to the library method GC.SuppressFinalize , for example). end note

7.3 Declarations

Declarations in a C# program define the constituent elements of the program. C# programs are organized using namespaces. These are introduced using namespace declarations ( §13 ), which can contain type declarations and nested namespace declarations. Type declarations ( §13.7 ) are used to define classes ( §14 ), structs ( §15 ), interfaces ( §17 ), enums ( §18 ), and delegates ( §19 ). The kinds of members permitted in a type declaration depend on the form of the type declaration. For instance, class declarations can contain declarations for constants ( §14.4 ), fields ( §14.5 ), methods ( §14.6 ), properties ( §14.7 ), events ( §14.8 ), indexers ( §14.9 ), operators ( §14.10 ), instance constructors ( §14.11 ), static constructors ( §14.12 ), finalizers ( §14.13 ), and nested types ( §14.3.9 ).

A declaration defines a name in the declaration space to which the declaration belongs. It is a compile-time error to have two or more declarations that introduce members with the same name in a declaration space, except in the following cases:

Note : However, these declarations could introduce ambiguities if included in the same application. end note

There are several different types of declaration spaces, as described in the following.

Within all compilation units of a program, namespace_member_declaration s with no enclosing namespace_declaration are members of a single combined declaration space called the global declaration space .

Within all compilation units of a program, namespace_member_declaration s within namespace_declaration s that have the same fully qualified namespace name are members of a single combined declaration space.

Each compilation_unit and namespace_body has an alias declaration space . Each extern_alias_directive and using_alias_directive of the compilation_unit or namespace_body contributes a member to the alias declaration space ( §13.5.2 ).

Each non-partial class, struct, or interface declaration creates a new declaration space. Each partial class, struct, or interface declaration contributes to a declaration space shared by all matching parts in the same program ( §15.2.3 ).Names are introduced into this declaration space through class_member_declaration s, struct_member_declaration s, interface_member_declaration s, or type_parameter s. Except for overloaded instance constructor declarations and static constructor declarations, a class or struct cannot contain a member declaration with the same name as the class or struct. A class, struct, or interface permits the declaration of overloaded methods and indexers. Furthermore, a class or struct permits the declaration of overloaded instance constructors and operators. For example, a class, struct, or interface may contain multiple method declarations with the same name, provided these method declarations differ in their signature ( §7.6 ). 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 delegate declaration creates a new declaration space. Names are introduced into this declaration space through formal parameters ( fixed_parameter s and parameter_array s) and type_parameter s.

Each enumeration declaration creates a new declaration space. Names are introduced into this declaration space through enum_member_declarations .

Each method declaration, property declaration, property accessor declaration, indexer declaration, indexer accessor declaration, operator declaration, instance constructor declaration, anonymous function, and local function creates a new declaration space called a local variable declaration space . Names are introduced into this declaration space through formal parameters ( fixed_parameter s and parameter_array s) and type_parameter s. The set accessor for a property or an indexer introduces the name value as a formal parameter. The body of the function member, anonymous function, or local function, if any, is considered to be nested within the local variable declaration space. It is an error for a local variable declaration space and a nested local variable declaration space to contain elements with the same name. Thus, within a nested declaration space it is not possible to declare a local variable or constant with the same name as a local variable or constant in an enclosing declaration space. It is possible for two declaration spaces to contain elements with the same name as long as neither declaration space contains the other.

Each block or switch_block , as well as a for , foreach , and using statement, creates a local variable declaration space for local variables and local constants. Names are introduced into this declaration space through local_variable_declaration s and local_constant_declaration s. Note that blocks that occur as or within the body of a function member, anonymous function, or local function are nested within the local variable declaration space declared by those functions for their parameters. Thus, it is an error to have, for example, a method with a local variable and a parameter of the same name.

Each block or switch_block creates a separate declaration space for labels. Names are introduced into this declaration space through labeled_statement s, and the names are referenced through goto_statement s. 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, constants, methods, properties, events, indexers, operators, instance constructors, finalizers, static constructors, and types. Declaration order is significant in the following ways:

Example : 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 caused a compile-time error if each contained a declaration of a class with the same name. end example
Note : As specified above, the declaration space of a block includes any nested blocks. Thus, in the following example, the  F and G  methods result in a compile-time error because the name  i is declared in the outer block and cannot be redeclared in the inner block. However, the  H and I  methods are 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(); } } } end note

7.4 Members

7.4.1 general.

Namespaces and types have members .

Note : 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. end note

Members of a type are either declared in the type declaration or inherited from the base class of the type. When a type inherits from a base class, all members of the base class, except instance constructors, finalizers, and static constructors 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 an instance constructor, static constructor, or finalizer.

Note : However, an inherited member might not be accessible in a derived type, for example because of its declared accessibility ( §7.5.2 ). end note

7.4.2 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.

7.4.3 Struct members

The members of a struct are the members declared in the struct and the members inherited from the struct’s direct base class System.ValueType and the indirect base class object .

The members of a simple type correspond directly to the members of the struct type aliased by the simple type ( §8.3.5 ).

7.4.4 Enumeration members

The members of an enumeration are the constants declared in the enumeration and the members inherited from the enumeration’s direct base class System.Enum and the indirect base classes System.ValueType and object .

7.4.5 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 instance constructors, finalizers, 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, instance constructors, finalizers, static constructors, and types.

The members of object ( §8.2.3 ) and string ( §8.2.5 ) correspond directly to the members of the class types they alias.

7.4.6 Interface members

The members of an interface are the members declared in the interface and in all base interfaces of the interface.

Note : The members in class object are not, strictly speaking, members of any interface ( §17.4 ). However, the members in class object are available via member lookup in any interface type ( §11.5 ). end note

7.4.7 Array members

The members of an array are the members inherited from class System.Array .

7.4.8 Delegate members

A delegate inherits members from class System.Delegate . Additionally, it contains a method named Invoke with the same return type and formal parameter list specified in its declaration ( §19.2 ). An invocation of this method shall behave identically to a delegate invocation ( §19.6 ) on the same delegate instance.

An implementation may provide additional members, either through inheritance or directly in the delegate itself.

7.5 Member access

7.5.1 general.

Declarations of members allow control over member access. The accessibility of a member is established by the declared accessibility ( §7.5.2 ) 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 ( §7.5.3 ) of the member.

7.5.2 Declared accessibility

The declared accessibility of a member can be one of the following:

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.

Note : A type declared as a member of a class can have any of the permitted kinds of declared accessibility, whereas a type declared as a member of a namespace can have only public or internal declared accessibility. end note
Note : A type declared as a member of a struct can have public , internal , or private declared accessibility, whereas a type declared as a member of a namespace can have only public or internal declared accessibility. end note

7.5.3 Accessibility domains

The accessibility domain of a member consists of 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 program is defined as all text contained in all compilation units of the program, and the program text of a type is defined as all text contained in the type_declaration s of that 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 unbound type  T ( §8.4.4 ) that is declared in a program  P is defined as follows:

Note : From these definitions, it follows that the accessibility domain of a top-level unbound type is always at least the program text of the program in which that type is declared. end note

The accessibility domain for a constructed type T<A₁, ..., Aₑ> is the intersection of the accessibility domain of the unbound generic type  T and the accessibility domains of the type arguments A₁, ..., Aₑ .

The accessibility domain of a nested member  M declared in a type  T within a program  P , is defined as follows (noting that M  itself might possibly be a type):

Note : 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. end note
Note : 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), a compile-time 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 program 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 ( §7.5.4 ). 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 ( §7.5.4 ). Otherwise, if M is internal , the access is permitted if it occurs within the program 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 a compile-time error occurs. end note
Example : In the following code 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 program. 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 and B.D.Y is the program text of  B , including the program text of B.C and B.D . The accessibility domain of 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. end example

As described in §7.4 , all members of a base class, except for instance constructors, finalizers, and static constructors, 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.

Example : In the following code 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. end example

7.5.4 Protected access

When a protected or private protected instance member is accessed outside the program text of the class in which it is declared, and when a protected internal instance member is accessed outside the program text of the program in which it is declared, the access shall take place within a class declaration that derives from the class in which it is declared. Furthermore, the access is required to take place through an instance of that derived class type or a class type constructed from it. This restriction prevents one derived class from accessing protected members of other derived classes, even when the members are inherited from the same base class.

Let  B be a base class that declares a protected instance 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:

In addition to these forms of access, a derived class can access a protected instance constructor of a base class in a constructor_initializer ( §14.11.2 ).

Example : In the following code 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 . end example
Example : class C<T> { protected T x; } class D<T> : C<T> { static void F() { D<T> dt = new D<T>(); D<int> di = new D<int>(); D<string> ds = new D<string>(); dt.x = default(T); di.x = 123; ds.x = "test"; } } Here, the three assignments to  x are permitted because they all take place through instances of class types constructed from the generic type. end example
Note: The accessibility domain ( §7.5.3 ) of a protected member declared in a generic class includes the program text of all class declarations derived from any type constructed from that generic class. In the example: class C<T> { protected static T x; } class D : C<string> { static void Main() { C<int>.x = 5; } } the reference to protected member  C<int>.x in  D is valid even though the class  D derives from C<string> . end note

7.5.5 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 in which M is accessible.

The following accessibility constraints exist:

Example : In the following code class A {...} public class B: A {...} the B class results in a compile-time error because A is not at least as accessible as B . end example
Example : Likewise, in the following code class A {...} public class B { A F() {...} internal A G() {...} public A H() {...} } the H  method in  B results in a compile-time error because the return type  A is not at least as accessible as the method. end example

7.6 Signatures and overloading

Methods, instance constructors, indexers, and operators are characterized by their signatures :

The signature of a method consists of the name of the method, the number of type parameters, and the type and parameter-passing mode (value, reference, or output) of each of its formal parameters, considered in the order left to right. For these purposes, any type parameter of the method that occurs in the type of a formal parameter is identified not by its name, but by its ordinal position in the type parameter list of the method. The signature of a method specifically does not include the return type, parameter names, type parameter names, type parameter constraints, the params or this parameter modifiers, nor whether parameters are required or optional.

The signature of an instance constructor consists of the type and parameter-passing mode (value, reference, or output) of each of its formal parameters, considered in the order left to right. The signature of an instance constructor specifically does not include the params modifier that may be specified for the right-most parameter.

The signature of an indexer consists of the type of each of its formal parameters, considered in the order left to right. The signature of an indexer specifically does not include the element type, nor does it include the params modifier that may be specified for the right-most parameter.

The signature of an operator consists of the name of the operator and the type of each of its formal parameters, considered in the order left to right. The signature of an operator specifically does not include the result type.

The signature of a conversion operator consists of the source type and the target type. The implicit or explicit classification of a conversion operator is not part of the signature.

Two signatures of the same member kind (method, instance constructor, indexer or operator) are considered to be the same signatures if they have the same name, number of type parameters, number of parameters, and parameter-passing modes, and an identity conversion exists between the types of their corresponding parameters ( §10.2.2 ).

Signatures are the enabling mechanism for overloading of members in classes, structs, and interfaces:

Although out and ref parameter modifiers are considered part of a signature, members declared in a single type cannot differ in signature solely by ref and out . A compile-time error occurs if two members are declared in the same type with signatures that would be the same if all parameters in both methods with out modifiers were changed to ref modifiers. For other purposes of signature matching (e.g., hiding or overriding), ref and out are considered part of the signature and do not match each other.

Note : This restriction is to allow C# programs to be easily translated to run on the Common Language Infrastructure (CLI), which does not provide a way to define methods that differ solely in ref and out . end note

The types object and dynamic are not distinguished when comparing signatures. Therefore members declared in a single type whose signatures differ only by replacing object with dynamic are not allowed.

Example : 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) error void F(object o); // F(object) void F(dynamic d); // error. void F(int x, int y); // F(int, int) int F(string s); // F(string) int F(int x); // F(int) error void F(string[] a); // F(string[]) void F(params string[] a); // F(string[]) error void F<S>(S s); // F<0>(0) void F<T>(T t); // F<0>(0) error void F<S,T>(S s); // F<0,1>(0) void F<T,S>(S s); // F<0,1>(1) ok } Note that any ref and out parameter modifiers ( §14.6.2 ) are part of a signature. Thus, F(int) , F(ref int) , and F(out int) are all unique signatures. However, F(ref int) and F(out int) cannot be declared within the same interface because their signatures differ solely by ref and out . Also, note that the return type and the params modifier are not part of a signature, so it is not possible to overload solely based on return type or on the inclusion or exclusion of the params modifier. As such, the declarations of the methods  F(int) and F(params string[]) identified above, result in a compile-time error. end example

7.7.1 General

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. (This does not, however, remove the restriction imposed by §7.3 that within a nested block it is not possible to declare a local variable or local constant with the same name as a local variable or local constant in an enclosing block.) 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 ( §13.6 ) with no enclosing namespace_declaration is the entire program text.

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  N , followed by a period.

The scope of a name defined by an extern_alias_directive ( §13.4 ) extends over the using_directive s, global_attributes and namespace_member_declaration s of its immediately containing compilation_unit or namespace_body . An extern_alias_directive does not contribute any new members to the underlying declaration space. In other words, an extern_alias_directive is not transitive, but, rather, affects only the compilation_unit or namespace_body in which it occurs.

The scope of a name defined or imported by a using_directive ( §13.5 ) extends over the global_attributes and namespace_member_declaration s 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 type parameter declared by a type_parameter_list on a class_declaration ( §14.2 ) is the class_base , type_parameter_constraints_clauses , and class_body of that class_declaration .

Note : Unlike members of a class, this scope does not extend to derived classes. end note

The scope of a type parameter declared by a type_parameter_list on a struct_declaration ( §15.2 ) is the struct_interfaces , type_parameter_constraints_clause s, and struct_body of that struct_declaration .

The scope of a type parameter declared by a type_parameter_list on an interface_declaration ( §17.2 ) is the interface_base , type_parameter_constraints_clause s, and interface_body of that interface_declaration .

The scope of a type parameter declared by a type_parameter_list on a delegate_declaration ( §19.2 ) is the return_type , formal_parameter_list , and type_parameter_constraints_clause s of that delegate_declaration .

The scope of a type parameter declared by a type_parameter_list on a method_declaration ( §14.6.1 ) is the method_declaration .

The scope of a member declared by a class_member_declaration ( §14.3.1 ) 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 ( §7.5.3 ) of the member.

The scope of a member declared by a struct_member_declaration ( §15.3 ) is the struct_body in which the declaration occurs.

The scope of a member declared by an enum_member_declaration ( §18.4 ) is the enum_body in which the declaration occurs.

The scope of a parameter declared in a method_declaration ( §14.6 ) is the method_body of that method_declaration .

The scope of a parameter declared in an indexer_declaration ( §14.9 ) is the indexer_body of that indexer_declaration .

The scope of a parameter declared in an operator_declaration ( §14.10 ) is the operator_body of that operator_declaration .

The scope of a parameter declared in a constructor_declaration ( §14.11 ) is the constructor_initializer and block of that constructor_declaration .

The scope of a parameter declared in a lambda_expression ( §11.17 ) is the lambda_expression_body of that lambda_expression .

The scope of a parameter declared in an anonymous_method_expression ( §11.17 ) is the block of that anonymous_method_expression .

The scope of a label declared in a labeled_statement ( §12.5 ) is the block in which the declaration occurs.

The scope of a local variable declared in a local_variable_declaration ( §12.6.2 ) is the block in which the declaration occurs.

The scope of a local variable declared in a switch_block of a switch statement ( §12.8.3 ) is the switch_block .

The scope of a local variable declared in a for_initializer of a for statement ( §12.9.4 ) is the for_initializer , the for_condition , the for_iterator , and the contained statement of the for statement.

The scope of a local constant declared in a local_constant_declaration ( §12.6.3 ) is the block in which the declaration occurs. It is a compile-time error to refer to a local constant in a textual position that precedes its constant_declarator .

The scope of a variable declared as part of a foreach_statement , using_statement , lock_statement or query_expression is determined by the expansion of the given construct.

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.

Example : class A { void F() { i = 1; } int i = 0; } Here, it is valid for  F to refer to  i before it is declared. end example

Within the scope of a local variable, it is a compile-time error to refer to the local variable in a textual position that precedes the local_variable_declarator of the local variable.

Example : class A { int i = 0; void F() { i = 1; // Error, use precedes declaration int i; i = 2; } void G() { int j = (j = 1); // Valid } void H() { int a = 1, b = ++a; // Valid } } 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 results in a compile-time 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 valid because the use does not precede the local_variable_declarator . In the H  method, a subsequent local_variable_declarator correctly refers to a local variable declared in an earlier local_variable_declarator within the same local_variable_declaration . end example
Note : The scoping rules for local variables and local constants 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 were 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 compile-time 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 A {} 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); // writes "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 . end note

7.7.2 Name hiding

7.7.2.1 general.

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 subclauses.

7.7.2.2 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, local variable, and local constant declarations.

Example : In the following code 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. end example

When a name in an inner scope hides a name in an outer scope, it hides all overloaded occurrences of that name.

Example : In the following code class Outer { static void F(int i) {} static void F(string s) {} class Inner { static void F(long l) {} void G() { F(1); // Invokes Outer.Inner.F F("Hello"); // Error } } } 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") results in a compile-time error. end example

7.7.2.3 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:

The rules governing operator declarations ( §14.10 ) 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 a visible name from an inherited scope causes a warning to be reported.

Example : In the following code 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 an F  method that wasn’t present in an earlier version of the class. end example

The warning caused by hiding an inherited name can be eliminated through use of the new modifier:

Example : class Base { public void F() {} } class Derived : Base { public new void F() {} } The new modifier indicates that the F in Derived is “new”, and that it is indeed intended to hide the inherited member. end example

A declaration of a new member hides an inherited member only within the scope of the new member.

Example : class Base { public static void F() {} } class Derived : Base { private new 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 . end example

7.8 Namespace and type names

7.8.1 general.

Several contexts in a C# program require a namespace_name or a type_name to be specified.

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 shall refer to a namespace, or otherwise a compile-time error occurs. No type arguments ( §8.4.2 ) can be present in a namespace_name (only types can have type arguments).

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 shall refer to a type, or otherwise a compile-time error occurs.

If the namespace_or_type_name is a qualified_alias_member its meaning is as described in  §13.8.1 . Otherwise, a namespace_or_type_name has one of four forms:

where I is a single identifier, N is a namespace_or_type_name and <A₁, ..., Aₓ> is an optional type_argument_list . When no type_argument_list is specified, consider x to be zero.

The meaning of a namespace_or_type_name is determined as follows:

Note : Non-type members (constants, fields, methods, properties, indexers, operators, instance constructors, finalizers, and static constructors) and type members with a different number of type parameters are ignored when determining the meaning of the namespace_or_type_name . end note
Note : If the meaning of  N.I is being determined as part of resolving the base class specification of  N then the direct base class of  N is considered to be object ( §14.2.4.2 ). end note

A namespace_or_type_name is permitted to reference a static class ( §14.2.2.4 ) only if

7.8.2 Unqualified names

Every namespace declaration and type declaration has an unqualified name determined as follows:

7.8.3 Fully qualified names

Every namespace and type declaration has a fully qualified name, which uniquely identifies the namespace or type declaration amongst all others within the program. The fully qualified name of a namespace or type declaration with unqualified name  N is determined as follows:

In other words, the fully qualified name of  N is the complete hierarchical path of identifiers and generic_dimension_specifier s that lead to  N , starting from the global namespace. Because every member of a namespace or type shall have a unique name, it follows that the fully qualified name of a namespace or type declaration is always unique. It is a compile-time error for the same fully qualified name to refer to two distinct entities. In particular:

Example : 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 class G<T> // X.Y.G<> { class H {} // X.Y.G<>.H } class G<S,T> // X.Y.G<,> { class H<U> {} // X.Y.G<,>.H<> } } end example

7.9 Automatic memory management

C# employs automatic memory management, which frees developers from manually allocating and freeing the memory occupied by objects. Automatic memory management policies are implemented by a garbage collector. The memory management life cycle of an object is as follows:

Note : The C# compiler and the garbage collector might choose to analyze code to determine which references to an object might be used in the future. For instance, if a local variable that is in scope is the only existing reference to an object, but that local variable is never referred to in any possible continuation of execution from the current execution point in the procedure, the garbage collector might (but is not required to) treat the object as no longer in use. end note
Note : An object which could previously not be accessed may become accessible again due to its finalizer. An example of this is provided below. end note

The garbage collector maintains information about object usage, and uses this information to make memory management decisions, such as where in memory to locate a newly created object, when to relocate an object, and when an object is no longer in use or inaccessible.

Like other languages that assume the existence of a garbage collector, C# is designed so that the garbage collector might implement a wide range of memory management policies. C# specifies neither a time constraint within that span, nor an order in which finalizers are run. Whether or not finalizers are run as part of application termination is implementation-specific ( §7.2 ).

The behavior of the garbage collector can be controlled, to some degree, via static methods on the class System.GC . This class can be used to request a collection to occur, finalizers to be run (or not run), and so forth.

Example : Since the garbage collector is allowed wide latitude in deciding when to collect objects and run finalizers, a conforming implementation might produce output that differs from that shown by the following code. The program class A { ~A() { Console.WriteLine("Finalize instance of A"); } } class B { object Ref; public B(object o) { Ref = o; } ~B() { Console.WriteLine("Finalize instance of B"); } } class Test { static void Main() { B b = new B(new A()); b = null; GC.Collect(); GC.WaitForPendingFinalizers(); } } creates an instance of class  A and an instance of class  B . These objects become eligible for garbage collection when the variable  b is assigned the value null , since after this time it is impossible for any user-written code to access them. The output could be either Finalize instance of A Finalize instance of B or Finalize instance of B Finalize instance of A because the language imposes no constraints on the order in which objects are garbage collected. In subtle cases, the distinction between “eligible for finalization” and “eligible for collection” can be important. For example, class A { ~A() { Console.WriteLine("Finalize instance of A"); } public void F() { Console.WriteLine("A.F"); Test.RefA = this; } } class B { public A Ref; ~B() { Console.WriteLine("Finalize instance of B"); Ref.F(); } } class Test { public static A RefA; public static B RefB; static void Main() { RefB = new B(); RefA = new A(); RefB.Ref = RefA; RefB = null; RefA = null; // A and B now eligible for finalization GC.Collect(); GC.WaitForPendingFinalizers(); // B now eligible for collection, but A is not if (RefA != null) { Console.WriteLine("RefA is not null"); } } } In the above program, if the garbage collector chooses to run the finalizer of A before the finalizer of B , then the output of this program might be: Finalize instance of A Finalize instance of B A.F RefA is not null Note that although the instance of  A was not in use and A ’s finalizer was run, it is still possible for methods of  A (in this case,  F ) to be called from another finalizer. Also, note that running of a finalizer might cause an object to become usable from the mainline program again. In this case, the running of B ’s finalizer caused an instance of  A that was previously not in use, to become accessible from the live reference Test.RefA . After the call to WaitForPendingFinalizers , the instance of  B is eligible for collection, but the instance of A is not, because of the reference Test.RefA . end example

7.10 Execution order

Execution of a C# program proceeds such that the side effects of each executing thread are preserved at critical execution points. A side effect is defined as a read or write of a volatile field, a write to a non-volatile variable, a write to an external resource, and the throwing of an exception. The critical execution points at which the order of these side effects shall be preserved are references to volatile fields ( §14.5.4 ), lock statements ( §12.13 ), and thread creation and termination. The execution environment is free to change the order of execution of a C# program, subject to the following constraints:

Submit and view feedback for

Additional resources

Dot Net Tutorials

Async Main in C#

Back to: C#.NET Tutorials For Beginners and Professionals

Async Main in C# with Examples

In this article, I am going to discuss the Async Main in C# with Examples. Please read our previous article where we discussed the Thrown Expression in C# with some examples. From C# 7.1 now it is possible to define the Main method as Async. At the end of this article, you will understand what exactly Async Main is in C# and when and how to use Async Main with examples.

The Application which must have an entry point includes Windows Forms App, Console App, WPF App, ASP.NET and ASP.NET Core App, and Xamarian App. On the other hand, the Applications which do not have an entry point include the Class Library.

Activating C# 7.1 features

To make Visual Studio 2017 use some other versions of C# follow the steps shown in the below image.

Async Main in C#

For those who need some more help the steps are described here:

C# latest minor version (latest) is the option to select if you want the latest version of C#.

The Main Method in C#

Like other programming languages, the C# program also starts from the  Main  method with the following properties. 

There are four overloaded versions that are considered as the valid signatures for the  Main  method in C# as shown below.

Before C# 7.1, when we wanted to call the async method from within the Main() method, then we need to add some code but now, C# compiler does it for us. Let’s try to understand how to call the async method from Main in C# before C# 7.1 with an example as shown below.

Why we need Async Main in C#?

Async Main in C#:

From C# 7.1, the Main() method which is the entry point of the application can be declared as async. Before C# 7.1, the Main() method can have a return type as either void or int; however, now, it also supports Task and Task<int> . So From C# 7.1, now we have eight overload versions that are considered as the valid signatures for the  Main()  method as shown below.

Let us understand async main in C# with an example as shown below.

As you can see in the above example, the Task.Delay is adding 2 seconds delay in the program execution. Now, C# 7.1 syntax is simpler and easy to use.  In the above example, we see how we could use the Task with Main. Now, let’s take another example where we will see the use of Task<int>.

Here, we will call another async method AdditionAsync from Main.

You can also see that in the above example, we have used a Local function SUM that is one of the new features of C# 7.0. 

In the next article, I am going to discuss Exception Handling in C# with real-time examples. Here, in this article, I try to explain Async Main in C#  step by step with some simple examples. I hope this article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this article.

2 thoughts on “Async Main in C#”

In the last section of this article, you have written AdditionAsync(int no1, int no2) using Task, I think since it’s is an immediately available operation, should it be written ValueTask as below

private static ValueTask AdditionAsync(int no1, int no2) { return Task.Run(() => SUM(no1, no2)); //Local function int SUM(int x, int y) { return x + y; } }

Please suggest. Thanks!

“Press any key to exist” 😮

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Can't specify the 'async' modifier on the 'Main' method of a console app

I am new to asynchronous programming with the async modifier. I am trying to figure out how to make sure that my Main method of a console application actually runs asynchronously.

I know this is not running asynchronously from "the top." Since it is not possible to specify the async modifier on the Main method, how can I run code within main asynchronously?

Liam's user avatar

20 Answers 20

As you discovered, in VS11 the compiler will disallow an async Main method. This was allowed (but never recommended) in VS2010 with the Async CTP.

Update, 2017-11-30: As of Visual Studio 2017 Update 3 (15.3), the language now supports an async Main - as long as it returns Task or Task<T> . So you can now do this:

The semantics appear to be the same as the GetAwaiter().GetResult() style of blocking the main thread. However, there's no language spec for C# 7.1 yet, so this is only an assumption.

I have recent blog posts about async/await and asynchronous console programs in particular. Here's some background info from the intro post:

If "await" sees that the awaitable has not completed, then it acts asynchronously. It tells the awaitable to run the remainder of the method when it completes, and then returns from the async method. Await will also capture the current context when it passes the remainder of the method to the awaitable. Later on, when the awaitable completes, it will execute the remainder of the async method (within the captured context).

Here's why this is a problem in Console programs with an async Main :

Remember from our intro post that an async method will return to its caller before it is complete. This works perfectly in UI applications (the method just returns to the UI event loop) and ASP.NET applications (the method returns off the thread but keeps the request alive). It doesn't work out so well for Console programs: Main returns to the OS - so your program exits.

One solution is to provide your own context - a "main loop" for your console program that is async-compatible.

If you have a machine with the Async CTP, you can use GeneralThreadAffineContext from My Documents\Microsoft Visual Studio Async CTP\Samples(C# Testing) Unit Testing\AsyncTestUtilities . Alternatively, you can use AsyncContext from my Nito.AsyncEx NuGet package .

Here's an example using AsyncContext ; GeneralThreadAffineContext has almost identical usage:

Alternatively, you can just block the main Console thread until your asynchronous work has completed:

Note the use of GetAwaiter().GetResult() ; this avoids the AggregateException wrapping that happens if you use Wait() or Result .

Stephen Cleary's user avatar

You can solve this with this simple construct:

That will put everything you do out on the ThreadPool where you'd want it (so other Tasks you start/await don't attempt to rejoin a Thread they shouldn't), and wait until everything's done before closing the Console app. No need for special loops or outside libs.

Edit: Incorporate Andrew's solution for uncaught Exceptions.

Chris Moschini's user avatar

You can do this without needing external libraries also by doing the following:

p.campbell's user avatar

In C# 7.1 you will be able to do a proper async Main . The appropriate signatures for Main method has been extended to:

For e.g. you could be doing:

At compile time, the async entry point method will be translated to call GetAwaitor().GetResult() .

Details: https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main

To enable C# 7.1 language features, you need to right-click on the project and click "Properties" then go to the "Build" tab. There, click the advanced button at the bottom:

enter image description here

From the language version drop-down menu, select "7.1" (or any higher value):

enter image description here

The default is "latest major version" which would evaluate (at the time of this writing) to C# 7.0, which does not support async main in console apps.

Mahmoud Al-Qudsi's user avatar

I'll add an important feature that all of the other answers have overlooked: cancellation.

One of the big things in TPL is cancellation support, and console apps have a method of cancellation built in (CTRL+C). It's very simple to bind them together. This is how I structure all of my async console apps:

Cory Nelson's user avatar

C# 7.1 (using vs 2017 update 3) introduces async main

You can write:

For more details C# 7 Series, Part 2: Async Main

You may get a compilation error:

Program does not contain a static 'Main' method suitable for an entry point

This error is due to that vs2017.3 is configured by default as c#7.0 not c#7.1.

You should explicitly modify the setting of your project to set c#7.1 features.

You can set c#7.1 by two methods:

Method 1: Using the project settings window:

enter image description here

Method2: Modify PropertyGroup of .csproj manually

Add this property:

M.Hassan's user avatar

If you're using C# 7.1 or later, go with the nawfal's answer and just change the return type of your Main method to Task or Task<int> . If you are not:

The final code looks like:

Şafak Gür's user avatar

Haven't needed this much yet, but when I've used console application for Quick tests and required async I've just solved it like this:

Johan Falk's user avatar

For asynchronously calling task from Main, use

Task.Run() for .NET 4.5

Task.Factory.StartNew() for .NET 4.0 (May require Microsoft.Bcl.Async library for async and await keywords)

Details: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx

user3408030's user avatar

In Main try changing the call to GetList to:

Fredrik Ljung's user avatar

When the C# 5 CTP was introduced, you certainly could mark Main with async ... although it was generally not a good idea to do so. I believe this was changed by the release of VS 2013 to become an error.

Unless you've started any other foreground threads, your program will exit when Main completes, even if it's started some background work.

What are you really trying to do? Note that your GetList() method really doesn't need to be async at the moment - it's adding an extra layer for no real reason. It's logically equivalent to (but more complicated than):

Jon Skeet's user avatar

Newest version of C# - C# 7.1 allows to create async console app. To enable C# 7.1 in project, you have to upgrade your VS to at least 15.3, and change C# version to C# 7.1 or C# latest minor version . To do this, go to Project properties -> Build -> Advanced -> Language version.

After this, following code will work:

Kedrzu's user avatar

As of C# 7.1 the following signatures are valid for the Main method.

So, now you can do async/await

Theodor Zoulias's user avatar

On MSDN, the documentation for Task.Run Method (Action) provides this example which shows how to run a method asynchronously from main :

Note this statement that follows the example:

The examples show that the asynchronous task executes on a different thread than the main application thread.

So, if instead you want the task to run on the main application thread, see the answer by @StephenCleary .

And regarding the thread on which the task runs, also note Stephen's comment on his answer:

You can use a simple Wait or Result , and there's nothing wrong with that. But be aware that there are two important differences: 1) all async continuations run on the thread pool rather than the main thread, and 2) any exceptions are wrapped in an AggregateException .

(See Exception Handling (Task Parallel Library) for how to incorporate exception handling to deal with an AggregateException .)

Finally, on MSDN from the documentation for Task.Delay Method (TimeSpan) , this example shows how to run an asynchronous task that returns a value:

Note that instead of passing a delegate to Task.Run , you can instead pass a lambda function like this:

Community's user avatar

In my case I had a list of jobs that I wanted to run in async from my main method, have been using this in production for quite sometime and works fine.

user_v's user avatar

To avoid freezing when you call a function somewhere down the call stack that tries to re-join the current thread (which is stuck in a Wait), you need to do the following:

(the cast is only required to resolve ambiguity)

Nathan Phillips's user avatar

Kristaps Koks's user avatar

This is hypothetical but I am thinking:

Tom Charles Zhang's user avatar

Not sure if this is what you're looking for, but I wanted to await a method on load. I ended up using the Main_Shown handler and making it async:

Andrew Gale's user avatar

The following code can be used to make a main async. I've tweak it to use long running tasks (learn more here: https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcreationoptions?view=net-7.0 )

It's also implementing the cancellation token from the above response.

In the following example I wrote. You can play with maxDegreeOfParallelism & numberOfIteration to understand / see how the task are handle. Good statring point for learning TPL!

Iannick 's user avatar

Not the answer you're looking for? Browse other questions tagged c# .net asynchronous console-application or ask your own question .

Hot Network Questions

static task main

Your privacy

By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy .

C# Corner

C#

Working With Async Main In C# 7.1

static task main

This article explains the async main feature introduced as part of C# 7.1 and demonstrates how to use it in application development.

Hello folks!

I am here to present the series related to C# 7.1's new features. In the first part, we will be going through one of the important features called async main.

Starting with C# 7.1, the main function that is the entry point of the application can have async. Before C# 7.1, the main function could have a return type as either void or int; however now, it also supports Task and Task<int>.

Let’s take a few examples to understand more.

Before C# 7.1, when you wanted to call async method from Main, you needed to add some boilerplate code but now, C# compiler does it for you and, in turn, enforces crisp coding by adding the required code automatically.

Let’s try to understand this by a simple example as following.

Before C# 7.1

Output

Output

As you can see that Task.Delay is adding 2 seconds. Now, C# 7.1 syntax is crispier and easy to use.

So we just looked at how we could use the Task with Main. Now, let’s take another example where we will demonstrate the use of Task<int>.

Here, we will call another async method (FactorialAsync) from Main.

Output

You can also see that in the above example, we have used a Local function that is the feature of C# 7.0. Moreover, we can also use expression-bodied method (introduced as part of C# 6.0) to call the async method directly from Main, as following.

Output

In this article, we have looked at one of the quite useful features “async main”, introduced as part of C# 7.1. We have seen how to use Task and Task<int> with Main. On the sideline, we have also seen the use of Local function and expression bodied method.

Hope you liked the article. I look forward to your comments/suggestions.

C# Corner Ebook

FileInfo in C#

Featured articles.

Learn

Gunnar Peipman

Gunnar Peipman – Programming Blog

ASP.NET Core, Blazor, .NET, Azure, SharePoint, IoT

C#

C# 7.1 introduces asynchronous Main method for console applications. It helps us to get rid of some ugly code to await for asynchronous methods. If Main method of console applications is application flow controller that makes multiple calls to asynchronous methods then async Main leads us to much cleaner code. This blog post is my deep dive to async Main.

New features in C# 7.1

Suppose we have console application built on .NET Core 2.0. Our application performs some simple tasks using asynchronous calls. We end up with Main method like shown below (I use dummy code to keep it simple).

It works okay and technically there is nothing wrong but still we drag out the fact that we have to wait for async method to finish. It’s not a big deal but when we look at it from platform provider side then there will be tons of solutions that repeat the same thing over and over again: wait until asynchronous method called in Main finishes.

From C# 7.1 there is support for asynchronous Main method at framework level meaning that there is no more need to manually wait for async methods to complete and it’s very simple. The previous code can be written like here.

But what about console applications returning integer? These are supported too. Here is the full list of supported async Main methods:

I think these overloads should be enough for console applications.

Who awaits?

As Main method is entry point on application we can ask how await is implemented? Main is topmost method of application.

To answer the question let’s see what’s going on behind the compiler. After building solution (in case of errors skip for a moment to next section) let’s see what’s inside DLL-file using dotPeek by JetBrains.

The trick here is private version of Main method that is invisible to IDE-s and code editors. This is the actual entry point of application and we can prove it by checking out the IL code for <Main> method.

Async Main stub in ILDASM

SpecialNameAttribute is added to all special methods by compiler. Don’t jump to any conclusions as this attribute is actually not used right now. Documentation sais it is reserved for future use.

Emulating async Main on earlier versions of C#

As we saw then running asynchronous code in synchronous method involves some simple tricks that are easy to produce. Still we would like to hide these tricks away or at least move to somewhere else so we doesn’t pollute our own code. On C# 7.1 things were simple but what we can do with applications built on earlier versions?

Simple thing to do is to have classic Main method that calls for asynchronous one that is part of our application. The point is to keep classic Main method as a stub that implements awaiting logic.

It’s similar solution that C# 7.1 hides behind the compiler. As we cannot hide anything in Visual Studio IDE we can just agree that let’s handle classic Main method as a minimal stub and implement all actual Main method logic in AsyncMain method.

Activating C# 7.1 features

If console application with async Main doesn’t compile it’s possible that Visual Studio is not using C# 7.1 compiler. To switch over to newer compiler version open project properties and advanced build settings. Select C# 7.1 as language version.

advanced-language-settings

Click Okay and build solution again.

Practical use case. As many libraries today are providing async calls by default the use of async Main will also gain more popularity. One good example is Entity Framework Core 2.0 that introduced async counterparts for most important querying methods. Read my blog post Entity Framework Core with .NET Core console application for detailed example of async Main and Entity Framework Core.

Wrapping up

Async main doesn’t seem like a big thing and it only cleans some ugliness out from Main method of console applications. Still we get cleaner code and at least for me this is important thing to achieve. Additionally it also decreases danger that some more artistic developers start inventing their own hacks to wait for async method to return. One may ask how much cleaner the code gets dut to this small language feature? Well, like always, it depends, but the rule of thumb is simple: the more async method calls in Main the bigger the effect of async Main. Async main is called by hidden <Main> method that is like the classic one. It is automatically created by compiler and it is not visible for development tools. Still it is easy to emulate it on earlier versions of C# to keep code cleaner.

More compiler secrets

Gunnar Peipman

Gunnar Peipman is ASP.NET, Azure and SharePoint fan, Estonian Microsoft user group leader, blogger, conference speaker, teacher, and tech maniac. Since 2008 he is Microsoft MVP specialized on ASP.NET.

' src=

You May Also Like

C#

EnumHelper class

ASP.NET

Converting System.Drawing.Color to hex

Visual Studio

What is Visual Studio Async?

Gunnar Peipman

Writing object to object mapper: moving to generics

Beer IoT

Beer IoT: Measuring cooling rate

Displaying custom html in webbrowser control, 3 thoughts on “ deep dive to async main ”.

Pingback: The Morning Brew - Chris Alcock » The Morning Brew #2480

' src=

Hi, I have a question, .net core turn all i/o calls to async/await model but why I would have to use or what would be the benefit of using async/await in a win service or console that run without a UI ?

' src=

It really depends on what you service or console application is doing. Using async calls doesn’t make program run faster but what gets better is throughput as machine can serve more threads with same hardware resources.

Leave a Reply

Your email address will not be published. Required fields are marked *

  Notify me when new comments are added.

C# 7 Async Main

Fastest entity framework extensions, what is main method.

The Main method is the entry point of a C# application. When the application is started, the Main method is the first method that is invoked.

Before C# 7.1, four overloaded versions were considered valid signatures for the Main method in C#, as shown below.

From C# 7.1, it is also possible to define the Main method as async with any of the following additional overloads.

An async Main method enables you to use await in your Main method. Before C# 7.1, when you want to call the async method from the Main method, you need to add some boilerplate code, as shown below.

Now in C# 7.1, the syntax is simpler and easy to use only using the async main.

If your program returns an exit code, you can declare a Main method that returns a Task<int> .

logo rip

Get monthly updates about new articles, cheatsheets, and tricks.

Asynchronous programming with async, await, Task in C#

C# and .NET Framework (4.5 & Core) supports asynchronous programming using some native functions, classes, and reserved keywords.

Before we see what is asynchronous programming, let's understand what is synchronous programming using the following console example.

In the above example, the LongProcess() method is some long-running task such as reading a file from the server, calling a web API that returns a large amount of data or uploading or downloading a big file. It takes a little longer time to execute ( Thread.Sleep(4000) holds it for 4 seconds just to show long execution time). The ShortProcess() is a simple method that gets executed after the LongProcess() method.

The above program executes synchronously. It means execution starts from the Main() method wherein it first executes the LongProcess() method and then ShortProcess() method. During the execution, an application gets blocked and becomes unresponsive (You can see this in Windows-based applications mainly). This is called synchronous programming where execution does not go to next line until the current line executed completely.

What is Asynchronous Programming?

In asynchronous programming, the code gets executed in a thread without having to wait for an I/O-bound or long-running task to finish. For example, in the asynchronous programming model, the LongProcess() method will be executed in a separate thread from the thread pool, and the main application thread will continue to execute the next statement.

Microsoft recommends Task-based Asynchronous Pattern  to implement asynchronous programming in the .NET Framework or .NET Core applications using async , await keywords and Task or Task<TResult> class.

Now let's rewrite the above example in asynchronous pattern using async keyword.

In the above example, the Main() method is marked by the async keyword, and the return type is Task . The async keyword marks the method as asynchronous. Note that all the methods in the method chain must be async in order to implement asynchronous programming. So, the Main() method must be async to make child methods asynchronous.

The LongProcess() method is also marked with the async keyword which makes it asynchronous. The await Task.Delay(4000); holds the thread execute for 4 seconds.

Now, the program starts executing from the async Main() method in the main application thread. The async LongProcess() method gets executed in a separate thread and the main application thread continues execution of the next statement which calls ShortProcess() method and does not wait for the LongProcess() to complete.

async, await, and Task

Use async along with await and Task if the async method returns a value back to the calling code. We used only the async keyword in the above program to demonstrate the simple asynchronous void method.

The await keyword waits for the async method until it returns a value. So the main application thread stops there until it receives a return value.

The Task class represents an asynchronous operation and Task<TResult> generic class represents an operation that can return a value. In the above example, we used await Task.Delay(4000) that started async operation that sleeps for 4 seconds and await holds a thread until 4 seconds.

The following demonstrates the async method that returns a value.

In the above example, in the static async Task<int> LongProcess() method, Task<int> is used to indicate the return value type int. int val = await result; will stop the main thread there until it gets the return value populated in the result. Once get the value in the result variable, it then automatically assigns an integer to val .

An async method should return void ,  Task , or  Task<TResult> , where TResult is the return type of the async method. Returning void is normally used for event handlers. The async keyword allows us to use the await keyword within the method so that we can wait for the asynchronous method to complete for other methods which are dependent on the return value.

If you have multiple async methods that return the values then you can use await for all methods just before you want to use the return value in further steps.

In the above program, we do await result1 and await result2 just before we need to pass the return value to another method.

Thus, you can use async , await, and Task to implement asynchronous programming in .NET Framework or .NET Core using C#.

static task main

tutorialsteacher.com is a free self-learning technology web site for beginners and professionals.

C# async Main()

Quick tip on how to solve the "not suitable main method" error., june 21, 2018.

C# 7.1 extended valid signatures for the static Main() method to allow the async keyword. Valid starting points of .NET applications are now:

Thanks to the Task return type, it's now possible to use async/await with Main.

And now to the issue: You have to specifially select C# 7.1 or higher to enable support for async Main.

If you don't do that, you will probably get this error message on build:

static task main

Program does not contain a static 'Main' method suitable for an entry point.

Solution is simple, but not obvious:

static task main

Found something inaccurate or plain wrong? Was this content helpful to you? Let me know!

📧 [email protected]

static task main

Author avatar

Advanced Tips for Using Task.Run With Async/Await

Deepening Our Understanding

Where to put a call to task.run, don't continue on the main thread unnecessarily, should i use task.run with asp.net core, what about task.factory.startnew.

In the previous guide in this series we saw why Task.Run is mainly useful for CPU-bound code. In exploring that topic it became clear that, although you can use Task.Run with other types of operations, it may not be the best use of system resources. We also saw how easy it is to await a call to Task.Run . But that's certainly not all there is to it. There are a few tips that can save you a lot of headaches if you know them.

Let's pick up where we left off with the application from the previous guide. That application downloaded an image and then blurred it using a library called ImageSharp (available in NuGet as SixLabors.ImageSharp ). We defined a method called BlurImage as follows:

Notice that the call to Task.Run is immediately before the image processing code. Is that the best approach?

Whether for convenience or clarity, you might find yourself putting a call to Task.Run as close as possible to the CPU intensive code, much like in the above method. As your application increases in complexity though, this turns out to be suboptimal. To illustrate this, imagine if in the future we wanted to add a method to our application that would rotate, darken, and blur. We might start by writing something like the following:

But then we notice that BlurImage (or a version of it that accepts a byte array) already returns a Task , so we change it to:

And then we notice that BlurImage itself calls Task.Run , which means we now have a nested Task.Run call. So we would be launching a thread from within another thread. This is again, not the best use of system resources, and will probably have a negative impact on performance. This is why library authors are discouraged from using Task.Run in library methods: It should be up to the caller when threads are launched.

Therefore, it's generally recommended that you put calls to Task.Run as close to the UI code and event handlers as possible. In following that recommendation you'll find that most CPU-bound code ends up being written as synchronous, and Task.Run goes in the outermost calling method. So, in this example, we'd end up with something like:

...and BlurImage would simply be:

As you probably recall, await captures information about the current thread when used with Task.Run . It does that so execution can continue on the original thread when it is done processing on the other thread. But what if the rest of the code in the calling method doesn't need to be run on the original thread?

In that case, it turns out you can give your code a bit of a performance boost by telling await that you don't want to continue in the original context. This is done by using a Task method called ConfigureAwait . A good example would be in the OnButtonClick method we defined earlier:

Defining a variable just for that is a bit verbose though, so most of the time one would just attach it to the end of the call to Task.Run :

The parameter to ConfigureAwait is a boolean named continueOnCapturedContext , and the default is true . By passing false instead, we're indicating that we wish to continue the rest of the method on a thread pool thread instead of the UI thread. As long as you're not modifying any UI elements in the code following the await (or doing anything else there that would require the main thread of your application), you can safely use this technique to enable an amount of parallelism.

Library authors that use await are especially encouraged to use ConfigureAwait(false) , as not doing so can cause deadlocks depending on how application developers are consuming your library methods. Most of the time library code does not care what thread it runs on, so using ConfigureAwait(false) will ensure you library code is never waiting on the main thread.

Now I must admit, ConfigureAwait(false) is not the greatest syntax, and its presence does clutter the code somewhat. Indeed, I wish there were a better way. But the less you run on your application's main thread, the faster the application will seem to the end user. So do use ConfigureAwait(false) where applicable. Your application's users will thank you!

Thus far we've talked about UI-based applications, but does this information about Task.Run apply to a web application framework such as ASP.NET Core?

There are certainly a number of advantages to using async/await with ASP.NET Core, but the same cannot be said for Task.Run . As it turns out, using thread pool threads doesn't make much sense when you're serving a web page. Generally, with ASP.NET there is one thread per request and you want to be able to handle as many requests concurrently as possible. Using Task.Run in that context actually reduces scalability because you're reducing the number of threads available to handle new requests. Furthermore, using a separate thread won't do anything for responsiveness, since the user is not able to interact with the page until it is loaded anyway. And once the page is loaded, responsiveness is primarily determined by the user's client-side browser interactions (and the quality of the JavaScript code), not by ASP.NET. So, for CPU-bound code in ASP.NET, it's best to stick to synchronous processing. In short, avoid using Task.Run in ASP.NET applications. If you are using async/await , focus on the naturally asynchronous I/O operations!

You may stumble across a similar method at Task.Factory.StartNew and be curious about it, so it is worth a brief mention. Actually, the method at Task.Factory.StartNew was introduced before Task.Run and is more configurable. However, it's best to stick with Task.Run . Except for a few very specific needs that are well outside normal application requirements (not to mention the scope of this guide), you really never need the additional complexity that Task.Factory.StartNew provides, and Task.Run is more succinct anyway. Don't try to get clever; just use Task.Run !

Understanding when and how to use Task.Run is important for any C# developer wanting to keep their applications responsive. As we saw with ASP.NET, sometimes the answer is to not use Task.Run at all! By contrast, for applications with user interfaces, it's the primary way to run CPU-bound code in a non-blocking fashion. For those situations, keep in mind the best practice regarding where to put the calls to Task.Run , and you'll surely find success using it conjunction with async/await .

IMAGES

  1. Classification of static task scheduling algorithms at compile time.

    static task main

  2. Classification of static task scheduling algorithms at compile time.

    static task main

  3. Classification of static task scheduling algorithms

    static task main

  4. static methods in java

    static task main

  5. Example of a static task graph. Values in parenthesis inside the nodes...

    static task main

  6. @staticmethod in Python

    static task main

VIDEO

  1. AirBnB web Static (Task 5) More filter

  2. #Conditions Task #No.1

  3. task 0 init

  4. AirBnB Web Statics (Task 1)

  5. Vlog 6 😆😜 Task Main Papa ne Mummy ke pair dabaye 😃🥳 or Mummy ne papa beta ke Kan Khinche

  6. MC Stan ne show se exit ka bolai

COMMENTS

  1. What Is Task Interdependence?

    Task interdependence sets rules and guidelines for the sharing of expertise, materials and information between members of an organization working on interdependent tasks.

  2. What Is a Task Environment?

    An organization’s task environment is the collection of factors that affects its ability to achieve goals. Common factors in the task environment include competitors, customers, suppliers and distributors.

  3. What Is a Main Switch?

    A main switch is a central cut-off switch that controls the smaller cut-off switches and machines of a building. The main switch can be cut off by a human or a computerized system to control the flow of power in the building.

  4. Basic concepts

    Task<int> is not considered an entry point method. ... class A {} class Test { static void Main() { string A = "hello, world"; string s = A;

  5. Async Main in C# with examples

    public static Task<int> Main(string[] args);.

  6. Can't specify the 'async' modifier on the 'Main' method of a console app

    In C# 7.1 you will be able to do a proper async Main. The appropriate signatures for Main method has been extended to: public static Task

  7. Working With Async Main In C# 7.1

    This article explains the async main feature introduced as part of C# ... static async Task Main(string[] args) => WriteLine($"Factorial:

  8. Deep dive to async Main

    What is async Main method in C# 7.1 and how to use it? ... public static async Task Main(string[] args) { await Task.Delay(0); Console.

  9. C# 7

    From C# 7.1, it is also possible to define the Main method as async with any of the following additional overloads. static Task Main(); static Task < int >

  10. The Main() Method in C#

    //parameterless Main() methods public static void Main() { } public ... //Use of Task return type in the Main() method public static async

  11. Asynchronous programming with async, await, Task in C#

    Now let's rewrite the above example in asynchronous pattern using async keyword. Example: Asynchronous Program. Copy. static async Task Main(

  12. C# async Main()

    C# 7.1 extended valid signatures for the static Main() method to allow ... public static int Main(string[] args); public static Task Main();

  13. CS5001 Program does not contain a static Main method suitable for

    I enabled C# 7.1 Language Level and my code is as simple as the following but it does not compile. public class Program { public static async Task Main() { var

  14. Advanced Tips for Using Task.Run with Async/Await

    1static async Task<byte[]> BlurImage(string imagePath) 2{ 3 return await Task. ... Don't Continue on the Main Thread Unnecessarily.