Some notable features of C# that distinguish it from C, C++, and Java where noted, are:
Portability By design, C# is the programming language that most directly reflects the underlying
Common Language Infrastructure (CLI). Most of its intrinsic types correspond to value-types implemented by the CLI framework. However, the language specification does not state the code generation requirements of the compiler: that is, it does not state that a C# compiler must target a
Common Language Runtime (CLR), or generate
Common Intermediate Language (CIL), or generate any other specific format. Some C# compilers can also generate machine code like traditional compilers of Objective-C, C, C++, Assembly and
Fortran.
Typing C# supports strongly, implicitly typed variable declarations with the keyword var, and implicitly typed arrays with the keyword new[] followed by a collection initializer. Its type system is split into two families: Value types, like the built-in numeric types and user-defined structs, which are automatically handed over as copies when used as parameters, and reference types, including arrays, instances of classes, and strings, which only hand over a pointer to the respective object. Due to their special handling of the equality operator and their
immutability, strings will nevertheless behave as if they were values, for all practical purposes. The programmer can even use them as
case labels. Where necessary, value types will be
boxed automatically. C# supports a strict
Boolean data type, bool. Statements that take conditions, such as while and if, require an expression of a type that evaluates to the true boolean value. While C++ also has a Boolean type, it can be freely converted to and from integers, and expressions such as if (a) require only that a is convertible to bool, allowing a to be an int, or a pointer. C# disallows this "integer meaning true or false" approach, on the grounds that forcing programmers to use expressions that return exactly bool can prevent certain types of programming mistakes such as if (a = b) (use of assignment = instead of equality ==). C# is more
type safe than C++. The only
implicit conversions by default are those that are considered safe, such as widening of integers. This is enforced at compile-time, during
Just-in-time compilation, and, in some cases, at runtime. No implicit conversions occur between Booleans and integers, nor between enumeration members and integers (except for literal 0, which can be implicitly converted to any enumerated type). Any user-defined conversion must be explicitly marked as explicit or implicit, unlike C++
copy constructors and conversion operators, which are both implicit by default. C# has explicit support for
covariance and contravariance in generic types, unlike C++ which has some degree of support for contravariance simply through the semantics of return types on virtual methods.
Enumeration members are placed in their own
scope. The C# language does not allow for global variables or functions. All methods and members must be declared within classes. Static members of public classes can substitute for global variables and functions. Local variables cannot
shadow variables of the enclosing block, unlike C and C++, but may shadow type-level names.
Metaprogramming Metaprogramming can be achieved in several ways: •
Reflection is supported through .NET APIs, which enable scenarios such as type metadata inspection and dynamic method invocation. • Expression trees represent code as an
abstract syntax tree, where each node is an expression that can be inspected or executed. This enables dynamic modification of executable code at runtime. Expression trees introduce some
homoiconicity to the language. •
Attributes, in C# parlance, are
metadata that can be attached to types, members, or entire
assemblies, equivalent to
annotations in Java. Attributes are accessible both to the compiler and to code through reflection, allowing them to adjust their behaviour. Many of the native attributes duplicate the functionality of GCC's and VisualC++'s platform-dependent preprocessor directives. • System.Reflection.Emit namespace, which contains classes that emit metadata and
CIL (types, assemblies, etc.) at
runtime. •
The .NET Compiler Platform (Roslyn) provides API access to language compilation services, allowing for the compilation of C# code from within .NET applications. It exposes APIs for syntactic (
lexical) analysis of code,
semantic analysis, dynamic compilation to CIL, and code emission. • Source generators, a feature of the Roslyn C# compiler, enable compile time metaprogramming. During the compilation process, developers can inspect the code being compiled with the compiler's
API and pass additional generated C# source code to be compiled.
Methods and functions A
method in C# is a member of a class that can be invoked as a
function, rather than the mere value-holding capability of a
field, i.e.,
class or
instance variable. As in other syntactically similar languages, such as
C++ and
ANSI C, the signature of a method is a declaration comprising in order: any optional accessibility keywords (such as private), the explicit specification of its return type (such as int, or the keyword void if no value is returned), the name of the method, and finally, a parenthesized sequence of comma-separated parameter specifications, each consisting of a parameter's type, its formal name and optionally, a default value to be used whenever none is provided. Different from most other languages,
call-by-reference parameters have to be marked both at the function definition and at the calling site, and the programmer can choose between ref and out, the latter allowing handing over an uninitialized variable which will have a definite value on return. Additionally, the programmer can specify a
variable-sized argument list by applying the params keyword to the last parameter. Certain specific kinds of methods, such as those that simply get or set a field's value by returning or assigning it, do not require an explicitly stated full signature, but in the general case, the definition of a class includes the full signature declaration of its methods. Like C++, and unlike Java, C# programmers must use the scope modifier keyword virtual to allow methods to be
overridden by subclasses. Unlike C++, the programmer must explicitly specify the keyword override when doing so. This is supposed to avoid confusion between overriding and newly overloading a function, i.e., hiding the former implementation. To do the latter, the programmer has to specify the new keyword. The keyword sealed can be used to disallow further overrides for individual methods or whole classes.
Extension methods in C# allow programmers to use static methods as if they were methods from a class's method table, allowing programmers to virtually add instance methods to a class that they feel should exist on that kind of objects (and instances of the respective derived classes). The type dynamic allows for run-time method binding, allowing for JavaScript-like method calls and run-time
object composition. C# has support for strongly-typed
function pointers via the keyword delegate. Like the
Qt framework's
pseudo-C++
signal and
slot, C# has semantics specifically surrounding publish-subscribe style events, though C# uses delegates to do so. Unlike fields, event variables can be part of an interface, because they technically consist of two default functions to add and remove to-be-called delegates. C# offers Java-like synchronized method calls, via the attribute [MethodImpl(MethodImplOptions.Synchronized)], and has support for
mutually-exclusive locks via the keyword lock.
Properties C# supports classes with
properties. The properties can be simple accessor functions with a backing field, or implement arbitrary getter and setter functions. A property is read-only if there's no setter. Like with fields, there can be class and instance properties. The underlying methods can be virtual or
abstract like any other method. where the
accessor (getter) and mutator (setter) encapsulate operations on a single field of a class.
Namespaces A C# namespace provides the same level of code isolation as a Java package or a C++ , with rules and features very similar to a package. Namespaces can be imported with the "using" syntax.
Memory access In C#, memory address pointers can only be used within blocks specifically marked as
unsafe, and programs with unsafe code need appropriate permissions to run. Most object access is done through safe object references, which always either point to a "live" object or have the well-defined
null value; it is impossible to obtain a reference to a "dead" object (one that has been garbage collected), or to an arbitrary block of memory. An unsafe pointer can point to an instance of an unmanaged value type that does not contain any references to objects subject to garbage collections such as class instances, arrays or strings. Code that is not marked as unsafe can still store and manipulate pointers through the System.IntPtr type, but it cannot dereference them. Managed memory cannot be explicitly freed; instead, it is automatically
garbage collected. Garbage collection addresses the problem of
memory leaks by freeing the programmer of responsibility for releasing memory that is no longer needed in most cases. Code that retains references to objects longer than is required can still experience higher memory usage than necessary, however once the final reference to an object is released the memory is available for garbage collection.
Exceptions A range of standard
exceptions are available to programmers. Methods in standard libraries regularly throw system exceptions in some circumstances and the range of exceptions thrown is normally documented. Custom exception classes can be defined for classes allowing handling to be put in place for particular circumstances as needed. The syntax for handling exceptions is the following:try { // something } catch (Exception ex) { // if error do this } finally { // always executes, regardless of error occurrence }Most of the time people call this a "try-catch" code block, because of the "try" and "catch" functions being used and accessible on all C# versions. try { // something here } catch (Exception ex) { // example return 0; } finally { return 1; } Depending on your plans, the "finally" part can be left out. If inspecting the error details is not required, the (Exception ex) parameter can be omitted as well. Also, there can be several "catch" parts handling different kinds of exceptions.
Checked exceptions are not present in C# (in contrast to Java). This has been a conscious decision based on the issues of scalability and version management.
Polymorphism Unlike
C++, C# does not support
multiple inheritance, although a class can implement any number of "
interfaces" (fully abstract classes). This was a design decision by the language's lead architect to avoid complications and to simplify architectural requirements throughout
CLI. When implementing multiple interfaces that contain a method with the same name and taking parameters of the same types in the same order, i.e., the same
signature, similar to
Java, C# allows both a single method to cover all interfaces and, if necessary, specific methods for each interface. C# also offers
function overloading (a.k.a.
ad-hoc-polymorphism), i.e., methods with the same name, but distinguishable signatures. Unlike Java, C# additionally supports
operator overloading. Since version 2.0, C# offers
parametric polymorphism, i.e., classes with arbitrary or constrained type parameters, e.g., List<T>, a variable-sized array which only can contain elements of type T. There are certain kinds of constraints the programmer can specify for the type parameters: Has to be type X (
or one derived from it), has to implement a certain interface, has to be a reference type, has to be a value type, has to implement a public parameterless
constructor. Most of them can be combined, and any number of interfaces can be specified.
Language Integrated Query (LINQ) C# has the ability to utilize
LINQ through the .NET Framework. A developer can query a variety of data sources, provided the IEnumerable<T> interface is implemented on the object. This includes
XML documents, an
ADO.NET dataset, and
SQL databases. Using
LINQ in C# brings advantages like
IntelliSense support, strong filtering capabilities, type safety with compile error checking ability, and consistency for querying data over a variety of sources. There are several different language structures that can be utilized with C# and LINQ and they are query expressions, lambda expressions, anonymous types, implicitly typed variables, extension methods, and object initializers. LINQ has two syntaxes: query syntax and method syntax. However, the compiler always converts the query syntax to method syntax at compile time. using System.Linq; var numbers = new int[] { 5, 10, 8, 3, 6, 12 }; // Query syntax (SELECT num FROM numbers WHERE num % 2 = 0 ORDER BY num) var numQuery1 = from num in numbers where num % 2 == 0 orderby num select num; // Method syntax var numQuery2 = numbers .Where(num => num % 2 == 0) .OrderBy(n => n);
Functional programming Though primarily an imperative language, C# always adds functional features over time, for example: •
Functions as first-class citizen – C# 1.0 delegates •
Higher-order functions – C# 1.0 together with delegates •
Anonymous functions – C# 2 anonymous delegates and C# 3 lambdas expressions •
Closures – C# 2 together with anonymous delegates and C# 3 together with lambdas expressions •
Nested functions – C# 7.0 and Init only setters •
Type classes – C# 12 roles/extensions (in development) ==Common type system==