dotnet-csharp
dotnet-csharp
C# documentation
Learn how to write any application using the C# programming language on the .NET
platform.
Learn to program
b GET STARTED
q VIDEO
g TUTORIAL
Self-guided tutorials
In-browser tutorial
i REFERENCE
C# on Q&A
C# on Stack Overflow
C# on Discord
Fundamentals
e OVERVIEW
A tour of C#
Inside a C# program
p CONCEPT
Type system
Object oriented programming
Functional techniques
Exceptions
Coding style
g TUTORIAL
Display command-line
Intro to classes
Object oriented C#
Converting types
Pattern matching
What's new
h WHAT'S NEW
What's new in C# 13
What's new in C# 12
What's new in C# 11
What's new in C# 10
g TUTORIAL
i REFERENCE
Version compatibility
Key concepts
e OVERVIEW
C# language strategy
Programming concepts
p CONCEPT
Asynchronous programming
Advanced concepts
i REFERENCE
Expression trees
Native interoperability
Performance engineering
Stay in touch
i REFERENCE
YouTube
Twitter
A tour of the C# language
Article • 05/08/2024
The C# language is the most popular language for the .NET platform, a free, cross-
platform, open source development environment. C# programs can run on many
different devices, from Internet of Things (IoT) devices to the cloud and everywhere in
between. You can write apps for phone, desktop, and laptop computers and servers.
Hello world
The "Hello, World" program is traditionally used to introduce a programming language.
Here it is in C#:
C#
The line starting with // is a single line comment. C# single line comments start with //
and continue to the end of the current line. C# also supports multi-line comments.
Multi-line comments start with /* and end with */ . The WriteLine method of the
Console class, which is in the System namespace, produces the output of the program.
This class is provided by the standard class libraries, which, by default, are automatically
referenced in every C# program.
The preceding example shows one form of a "Hello, World" program, using top-level
statements. Earlier versions of C# required you to define the program's entry point in a
method. This format is still valid, and you'll see it in many existing C# samples. You
should be familiar with this form as well, as shown in the following example:
C#
using System;
class Hello
{
static void Main()
{
// This line prints "Hello, World"
Console.WriteLine("Hello, World");
}
}
This version shows the building blocks you use in your programs. The "Hello, World"
program starts with a using directive that references the System namespace.
Namespaces provide a hierarchical means of organizing C# programs and libraries.
Namespaces contain types and other namespaces—for example, the System namespace
contains many types, such as the Console class referenced in the program, and many
other namespaces, such as IO and Collections . A using directive that references a
given namespace enables unqualified use of the types that are members of that
namespace. Because of the using directive, the program can use Console.WriteLine as
shorthand for System.Console.WriteLine . In the earlier example, that namespace was
implicitly included.
The Hello class declared by the "Hello, World" program has a single member, the
method named Main . The Main method is declared with the static modifier. While
instance methods can reference a particular enclosing object instance using the keyword
this , static methods operate without reference to a particular object. By convention,
when there are no top-level statements a static method named Main serves as the entry
point of a C# program.
Both entry point forms produce equivalent code. When you use top-level statements,
the compiler synthesizes the containing class and method for the program's entry point.
Tip
The examples in this article give you a first look at C# code. Some samples may
show elements of C# that you're not familiar with. When you're ready to learn C#,
start with our beginner tutorials, or dive into the links in each section. If you're
experienced in Java, JavaScript, TypeScript or Python, read our tips to help you
find the information you need to quickly learn C#.
Familiar C# features
C# is approachable for beginners yet offers advanced features for experienced
developers writing specialized applications. You can be productive quickly. You can learn
more specialized techniques as you need them for your applications.
C# apps benefit from the .NET Runtime's automatic memory management. C# apps also
use the extensive runtime libraries provided by the .NET SDK. Some components are
platform independent, like file system libraries, data collections, and math libraries.
Others are specific to a single workload, like the ASP.NET Core web libraries, or the .NET
MAUI UI library. A rich Open Source ecosystem on NuGet augments the libraries that
are part of the runtime. These libraries provide even more components you can use.
C# is a strongly typed language. Every variable you declare has a type known at compile
time. The compiler, or editing tools tell you if you're using that type incorrectly. You can
fix those errors before you ever run your program. Fundamental data types are built into
the language and runtime: value types like int , double , char , reference types like
string , arrays, and other collections. As you write your programs, you create your own
types. Those types can be struct types for values, or class types that define object-
oriented behavior. You can add the record modifier to either struct or class types so
the compiler synthesizes code for equality comparisons. You can also create interface
definitions, which define a contract, or a set of members, that a type implementing that
interface must provide. You can also define generic types and methods. Generics use
type parameters to provide a placeholder for an actual type when used.
As you write code, you define functions, also called methods, as members of struct and
class types. These methods define the behavior of your types. Methods can be
C# apps use exceptions to report and handle errors. You'll be familiar with this practice if
you've used C++ or Java. Your code throws an exception when it can't do what was
intended. Other code, no matter how many levels up the call stack, can optionally
recover by using a try - catch block.
Distinctive C# features
Some elements of C# might be less familiar. Language integrated query (LINQ) provides
a common pattern-based syntax to query or transform any collection of data. LINQ
unifies the syntax for querying in-memory collections, structured data like XML or JSON,
database storage, and even cloud based data APIs. You learn one set of syntax and you
can search and manipulate data regardless of its storage. The following query finds all
students whose grade point average is greater than 3.5:
C#
The preceding query works for many storage types represented by Students . It could be
a collection of objects, a database table, a cloud storage blob, or an XML structure. The
same query syntax works for all storage types.
The Task based asynchronous programming model enables you to write code that reads
as though it runs synchronously, even though it runs asynchronously. It utilizes the
async and await keywords to describe methods that are asynchronous, and when an
C#
C#
C#
C# provides pattern matching. Those expressions enable you to inspect data and make
decisions based on its characteristics. Pattern matching provides a great syntax for
control flow based on data. The following code shows how methods for the boolean
and, or, and xor operations could be expressed using pattern matching syntax:
C#
Pattern matching expressions can be simplified using _ as a catch all for any value. The
following example shows how you can simplify the and method:
C#
Finally, as part of the .NET ecosystem, you can use Visual Studio , or Visual Studio
Code with the C# DevKit . These tools provide rich understanding of C#, including
the code you write. They also provide debugging capabilities.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Roadmap for Java developers learning
C#
Article • 04/09/2024
C# and Java have many similarities. As you learn C#, you can apply much of the
knowledge you already have from programming in Java:
1. Similar syntax: Both Java and C# are in the C family of languages. That similarity
means you can already read and understand C#. There are some differences, but
most of the syntax is the same as Java, and C. The curly braces and semicolons are
familiar. The control statements like if , else , switch are the same. The looping
statements of for , while , and do ... while are same. The same keywords for class
and interface are in both languages. The access modifiers from public to
private are the same. Even many of the builtin types use the same keywords: int ,
There are other features in C# that aren't in Java. You'll see features like async and await,
and using statements to automatically free nonmemory resources.
There are also some similar features between C# and Java that have subtle but
important differences:
1. Properties and Indexers: Both properties and indexers (treating a class like an array
or dictionary) have language support. In Java, they're naming conventions for
methods starting with get and set .
2. Records: In C#, records can be either class (reference) or struct (value) types. C#
records can be immutable, but aren't required to be immutable.
3. Tuples have different syntax in C# and Java.
4. Attributes are similar to Java annotations.
Finally, there are Java language features that aren't available in C#:
1. Checked exceptions: In C#, any method could theoretically throw any exception.
2. Checked array covariance: In C#, arrays aren't safely covariant. You should use the
generic collection classes and interfaces if you need covariant structures.
Overall, learning C# for a developer experienced in Java should be smooth. You'll find
enough familiar idioms to quickly be productive, and you'll learn the new idioms quickly.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Roadmap for JavaScript and TypeScript
developers learning C#
Article • 04/09/2024
C#, TypeScript and JavaScript are all members of the C family of languages. The
similarities between the languages help you quickly become productive in C#.
1. Similar syntax: JavaScript, TypeScript, and C# are in the C family of languages. That
similarity means you can already read and understand C#. There are some
differences, but most of the syntax is the same as JavaScript, and C. The curly
braces and semicolons are familiar. The control statements like if , else , switch
are the same. The looping statements of for , while , and do ... while are same. The
same keywords for class and interface are in both C# and TypeScript. The access
modifiers in TypeScript and C#, from public to private , are the same.
2. The => token: All languages support lightweight function definitions. In C#, they're
referred to as lambda expressions, in JavaScript, they're typically called arrow
functions.
3. Function hierarchies: All three languages support local functions, which are
functions defined in other functions.
4. Async / Await: All three languages share the same async and await keywords for
asynchronous programming.
5. Garbage collection: All three languages rely on a garbage collector for automatic
memory management.
6. Event model: C#'s event syntax is similar to JavaScript's model for document object
model (DOM) events.
7. Package manager: NuGet is the most common package manager for C# and
.NET, similar to npm for JavaScript applications. C# libraries are delivered in
assemblies.
As you continue learning C#, you'll learn concepts that aren't part of JavaScript. Some of
these concepts might be familiar to you if you use TypeScript:
1. C# Type System: C# is a strongly typed language. Every variable has a type, and
that type can't change. You define class or struct types. You can define interface
definitions that define behavior implemented by other types. TypeScript includes
many of these concepts, but because TypeScript is built on JavaScript, the type
system isn't as strict.
2. Pattern matching: Pattern matching enables concise conditional statements and
expressions based on the shape of complex data structures. The is expression
checks if a variable "is" some pattern. The pattern-based switch expression
provides a rich syntax to inspect a variable and make decisions based on its
characteristics.
3. String interpolation and raw string literals: String interpolation enables you to
insert evaluated expressions in a string, rather than using positional identifiers. Raw
string literals provide a way to minimize escape sequences in text.
4. Nullable and non-nullable types: C# supports nullable value types, and nullable
reference types by appending the ? suffix to a type. For nullable types, the
compiler warns you if you don't check for null before dereferencing the
expression. For non-nullable types, the compiler warns you if you might be
assigning a null value to that variable. These features can minimize your
application throwing a System.NullReferenceException. The syntax might be
familiar from TypeScript's use of ? for optional properties.
5. LINQ: Language integrated query (LINQ) provides a common syntax to query and
transform data, regardless of its storage.
As you learn more other differences become apparent, but many of those differences
are smaller in scope.
Some familiar features and idioms from JavaScript and TypeScript aren't available in C#:
1. dynamic types: C# uses static typing. A variable declaration includes the type, and
that type can't change. There is a dynamic type in C# that provides runtime
binding.
2. Prototypal inheritance: C# inheritance is part of the type declaration. A C# class
declaration states any base class. In JavaScript, you can set the __proto__ property
to set the base type on any instance.
3. Interpreted language: C# code must be compiled before you run it. JavaScript
code can be run directly in the browser.
1. Union types: C# doesn't support union types. However, design proposals are in
progress.
2. Decorators: C# doesn't have decorators. Some common decorators, such as
@sealed are reserved keywords in C#. Other common decorators might have
corresponding Attributes. For other decorators, you can create your own attributes.
3. More forgiving syntax: The C# compiler parses code more strictly than JavaScript
requires.
If you're building a web application, you should consider using Blazor to build your
application. Blazor is a full-stack web framework built for .NET and C#. Blazor
components can run on the server, as .NET assemblies, or on the client using
WebAssembly. Blazor supports interop with your favorite JavaScript or TypeScript
libraries.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Roadmap for Python developers
learning C#
Article • 04/09/2024
C# and Python share similar concepts. These familiar constructs help you learn C# when
you already know Python.
1. Object oriented: Both Python and C# are object-oriented languages. All the
concepts around classes in Python apply in C#, even if the syntax is different.
2. Cross-platform: Both Python and C# are cross-platform languages. Apps written in
either language can run on many platforms.
3. Garbage collection: Both languages employ automatic memory management
through garbage collection. The runtime reclaims the memory from objects that
aren't referenced.
4. Strongly typed: Both Python and C# are strongly typed languages. Type coercion
doesn't occur implicitly. There are differences described later, as C# is statically
typed whereas Python is dynamically typed.
5. Async / Await: Python's async and await feature was directly inspired by C#'s
async and await support.
As you start learning C#, you'll learn these important concepts where C# is different
than Python:
1. Indentation vs. tokens: In Python, newlines and indentation are first-class syntactic
elements. In C#, whitespace isn't significant. Tokens, like ; separate statements,
and other tokens { and } control block scope for if and other block statements.
However, for readability, most coding styles (including the style used in these docs)
use indentation to reinforce the block scopes declared by { and } .
2. Static typing: In C#, a variable declaration includes its type. Reassigning a variable
to an object of a different type generates a compiler error. In Python, the type can
change when reassigned.
3. Nullable types: C# variables can be nullable or non-nullable. A non-nullable type is
one that can't be null (or nothing). It always refers to a valid object. By contrast, a
nullable type might either refer to a valid object, or null.
4. LINQ: The query expression keywords that make up Language Integrated Query
(LINQ) aren't keywords in Python. However, Python libraries like itertools , more-
itertools , and py-linq provide similar functionality.
5. Generics: C# generics use C# static typing to make assertions about the arguments
supplied for type parameters. A generic algorithm might need to specify
constraints that an argument type must satisfy.
Finally, there are some features of Python that aren't available in C#:
1. Structural (duck) typing: In C#, types have names and declarations. Except for
tuples, types with the same structure aren't interchangeable.
2. REPL: C# doesn't have a Read-Eval-Print Loop (REPL) to quickly prototype
solutions.
3. Significant whitespace: You need to correctly use braces { and } to note block
scope.
Learning C# if you know Python is a smooth journey. The languages have similar
concepts and similar idioms to use.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Annotated C# strategy
Article • 02/21/2023
We will keep evolving C# to meet the changing needs of developers and remain a state-
of-the-art programming language. We will innovate eagerly and broadly in collaboration
with the teams responsible for .NET libraries, developer tools, and workload support,
while being careful to stay within the spirit of the language. Recognizing the diversity of
domains where C# is being used, we will prefer language and performance
improvements that benefit all or most developers and maintain a high commitment to
backwards compatibility. We will continue to empower the broader .NET ecosystem and
grow its role in C#’s future, while maintaining stewardship of design decisions.
The C# community continues to grow, and the C# language continues to evolve to meet
the community's needs and expectations. We draw inspiration from a variety of sources
to select features that benefit a large segment of C# developers, and that provide
consistent improvements in productivity, readability, and performance.
We evaluate new ideas in the spirit and history of the C# language. We prioritize
innovations that make sense to the majority of existing C# developers.
Developers use C# in all .NET workloads, such as web front and back ends, cloud native
development, desktop development and building cross platform applications. We focus
on new features that have the most impact either directly, or by empowering
improvements to common libraries. Language feature development includes integration
into our developer tools and learning resources.
"maintaining stewardship"
C# language design takes place in the open with community participation. Anyone
can propose new C# features in our GitHub repos . The Language Design Team
makes the final decisions after weighing community input.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Introduction to C#
Article • 03/15/2024
Welcome to the introduction to C# tutorials. These lessons start with interactive code
that you can run in your browser. You can learn the basics of C# from the C# for
Beginners video series before starting these interactive lessons.
https://www.youtube-nocookie.com/embed/9THmGiSPjBQ?si=3kUKFtOMLpEzeq7J
The first lessons explain C# concepts using small snippets of code. You'll learn the basics
of C# syntax and how to work with data types like strings, numbers, and booleans. It's all
interactive, and you'll be writing and running code within minutes. These first lessons
assume no prior knowledge of programming or the C# language.
You can try these tutorials in different environments. The concepts you'll learn are the
same. The difference is which experience you prefer:
In your browser, on the docs platform: This experience embeds a runnable C# code
window in docs pages. You write and execute C# code in the browser.
In the Microsoft Learn training experience. This learning path contains several
modules that teach the basics of C#.
In Jupyter on Binder . You can experiment with C# code in a Jupyter notebook on
binder.
On your local machine. After you've explored online, you can download the .NET
SDK and build programs on your machine.
All the introductory tutorials following the Hello World lesson are available using the
online browser experience or in your own local development environment. At the end of
each tutorial, you decide if you want to continue with the next lesson online or on your
own machine. There are links to help you set up your environment and continue with
the next tutorial on your machine.
Hello world
In the Hello world tutorial, you'll create the most basic C# program. You'll explore the
string type and how to work with text. You can also use the path on Microsoft Learn
Numbers in C#
In the Numbers in C# tutorial, you'll learn how computers store numbers and how to
perform calculations with different numeric types. You'll learn the basics of rounding,
and how to perform mathematical calculations using C#. This tutorial is also available to
run locally on your machine.
This tutorial assumes that you've finished the Hello world lesson.
This tutorial assumes that you've finished the Hello world and Numbers in C# lessons.
List collection
The List collection lesson gives you a tour of the List collection type that stores
sequences of data. You'll learn how to add and remove items, search for items, and sort
the lists. You'll explore different kinds of lists. This tutorial is also available to run locally
on your machine.
This tutorial assumes that you've finished the lessons listed above.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Set up your local environment
Article • 04/28/2022
We recommend Visual Studio for Windows. You can download a free version
from the Visual Studio downloads page . Visual Studio includes the .NET SDK.
You can also use the Visual Studio Code editor with the C# DevKit . You'll need
to install the latest .NET SDK separately.
If you prefer a different editor, you need to install the latest .NET SDK .
dotnet new creates an application. This command generates the files and assets
necessary for your application. The introduction to C# tutorials all use the console
application type. Once you've got the basics, you can expand to other application
types.
dotnet build builds the executable.
dotnet run runs the executable.
If you use Visual Studio 2019 for these tutorials, you'll choose a Visual Studio menu
selection when a tutorial directs you to run one of these CLI commands:
Numbers in C#
In the Numbers in C# tutorial, you'll learn how computers store numbers and how to
perform calculations with different numeric types. You'll learn the basics of rounding and
how to perform mathematical calculations using C#.
This tutorial assumes that you have finished the Hello world lesson.
This tutorial assumes that you have finished the Hello world and Numbers in C# lessons.
List collection
The List collection lesson gives you a tour of the List collection type that stores
sequences of data. You'll learn how to add and remove items, search for items, and sort
the lists. You'll explore different kinds of lists.
This tutorial assumes that you have finished the lessons listed above.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to use integer and floating point
numbers in C#
Article • 10/15/2022
This tutorial teaches you about the numeric types in C#. You'll write small amounts of
code, then you'll compile and run that code. The tutorial contains a series of lessons that
explore numbers and math operations in C#. These lessons teach you the fundamentals
of the C# language.
Tip
To paste a code snippet inside the focus mode you should use your keyboard
shortcut ( Ctrl + v , or cmd + v ).
Prerequisites
The tutorial expects that you have a machine set up for local development. See Set up
your local environment for installation instructions and an overview of application
development in .NET.
If you don't want to set up a local environment, see the interactive-in-browser version of
this tutorial.
.NET CLI
) Important
The C# templates for .NET 6 use top level statements. Your application may not
match the code in this article, if you've already upgraded to the .NET 6. For more
information see the article on New C# templates generate top level statements
The .NET 6 SDK also adds a set of implicit global using directives for projects that
use the following SDKs:
Microsoft.NET.Sdk
Microsoft.NET.Sdk.Web
Microsoft.NET.Sdk.Worker
These implicit global using directives include the most common namespaces for
the project type.
Open Program.cs in your favorite editor, and replace the contents of the file with the
following code:
C#
int a = 18;
int b = 6;
int c = a + b;
Console.WriteLine(c);
You've seen one of the fundamental math operations with integers. The int type
represents an integer, a zero, positive, or negative whole number. You use the + symbol
for addition. Other common mathematical operations for integers include:
- for subtraction
* for multiplication
/ for division
Start by exploring those different operations. Add these lines after the line that writes
the value of c :
C#
// subtraction
c = a - b;
Console.WriteLine(c);
// multiplication
c = a * b;
Console.WriteLine(c);
// division
c = a / b;
Console.WriteLine(c);
You can also experiment by writing multiple mathematics operations in the same line, if
you'd like. Try c = a + b - 12 * 17; for example. Mixing variables and constant
numbers is allowed.
Tip
As you explore C# (or any programming language), you'll make mistakes when you
write code. The compiler will find those errors and report them to you. When the
output contains error messages, look closely at the example code and the code in
your window to see what to fix. That exercise will help you learn the structure of C#
code.
You've finished the first step. Before you start the next section, let's move the current
code into a separate method. A method is a series of statements grouped together and
given a name. You call a method by writing the method's name followed by () .
Organizing your code into methods makes it easier to start working with a new example.
When you finish, your code should look like this:
C#
WorkWithIntegers();
void WorkWithIntegers()
{
int a = 18;
int b = 6;
int c = a + b;
Console.WriteLine(c);
// subtraction
c = a - b;
Console.WriteLine(c);
// multiplication
c = a * b;
Console.WriteLine(c);
// division
c = a / b;
Console.WriteLine(c);
}
The line WorkWithIntegers(); invokes the method. The following code declares the
method and defines it.
C#
//WorkWithIntegers();
The // starts a comment in C#. Comments are any text you want to keep in your source
code but not execute as code. The compiler doesn't generate any executable code from
comments. Because WorkWithIntegers() is a method, you need to only comment out
one line.
The C# language defines the precedence of different mathematics operations with rules
consistent with the rules you learned in mathematics. Multiplication and division take
precedence over addition and subtraction. Explore that by adding the following code
after the call to WorkWithIntegers() , and executing dotnet run :
C#
int a = 5;
int b = 4;
int c = 2;
int d = a + b * c;
Console.WriteLine(d);
The output demonstrates that the multiplication is performed before the addition.
You can force a different order of operation by adding parentheses around the
operation or operations you want performed first. Add the following lines and run again:
C#
d = (a + b) * c;
Console.WriteLine(d);
Explore more by combining many different operations. Add something like the following
lines. Try dotnet run again.
C#
d = (a + b) - 6 * c + (12 * 4) / 3 + 12;
Console.WriteLine(d);
You may have noticed an interesting behavior for integers. Integer division always
produces an integer result, even when you'd expect the result to include a decimal or
fractional portion.
C#
int e = 7;
int f = 4;
int g = 3;
int h = (e + f) / g;
Console.WriteLine(h);
Before moving on, let's take all the code you've written in this section and put it in a
new method. Call that new method OrderPrecedence . Your code should look something
like this:
C#
// WorkWithIntegers();
OrderPrecedence();
void WorkWithIntegers()
{
int a = 18;
int b = 6;
int c = a + b;
Console.WriteLine(c);
// subtraction
c = a - b;
Console.WriteLine(c);
// multiplication
c = a * b;
Console.WriteLine(c);
// division
c = a / b;
Console.WriteLine(c);
}
void OrderPrecedence()
{
int a = 5;
int b = 4;
int c = 2;
int d = a + b * c;
Console.WriteLine(d);
d = (a + b) * c;
Console.WriteLine(d);
d = (a + b) - 6 * c + (12 * 4) / 3 + 12;
Console.WriteLine(d);
int e = 7;
int f = 4;
int g = 3;
int h = (e + f) / g;
Console.WriteLine(h);
}
C#
int a = 7;
int b = 4;
int c = 3;
int d = (a + b) / c;
int e = (a + b) % c;
Console.WriteLine($"quotient: {d}");
Console.WriteLine($"remainder: {e}");
The C# integer type differs from mathematical integers in one other way: the int type
has minimum and maximum limits. Add this code to see those limits:
C#
int max = int.MaxValue;
int min = int.MinValue;
Console.WriteLine($"The range of integers is {min} to {max}");
If a calculation produces a value that exceeds those limits, you have an underflow or
overflow condition. The answer appears to wrap from one limit to the other. Add these
two lines to see an example:
C#
Notice that the answer is very close to the minimum (negative) integer. It's the same as
min + 2 . The addition operation overflowed the allowed values for integers. The answer
is a very large negative number because an overflow "wraps around" from the largest
possible integer value to the smallest.
There are other numeric types with different limits and precision that you would use
when the int type doesn't meet your needs. Let's explore those other types next.
Before you start the next section, move the code you wrote in this section into a
separate method. Name it TestLimits .
C#
double a = 5;
double b = 4;
double c = 2;
double d = (a + b) / c;
Console.WriteLine(d);
Notice that the answer includes the decimal portion of the quotient. Try a slightly more
complicated expression with doubles:
C#
double e = 19;
double f = 23;
double g = 8;
double h = (e + f) / g;
Console.WriteLine(h);
The range of a double value is much greater than integer values. Try the following code
below what you've written so far:
C#
These values are printed in scientific notation. The number to the left of the E is the
significand. The number to the right is the exponent, as a power of 10. Just like decimal
numbers in math, doubles in C# can have rounding errors. Try this code:
C#
You know that 0.3 repeating finite number of times isn't exactly the same as 1/3 .
Challenge
Try other calculations with large numbers, small numbers, multiplication, and division
using the double type. Try more complicated calculations. After you've spent some time
with the challenge, take the code you've written and place it in a new method. Name
that new method WorkWithDoubles .
Notice that the range is smaller than the double type. You can see the greater precision
with the decimal type by trying the following code:
C#
double a = 1.0;
double b = 3.0;
Console.WriteLine(a / b);
decimal c = 1.0M;
decimal d = 3.0M;
Console.WriteLine(c / d);
The M suffix on the numbers is how you indicate that a constant should use the decimal
type. Otherwise, the compiler assumes the double type.
7 Note
The letter M was chosen as the most visually distinct letter between the double and
decimal keywords.
Notice that the math using the decimal type has more digits to the right of the decimal
point.
Challenge
Now that you've seen the different numeric types, write code that calculates the area of
a circle whose radius is 2.50 centimeters. Remember that the area of a circle is the radius
squared multiplied by PI. One hint: .NET contains a constant for PI, Math.PI that you can
use for that value. Math.PI, like all constants declared in the System.Math namespace, is
a double value. For that reason, you should use double instead of decimal values for
this challenge.
You should get an answer between 19 and 20. You can check your answer by looking at
the finished sample code on GitHub .
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
C# if statements and loops -
conditional logic tutorial
Article • 10/15/2022
This tutorial teaches you how to write C# code that examines variables and changes the
execution path based on those variables. You write C# code and see the results of
compiling and running it. The tutorial contains a series of lessons that explore branching
and looping constructs in C#. These lessons teach you the fundamentals of the C#
language.
Tip
To paste a code snippet inside the focus mode you should use your keyboard
shortcut ( Ctrl + v , or cmd + v ).
Prerequisites
The tutorial expects that you have a machine set up for local development. See Set up
your local environment for installation instructions and an overview of application
development in .NET.
If you prefer to run the code without having to set up a local environment, see the
interactive-in-browser version of this tutorial.
.NET CLI
) Important
The C# templates for .NET 6 use top level statements. Your application may not
match the code in this article, if you've already upgraded to the .NET 6. For more
information see the article on New C# templates generate top level statements
The .NET 6 SDK also adds a set of implicit global using directives for projects that
use the following SDKs:
Microsoft.NET.Sdk
Microsoft.NET.Sdk.Web
Microsoft.NET.Sdk.Worker
These implicit global using directives include the most common namespaces for
the project type.
This command creates a new .NET console application in the current directory. Open
Program.cs in your favorite editor, and replace the contents with the following code:
C#
int a = 5;
int b = 6;
if (a + b > 10)
Console.WriteLine("The answer is greater than 10.");
Try this code by typing dotnet run in your console window. You should see the message
"The answer is greater than 10." printed to your console. Modify the declaration of b so
that the sum is less than 10:
C#
int b = 3;
Type dotnet run again. Because the answer is less than 10, nothing is printed. The
condition you're testing is false. You don't have any code to execute because you've
only written one of the possible branches for an if statement: the true branch.
Tip
As you explore C# (or any programming language), you'll make mistakes when you
write code. The compiler will find and report the errors. Look closely at the error
output and the code that generated the error. The compiler error can usually help
you find the problem.
This first sample shows the power of if and Boolean types. A Boolean is a variable that
can have one of two values: true or false . C# defines a special type, bool for Boolean
variables. The if statement checks the value of a bool . When the value is true , the
statement following the if executes. Otherwise, it's skipped. This process of checking
conditions and executing statements based on those conditions is powerful.
C#
int a = 5;
int b = 3;
if (a + b > 10)
Console.WriteLine("The answer is greater than 10");
else
Console.WriteLine("The answer is not greater than 10");
The statement following the else keyword executes only when the condition being
tested is false . Combining if and else with Boolean conditions provides all the power
you need to handle both a true and a false condition.
) Important
The indentation under the if and else statements is for human readers. The C#
language doesn't treat indentation or white space as significant. The statement
following the if or else keyword will be executed based on the condition. All the
samples in this tutorial follow a common practice to indent lines based on the
control flow of statements.
Because indentation isn't significant, you need to use { and } to indicate when you
want more than one statement to be part of the block that executes conditionally. C#
programmers typically use those braces on all if and else clauses. The following
example is the same as the one you created. Modify your code above to match the
following code:
C#
int a = 5;
int b = 3;
if (a + b > 10)
{
Console.WriteLine("The answer is greater than 10");
}
else
{
Console.WriteLine("The answer is not greater than 10");
}
Tip
Through the rest of this tutorial, the code samples all include the braces, following
accepted practices.
You can test more complicated conditions. Add the following code after the code you've
written so far:
C#
int c = 4;
if ((a + b + c > 10) && (a == b))
{
Console.WriteLine("The answer is greater than 10");
Console.WriteLine("And the first number is equal to the second");
}
else
{
Console.WriteLine("The answer is not greater than 10");
Console.WriteLine("Or the first number is not equal to the second");
}
The == symbol tests for equality. Using == distinguishes the test for equality from
assignment, which you saw in a = 5 .
The && represents "and". It means both conditions must be true to execute the
statement in the true branch. These examples also show that you can have multiple
statements in each conditional branch, provided you enclose them in { and } . You can
also use || to represent "or". Add the following code after what you've written so far:
C#
Modify the values of a , b , and c and switch between && and || to explore. You'll gain
more understanding of how the && and || operators work.
You've finished the first step. Before you start the next section, let's move the current
code into a separate method. That makes it easier to start working with a new example.
Put the existing code in a method called ExploreIf() . Call it from the top of your
program. When you finished those changes, your code should look like the following:
C#
ExploreIf();
void ExploreIf()
{
int a = 5;
int b = 3;
if (a + b > 10)
{
Console.WriteLine("The answer is greater than 10");
}
else
{
Console.WriteLine("The answer is not greater than 10");
}
int c = 4;
if ((a + b + c > 10) && (a > b))
{
Console.WriteLine("The answer is greater than 10");
Console.WriteLine("And the first number is greater than the
second");
}
else
{
Console.WriteLine("The answer is not greater than 10");
Console.WriteLine("Or the first number is not greater than the
second");
}
Comment out the call to ExploreIf() . It will make the output less cluttered as you work
in this section:
C#
//ExploreIf();
The // starts a comment in C#. Comments are any text you want to keep in your source
code but not execute as code. The compiler doesn't generate any executable code from
comments.
C#
int counter = 0;
while (counter < 10)
{
Console.WriteLine($"Hello World! The counter is {counter}");
counter++;
}
The while statement checks a condition and executes the statement or statement block
following the while . It repeatedly checks the condition, executing those statements until
the condition is false.
There's one other new operator in this example. The ++ after the counter variable is the
increment operator. It adds 1 to the value of counter and stores that value in the
counter variable.
) Important
Make sure that the while loop condition changes to false as you execute the code.
Otherwise, you create an infinite loop where your program never ends. That is not
demonstrated in this sample, because you have to force your program to quit using
CTRL-C or other means.
The while loop tests the condition before executing the code following the while . The
do ... while loop executes the code first, and then checks the condition. The do while
C#
int counter = 0;
do
{
Console.WriteLine($"Hello World! The counter is {counter}");
counter++;
} while (counter < 10);
This do loop and the earlier while loop produce the same output.
C#
The previous code does the same work as the while loop and the do loop you've
already used. The for statement has three parts that control how it works.
The first part is the for initializer: int index = 0; declares that index is the loop
variable, and sets its initial value to 0 .
The middle part is the for condition: index < 10 declares that this for loop continues
to execute as long as the value of counter is less than 10.
The final part is the for iterator: index++ specifies how to modify the loop variable after
executing the block following the for statement. Here, it specifies that index should be
incremented by 1 each time the block executes.
Experiment yourself. Try each of the following variations:
When you're done, let's move on to write some code yourself to use what you've
learned.
There's one other looping statement that isn't covered in this tutorial: the foreach
statement. The foreach statement repeats its statement for every item in a sequence of
items. It's most often used with collections, so it's covered in the next tutorial.
C#
C#
You can nest one loop inside the other to form pairs:
C#
You can see that the outer loop increments once for each full run of the inner loop.
Reverse the row and column nesting, and see the changes for yourself. When you're
done, place the code from this section in a method called ExploreLoops() .
Try it yourself. Then check how you did. You should get 63 for an answer. You can see
one possible answer by viewing the completed code on GitHub .
You can continue with the Arrays and collections tutorial in your own development
environment.
Selection statements
Iteration statements
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Learn to manage data collections using
List<T> in C#
Article • 03/07/2023
This introductory tutorial provides an introduction to the C# language and the basics of
the List<T> class.
Prerequisites
The tutorial expects that you have a machine set up for local development. See Set up
your local environment for installation instructions and an overview of application
development in .NET.
If you prefer to run the code without having to set up a local environment, see the
interactive-in-browser version of this tutorial.
) Important
The C# templates for .NET 6 use top level statements. Your application may not
match the code in this article, if you've already upgraded to the .NET 6. For more
information see the article on New C# templates generate top level statements
The .NET 6 SDK also adds a set of implicit global using directives for projects that
use the following SDKs:
Microsoft.NET.Sdk
Microsoft.NET.Sdk.Web
Microsoft.NET.Sdk.Worker
These implicit global using directives include the most common namespaces for
the project type.
C#
Replace <name> with your name. Save Program.cs. Type dotnet run in your console
window to try it.
You've created a list of strings, added three names to that list, and printed the names in
all CAPS. You're using concepts that you've learned in earlier tutorials to loop through
the list.
The code to display names makes use of the string interpolation feature. When you
precede a string with the $ character, you can embed C# code in the string
declaration. The actual string replaces that C# code with the value it generates. In this
example, it replaces the {name.ToUpper()} with each name, converted to capital letters,
because you called the ToUpper method.
One important aspect of this List<T> type is that it can grow or shrink, enabling you to
add or remove elements. Add this code at the end of your program:
C#
Console.WriteLine();
names.Add("Maria");
names.Add("Bill");
names.Remove("Ana");
foreach (var name in names)
{
Console.WriteLine($"Hello {name.ToUpper()}!");
}
You've added two more names to the end of the list. You've also removed one as well.
Save the file, and type dotnet run to try it.
The List<T> enables you to reference individual items by index as well. You place the
index between [ and ] tokens following the list name. C# uses 0 for the first index. Add
this code directly below the code you just added and try it:
C#
You can't access an index beyond the end of the list. Remember that indices start at 0,
so the largest valid index is one less than the number of items in the list. You can check
how long the list is using the Count property. Add the following code at the end of your
program:
C#
Save the file, and type dotnet run again to see the results.
C#
The items in your list can be sorted as well. The Sort method sorts all the items in the list
in their normal order (alphabetically for strings). Add this code to the bottom of your
program:
C#
names.Sort();
foreach (var name in names)
{
Console.WriteLine($"Hello {name.ToUpper()}!");
}
Save the file and type dotnet run to try this latest version.
Before you start the next section, let's move the current code into a separate method.
That makes it easier to start working with a new example. Place all the code you've
written in a new method called WorkWithStrings() . Call that method at the top of your
program. When you finish, your code should look like this:
C#
WorkWithStrings();
void WorkWithStrings()
{
List<string> names = ["<name>", "Ana", "Felipe"];
foreach (var name in names)
{
Console.WriteLine($"Hello {name.ToUpper()}!");
}
Console.WriteLine();
names.Add("Maria");
names.Add("Bill");
names.Remove("Ana");
foreach (var name in names)
{
Console.WriteLine($"Hello {name.ToUpper()}!");
}
Console.WriteLine($"My name is {names[0]}");
Console.WriteLine($"I've added {names[2]} and {names[3]} to the list");
names.Sort();
foreach (var name in names)
{
Console.WriteLine($"Hello {name.ToUpper()}!");
}
}
C#
That creates a list of integers, and sets the first two integers to the value 1. These are the
first two values of a Fibonacci Sequence, a sequence of numbers. Each next Fibonacci
number is found by taking the sum of the previous two numbers. Add this code:
C#
fibonacciNumbers.Add(previous + previous2);
Save the file and type dotnet run to see the results.
Tip
To concentrate on just this section, you can comment out the code that calls
WorkWithStrings(); . Just put two / characters in front of the call like this: //
WorkWithStrings(); .
Challenge
See if you can put together some of the concepts from this and earlier lessons. Expand
on what you've built so far with Fibonacci Numbers. Try to write the code to generate
the first 20 numbers in the sequence. (As a hint, the 20th Fibonacci number is 6765.)
Complete challenge
You can see an example solution by looking at the finished sample code on GitHub .
With each iteration of the loop, you're taking the last two integers in the list, summing
them, and adding that value to the list. The loop repeats until you've added 20 items to
the list.
Congratulations, you've completed the list tutorial. You can continue with additional
tutorials in your own development environment.
You can learn more about working with the List type in the .NET fundamentals article
on collections. You'll also learn about many other collection types.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
General Structure of a C# Program
Article • 08/01/2024
C# programs consist of one or more files. Each file contains zero or more namespaces. A
namespace contains types such as classes, structs, interfaces, enumerations, and
delegates, or other namespaces. The following example is the skeleton of a C# program
that contains all of these elements.
C#
// A skeleton of a C# program
using System;
namespace YourNamespace
{
class YourClass
{
}
struct YourStruct
{
}
interface IYourInterface
{
}
enum YourEnum
{
}
namespace YourNestedNamespace
{
struct YourStruct
{
}
}
}
The preceding example uses top-level statements for the program's entry point. Only
one file can have top-level statements. The program's entry point is the first line of
program text in that file. You can also create a static method named Main as the
program's entry point, as shown in the following example:
C#
// A skeleton of a C# program
using System;
namespace YourNamespace
{
class YourClass
{
}
struct YourStruct
{
}
interface IYourInterface
{
}
enum YourEnum
{
}
namespace YourNestedNamespace
{
struct YourStruct
{
}
}
class Program
{
static void Main(string[] args)
{
//Your program starts here...
Console.WriteLine("Hello world!");
}
}
}
Related Sections
You learn about these program elements in the types section of the fundamentals guide:
Classes
Structs
Namespaces
Interfaces
Enums
Delegates
C# Language Specification
For more information, see Basic concepts in the C# Language Specification. The
language specification is the definitive source for C# syntax and usage.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Main() and command-line arguments
Article • 06/22/2024
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.
There can only be one entry point in a C# program. If you have more than one class that
has a Main method, you must compile your program with the StartupObject compiler
option to specify which Main method to use as the entry point. For more information,
see StartupObject (C# Compiler Options).
C#
class TestClass
{
static void Main(string[] args)
{
// Display the number of command line arguments.
Console.WriteLine(args.Length);
}
}
You can also use Top-level statements in one file as the entry point for your application.
Just as the Main method, top-level statements can also return values and access
command-line arguments. For more information, see Top-level statements.
C#
using System.Text;
Console.WriteLine(builder.ToString());
Overview
The Main method is the entry point of an executable program; it is where the
program control starts and ends.
Main must be declared inside a class or struct. The enclosing class can be static .
Main must be static.
Main can either have a void , int , Task , or Task<int> return type.
If and only if Main returns a Task or Task<int> , the declaration of Main may
include the async modifier. This specifically excludes an async void Main method.
The Main method can be declared with or without a string[] parameter that
contains command-line arguments. When using Visual Studio to create Windows
applications, you can add the parameter manually or else use the
GetCommandLineArgs() method to obtain the command-line arguments.
Parameters are read as zero-indexed command-line arguments. Unlike C and C++,
the name of the program is not treated as the first command-line argument in the
args array, but it is the first element of the GetCommandLineArgs() method.
C#
The preceding examples don't specify an access modifier, so they're implicitly private
by default. That's typical, but it's possible to specify any explicit access modifier.
Tip
The addition of async and Task , Task<int> return types simplifies program code
when console applications need to start and await asynchronous operations in
Main .
ノ Expand table
If the return value from Main is not used, returning void or Task allows for slightly
simpler code.
ノ Expand table
The following example shows how the exit code for the process can be accessed.
This example uses .NET Core command-line tools. If you are unfamiliar with .NET Core
command-line tools, you can learn about them in this get-started article.
Create a new application by running dotnet new console . Modify the Main method in
Program.cs as follows:
C#
When a program is executed in Windows, any value returned from the Main function is
stored in an environment variable. This environment variable can be retrieved using
ERRORLEVEL from a batch file, or $LastExitCode from PowerShell.
You can build the application using the dotnet CLI dotnet build command.
Next, create a PowerShell script to run the application and display the result. Paste the
following code into a text file and save it as test.ps1 in the folder that contains the
project. Run the PowerShell script by typing test.ps1 at the PowerShell prompt.
Because the code returns zero, the batch file will report success. However, if you change
MainReturnValTest.cs to return a non-zero value and then recompile the program,
subsequent execution of the PowerShell script will report failure.
PowerShell
dotnet run
if ($LastExitCode -eq 0) {
Write-Host "Execution succeeded"
} else
{
Write-Host "Execution Failed"
}
Write-Host "Return value = " $LastExitCode
Output
Execution succeeded
Return value = 0
example. The code in the example ensures that your program runs until the
asynchronous operation is completed:
C#
class AsyncMainReturnValTest
{
public static int Main()
{
return AsyncConsoleWork().GetAwaiter().GetResult();
}
C#
class Program
{
static async Task<int> Main(string[] args)
{
return await AsyncConsoleWork();
}
An advantage of declaring Main as async is that the compiler always generates the
correct code.
When the application entry point returns a Task or Task<int> , the compiler generates a
new entry point that calls the entry point method declared in the application code.
Assuming that this entry point is called $GeneratedMain , the compiler generates the
following code for these entry points:
static Task Main() results in the compiler emitting the equivalent of private
Main(args).GetAwaiter().GetResult();
static Task<int> Main() results in the compiler emitting the equivalent of private
static int $GeneratedMain() => Main().GetAwaiter().GetResult();
Main(args).GetAwaiter().GetResult();
7 Note
If the examples used async modifier on the Main method, the compiler would
generate the same code.
Command-Line Arguments
You can send arguments to the Main method by defining the method in one of the
following ways:
ノ Expand table
If the arguments are not used, you can omit args from the method declaration for
slightly simpler code:
ノ Expand table
The parameter of the Main method is a String array that represents the command-line
arguments. Usually you determine whether arguments exist by testing the Length
property, for example:
C#
if (args.Length == 0)
{
System.Console.WriteLine("Please enter a numeric argument.");
return 1;
}
Tip
The args array can't be null. So, it's safe to access the Length property without null
checking.
You can also convert the string arguments to numeric types by using the Convert class
or the Parse method. For example, the following statement converts the string to a
long number by using the Parse method:
C#
C#
You can also use the Convert class method ToInt64 to do the same thing:
C#
Tip
To compile and run the application from a command prompt, follow these steps:
1. Paste the following code into any text editor, and then save the file as a text file
with the name Factorial.cs.
C#
class MainClass
{
static int Main(string[] args)
{
// Test if input arguments were supplied.
if (args.Length == 0)
{
Console.WriteLine("Please enter a numeric argument.");
Console.WriteLine("Usage: Factorial <num>");
return 1;
}
// Calculate factorial.
long result = Functions.Factorial(num);
// Print result.
if (result == -1)
Console.WriteLine("Input must be >= 0 and <= 20.");
else
Console.WriteLine($"The Factorial of {num} is {result}.");
return 0;
}
}
// If 3 is entered on command line, the
// output reads: The factorial of 3 is 6.
2. From the Start screen or Start menu, open a Visual Studio Developer Command
Prompt window, and then navigate to the folder that contains the file that you
created.
dotnet build
dotnet run -- 3
C# language specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
See also
System.Environment
How to display command line arguments
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Top-level statements - programs
without Main methods
Article • 02/29/2024
You don't have to explicitly include a Main method in a console application project.
Instead, you can use the top-level statements feature to minimize the code you have to
write.
Top-level statements allows you to write executable code directly at the root of a file,
eliminating the need for wrapping your code in a class or method. This means you can
create programs without the ceremony of a Program class and a Main method. In this
case, the compiler generates a Program class with an entry point method for the
application. The name of the generated method isn't Main , it's an implementation detail
that your code can't reference directly.
C#
Console.WriteLine("Hello World!");
Top-level statements let you write simple programs for small utilities such as Azure
Functions and GitHub Actions. They also make it simpler for new C# programmers to
get started learning and writing code.
The following sections explain the rules on what you can and can't do with top-level
statements.
A project can have any number of additional source code files that don't have top-level
statements.
No other entry points
You can write a Main method explicitly, but it can't function as an entry point. The
compiler issues the following warning:
CS7022 The entry point of the program is global code; ignoring 'Main()' entry point.
In a project with top-level statements, you can't use the -main compiler option to select
the entry point, even if the project has one or more Main methods.
using directives
If you include using directives, they must come first in the file, as in this example:
C#
using System.Text;
Console.WriteLine(builder.ToString());
Global namespace
Top-level statements are implicitly in the global namespace.
C#
MyClass.TestMethod();
MyNamespace.MyClass.MyMethod();
namespace MyNamespace
{
class MyClass
{
public static void MyMethod()
{
Console.WriteLine("Hello World from
MyNamespace.MyClass.MyMethod!");
}
}
}
args
Top-level statements can reference the args variable to access any command-line
arguments that were entered. The args variable is never null but its Length is zero if no
command-line arguments were provided. For example:
C#
if (args.Length > 0)
{
foreach (var arg in args)
{
Console.WriteLine($"Argument={arg}");
}
}
else
{
Console.WriteLine("No arguments");
}
await
You can call an async method by using await . For example:
C#
Console.Write("Hello ");
await Task.Delay(5000);
Console.WriteLine("World!");
C#
string? s = Console.ReadLine();
ノ Expand table
C# language specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
Feature specification - Top-level statements
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
The C# type system
Article • 11/14/2023
C# is a strongly typed language. Every variable and constant has a type, as does every
expression that evaluates to a value. Every method declaration specifies a name, the
type and kind (value, reference, or output) for each input parameter and for the return
value. The .NET class library defines built-in numeric types and complex types that
represent a wide variety of constructs. These include the file system, network
connections, collections and arrays of objects, and dates. A typical C# program uses
types from the class library and user-defined types that model the concepts that are
specific to the program's problem domain.
The compiler uses type information to make sure all operations that are performed in
your code are type safe. For example, if you declare a variable of type int, the compiler
allows you to use the variable in addition and subtraction operations. If you try to
perform those same operations on a variable of type bool, the compiler generates an
error, as shown in the following example:
C#
int a = 5;
int b = a + 2; //OK
7 Note
C and C++ developers, notice that in C#, bool is not convertible to int .
The compiler embeds the type information into the executable file as metadata. The
common language runtime (CLR) uses that metadata at run time to further guarantee
type safety when it allocates and reclaims memory.
C#
// Declaration only:
float temperature;
string name;
MyClass myClass;
The types of method parameters and return values are specified in the method
declaration. The following signature shows a method that requires an int as an input
argument and returns a string:
C#
After you declare a variable, you can't redeclare it with a new type, and you can't assign
a value not compatible with its declared type. For example, you can't declare an int and
then assign it a Boolean value of true . However, values can be converted to other types,
for example when they're assigned to new variables or passed as method arguments. A
type conversion that doesn't cause data loss is performed automatically by the compiler.
A conversion that might cause data loss requires a cast in the source code.
Built-in types
C# provides a standard set of built-in types. These represent integers, floating point
values, Boolean expressions, text characters, decimal values, and other types of data.
There are also built-in string and object types. These types are available for you to use
in any C# program. For the complete list of the built-in types, see Built-in types.
Custom types
You use the struct, class, interface, enum, and record constructs to create your own
custom types. The .NET class library itself is a collection of custom types that you can
use in your own applications. By default, the most frequently used types in the class
library are available in any C# program. Others become available only when you
explicitly add a project reference to the assembly that defines them. After the compiler
has a reference to the assembly, you can declare variables (and constants) of the types
declared in that assembly in source code. For more information, see .NET Class Library.
It supports the principle of inheritance. Types can derive from other types, called
base types. The derived type inherits (with some restrictions) the methods,
properties, and other members of the base type. The base type can in turn derive
from some other type, in which case the derived type inherits the members of both
base types in its inheritance hierarchy. All types, including built-in numeric types
such as System.Int32 (C# keyword: int ), derive ultimately from a single base type,
which is System.Object (C# keyword: object). This unified type hierarchy is called
the Common Type System (CTS). For more information about inheritance in C#, see
Inheritance.
Each type in the CTS is defined as either a value type or a reference type. These
types include all custom types in the .NET class library and also your own user-
defined types. Types that you define by using the struct keyword are value types;
all the built-in numeric types are structs . Types that you define by using the
class or record keyword are reference types. Reference types and value types
The following illustration shows the relationship between value types and reference
types in the CTS.
7 Note
You can see that the most commonly used types are all organized in the System
namespace. However, the namespace in which a type is contained has no relation
to whether it is a value type or reference type.
Classes and structs are two of the basic constructs of the common type system in .NET.
Each is essentially a data structure that encapsulates a set of data and behaviors that
belong together as a logical unit. The data and behaviors are the members of the class,
struct, or record. The members include its methods, properties, events, and so on, as
listed later in this article.
A class, struct, or record declaration is like a blueprint that is used to create instances or
objects at run time. If you define a class, struct, or record named Person , Person is the
name of the type. If you declare and initialize a variable p of type Person , p is said to
be an object or instance of Person . Multiple instances of the same Person type can be
created, and each instance can have different values in its properties and fields.
A class is a reference type. When an object of the type is created, the variable to which
the object is assigned holds only a reference to that memory. When the object reference
is assigned to a new variable, the new variable refers to the original object. Changes
made through one variable are reflected in the other variable because they both refer to
the same data.
A struct is a value type. When a struct is created, the variable to which the struct is
assigned holds the struct's actual data. When the struct is assigned to a new variable, it's
copied. The new variable and the original variable therefore contain two separate copies
of the same data. Changes made to one copy don't affect the other copy.
Record types may be either reference types ( record class ) or value types ( record
struct ). Record types contain methods that support value-equality.
In general, classes are used to model more complex behavior. Classes typically store
data that is intended to be modified after a class object is created. Structs are best
suited for small data structures. Structs typically store data that isn't intended to be
modified after the struct is created. Record types are data structures with additional
compiler synthesized members. Records typically store data that isn't intended to be
modified after the object is created.
Value types
Value types derive from System.ValueType, which derives from System.Object. Types that
derive from System.ValueType have special behavior in the CLR. Value type variables
directly contain their values. The memory for a struct is allocated inline in whatever
context the variable is declared. There's no separate heap allocation or garbage
collection overhead for value-type variables. You can declare record struct types that
are value types and include the synthesized members for records.
The built-in numeric types are structs, and they have fields and methods that you can
access:
C#
But you declare and assign values to them as if they're simple non-aggregate types:
C#
byte num = 0xA;
int i = 5;
char c = 'Z';
Value types are sealed. You can't derive a type from any value type, for example
System.Int32. You can't define a struct to inherit from any user-defined class or struct
because a struct can only inherit from System.ValueType. However, a struct can
implement one or more interfaces. You can cast a struct type to any interface type that it
implements. This cast causes a boxing operation to wrap the struct inside a reference
type object on the managed heap. Boxing operations occur when you pass a value type
to a method that takes a System.Object or any interface type as an input parameter. For
more information, see Boxing and Unboxing.
You use the struct keyword to create your own custom value types. Typically, a struct is
used as a container for a small set of related variables, as shown in the following
example:
C#
For more information about structs, see Structure types. For more information about
value types, see Value types.
The other category of value types is enum . An enum defines a set of named integral
constants. For example, the System.IO.FileMode enumeration in the .NET class library
contains a set of named constant integers that specify how a file should be opened. It's
defined as shown in the following example:
C#
All enums inherit from System.Enum, which inherits from System.ValueType. All the rules
that apply to structs also apply to enums. For more information about enums, see
Enumeration types.
Reference types
A type that is defined as a class , record , delegate, array, or interface is a reference
type.
When declaring a variable of a reference type, it contains the value null until you assign
it with an instance of that type or create one using the new operator. Creation and
assignment of a class are demonstrated in the following example:
C#
An interface cannot be directly instantiated using the new operator. Instead, create and
assign an instance of a class that implements the interface. Consider the following
example:
C#
When the object is created, the memory is allocated on the managed heap. The variable
holds only a reference to the location of the object. Types on the managed heap require
overhead both when they're allocated and when they're reclaimed. Garbage collection is
the automatic memory management functionality of the CLR, which performs the
reclamation. However, garbage collection is also highly optimized, and in most scenarios
it doesn't create a performance issue. For more information about garbage collection,
see Automatic Memory Management.
All arrays are reference types, even if their elements are value types. Arrays implicitly
derive from the System.Array class. You declare and use them with the simplified syntax
that is provided by C#, as shown in the following example:
C#
Reference types fully support inheritance. When you create a class, you can inherit from
any other interface or class that isn't defined as sealed. Other classes can inherit from
your class and override your virtual methods. For more information about how to create
your own classes, see Classes, structs, and records. For more information about
inheritance and virtual methods, see Inheritance.
Because literals are typed, and all types derive ultimately from System.Object, you can
write and compile code such as the following code:
C#
C#
The use of the type parameter makes it possible to reuse the same class to hold any
type of element, without having to convert each element to object. Generic collection
classes are called strongly typed collections because the compiler knows the specific type
of the collection's elements and can raise an error at compile time if, for example, you
try to add an integer to the stringList object in the previous example. For more
information, see Generics.
It can be inconvenient to create a named type for simple sets of related values that you
don't intend to store or pass outside method boundaries. You can create anonymous
types for this purpose. For more information, see Anonymous Types.
Ordinary value types can't have a value of null. However, you can create nullable value
types by appending a ? after the type. For example, int? is an int type that can also
have the value null. Nullable value types are instances of the generic struct type
System.Nullable<T>. Nullable value types are especially useful when you're passing data
to and from databases in which numeric values might be null . For more information,
see Nullable value types.
Compile-time type and run-time type
A variable can have different compile-time and run-time types. The compile-time type is
the declared or inferred type of the variable in the source code. The run-time type is the
type of the instance referred to by that variable. Often those two types are the same, as
in the following example:
C#
In other cases, the compile-time type is different, as shown in the following two
examples:
C#
In both of the preceding examples, the run-time type is a string . The compile-time
type is object in the first line, and IEnumerable<char> in the second.
If the two types are different for a variable, it's important to understand when the
compile-time type and the run-time type apply. The compile-time type determines all
the actions taken by the compiler. These compiler actions include method call
resolution, overload resolution, and available implicit and explicit casts. The run-time
type determines all actions that are resolved at run time. These run-time actions include
dispatching virtual method calls, evaluating is and switch expressions, and other type
testing APIs. To better understand how your code interacts with types, recognize which
action applies to which type.
Related sections
For more information, see the following articles:
Builtin types
Value Types
Reference Types
C# language specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Declare namespaces to organize types
Article • 01/12/2022
Namespaces are heavily used in C# programming in two ways. First, .NET uses
namespaces to organize its many classes, as follows:
C#
System.Console.WriteLine("Hello World!");
System is a namespace and Console is a class in that namespace. The using keyword
can be used so that the complete name isn't required, as in the following example:
C#
using System;
C#
Console.WriteLine("Hello World!");
) Important
The C# templates for .NET 6 use top level statements. Your application may not
match the code in this article, if you've already upgraded to the .NET 6. For more
information see the article on New C# templates generate top level statements
The .NET 6 SDK also adds a set of implicit global using directives for projects that
use the following SDKs:
Microsoft.NET.Sdk
Microsoft.NET.Sdk.Web
Microsoft.NET.Sdk.Worker
These implicit global using directives include the most common namespaces for
the project type.
Second, declaring your own namespaces can help you control the scope of class and
method names in larger programming projects. Use the namespace keyword to declare
a namespace, as in the following example:
C#
namespace SampleNamespace
{
class SampleClass
{
public void SampleMethod()
{
System.Console.WriteLine(
"SampleMethod inside SampleNamespace");
}
}
}
Beginning with C# 10, you can declare a namespace for all types defined in that file, as
shown in the following example:
C#
namespace SampleNamespace;
class AnotherSampleClass
{
public void AnotherSampleMethod()
{
System.Console.WriteLine(
"SampleMethod inside SampleNamespace");
}
}
The advantage of this new syntax is that it's simpler, saving horizontal space and braces.
That makes your code easier to read.
Namespaces overview
Namespaces have the following properties:
C# language specification
For more information, see the Namespaces section of the C# language specification.
Introduction to classes
Article • 05/26/2023
Reference types
A type that is defined as a class is a reference type. At run time, when you declare a
variable of a reference type, the variable contains the value null until you explicitly
create an instance of the class by using the new operator, or assign it an object of a
compatible type that may have been created elsewhere, as shown in the following
example:
C#
//Declaring another object of the same type, assigning it the value of the
first object.
MyClass mc2 = mc;
When the object is created, enough memory is allocated on the managed heap for that
specific object, and the variable holds only a reference to the location of said object. The
memory used by an object is reclaimed by the automatic memory management
functionality of the CLR, which is known as garbage collection. For more information
about garbage collection, see Automatic memory management and garbage collection.
Declaring classes
Classes are declared by using the class keyword followed by a unique identifier, as
shown in the following example:
C#
An optional access modifier precedes the class keyword. Because public is used in this
case, anyone can create instances of this class. The name of the class follows the class
keyword. The name of the class must be a valid C# identifier name. The remainder of the
definition is the class body, where the behavior and data are defined. Fields, properties,
methods, and events on a class are collectively referred to as class members.
Creating objects
Although they're sometimes used interchangeably, a class and an object are different
things. A class defines a type of object, but it isn't an object itself. An object is a concrete
entity based on a class, and is sometimes referred to as an instance of a class.
Objects can be created by using the new keyword followed by the name of the class, like
this:
C#
When an instance of a class is created, a reference to the object is passed back to the
programmer. In the previous example, object1 is a reference to an object that is based
on Customer . This reference refers to the new object but doesn't contain the object data
itself. In fact, you can create an object reference without creating an object at all:
C#
Customer object2;
We don't recommend creating object references that don't refer to an object because
trying to access an object through such a reference fails at run time. A reference can be
made to refer to an object, either by creating a new object, or by assigning it an existing
object, such as this:
C#
This code creates two object references that both refer to the same object. Therefore,
any changes to the object made through object3 are reflected in subsequent uses of
object4 . Because objects that are based on classes are referred to by reference, classes
Every .NET type has a default value. Typically, that value is 0 for number types, and null
for all reference types. You can rely on that default value when it's reasonable in your
app.
When the .NET default isn't the right value, you can set an initial value using a field
initializer:
C#
You can require callers to provide an initial value by defining a constructor that's
responsible for setting that initial value:
C#
Beginning with C# 12, you can define a primary constructor as part of the class
declaration:
C#
Adding parameters to the class name defines the primary constructor. Those parameters
are available in the class body, which includes its members. You can use them to
initialize fields or anywhere else where they're needed.
You can also use the required modifier on a property and allow callers to use an object
initializer to set the initial value of the property:
C#
The addition of the required keyword mandates that callers must set those properties
as part of a new expression:
C#
Class inheritance
Classes fully support inheritance, a fundamental characteristic of object-oriented
programming. When you create a class, you can inherit from any other class that isn't
defined as sealed. Other classes can inherit from your class and override class virtual
methods. Furthermore, you can implement one or more interfaces.
C#
When a class declaration includes a base class, it inherits all the members of the base
class except the constructors. For more information, see Inheritance.
A class in C# can only directly inherit from one base class. However, because a base class
may itself inherit from another class, a class might indirectly inherit multiple base
classes. Furthermore, a class can directly implement one or more interfaces. For more
information, see Interfaces.
A class can be declared as abstract. An abstract class contains abstract methods that
have a signature definition but no implementation. Abstract classes can't be
instantiated. They can only be used through derived classes that implement the abstract
methods. By contrast, a sealed class doesn't allow other classes to derive from it. For
more information, see Abstract and Sealed Classes and Class Members.
Class definitions can be split between different source files. For more information, see
Partial Classes and Methods.
C# Language Specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
Introduction to record types in C#
Article • 05/26/2023
A record in C# is a class or struct that provides special syntax and behavior for working
with data models. The record modifier instructs the compiler to synthesize members
that are useful for types whose primary role is storing data. These members include an
overload of ToString() and members that support value equality.
Value equality
For records, value equality means that two variables of a record type are equal if the
types match and all property and field values compare equal. For other reference types
such as classes, equality means reference equality by default, unless value equality was
implemented. That is, two variables of a class type are equal if they refer to the same
object. Methods and operators that determine equality of two record instances use
value equality.
Not all data models work well with value equality. For example, Entity Framework Core
depends on reference equality to ensure that it uses only one instance of an entity type
for what is conceptually one entity. For this reason, record types aren't appropriate for
use as entity types in Entity Framework Core.
Immutability
An immutable type is one that prevents you from changing any property or field values
of an object after it's instantiated. Immutability can be useful when you need a type to
be thread-safe or you're depending on a hash code remaining the same in a hash table.
Records provide concise syntax for creating and working with immutable types.
Immutability isn't appropriate for all data scenarios. Entity Framework Core, for example,
doesn't support updating with immutable entity types.
How records differ from classes and structs
The same syntax that declares and instantiates classes or structs can be used with
records. Just substitute the class keyword with the record , or use record struct
instead of struct . Likewise, the same syntax for expressing inheritance relationships is
supported by record classes. Records differ from classes in the following ways:
Record structs differ from structs in that the compiler synthesizes the methods for
equality, and ToString . The compiler synthesizes a Deconstruct method for positional
record structs.
The compiler synthesizes a public init-only property for each primary constructor
parameter in a record class . In a record struct , the compiler synthesizes a public
read-write property. The compiler doesn't create properties for primary constructor
parameters in class and struct types that don't include record modifier.
Examples
The following example defines a public record that uses positional parameters to
declare and instantiate a record. It then prints the type name and property values:
C#
C#
person1.PhoneNumbers[0] = "555-1234";
Console.WriteLine(person1 == person2); // output: True
C#
C# Language Specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Interfaces - define behavior for multiple
types
Article • 03/18/2023
By using interfaces, you can, for example, include behavior from multiple sources in a
class. That capability is important in C# because the language doesn't support multiple
inheritance of classes. In addition, you must use an interface if you want to simulate
inheritance for structs, because they can't actually inherit from another struct or class.
You define an interface by using the interface keyword as the following example shows.
C#
interface IEquatable<T>
{
bool Equals(T obj);
}
Any class or struct that implements the IEquatable<T> interface must contain a
definition for an Equals method that matches the signature that the interface specifies.
As a result, you can count on a class that implements IEquatable<T> to contain an
Equals method with which an instance of the class can determine whether it's equal to
another instance of the same class.
For more information about abstract classes, see Abstract and Sealed Classes and Class
Members.
implementation.
7 Note
When an interface declares static members, a type implementing that interface may
also declare static members with the same signature. Those are distinct and
uniquely identified by the type declaring the member. The static member declared
in a type doesn't override the static member declared in the interface.
A class or struct that implements an interface must provide an implementation for all
declared members without a default implementation provided by the interface.
However, if a base class implements an interface, any class that's derived from the base
class inherits that implementation.
C#
Properties and indexers of a class can define extra accessors for a property or indexer
that's defined in an interface. For example, an interface might declare a property that
has a get accessor. The class that implements the interface can declare the same
property with both a get and set accessor. However, if the property or indexer uses
explicit implementation, the accessors must match. For more information about explicit
implementation, see Explicit Interface Implementation and Interface Properties.
Interfaces can inherit from one or more interfaces. The derived interface inherits the
members from its base interfaces. A class that implements a derived interface must
implement all members in the derived interface, including all members of the derived
interface's base interfaces. That class may be implicitly converted to the derived
interface or any of its base interfaces. A class might include an interface multiple times
through base classes that it inherits or through interfaces that other interfaces inherit.
However, the class can provide an implementation of an interface only one time and
only if the class declares the interface as part of the definition of the class ( class
ClassName : InterfaceName ). If the interface is inherited because you inherited a base
class that implements the interface, the base class provides the implementation of the
members of the interface. However, the derived class can reimplement any virtual
interface members instead of using the inherited implementation. When interfaces
declare a default implementation of a method, any class implementing that interface
inherits that implementation (You need to cast the class instance to the interface type to
access the default implementation on the Interface member).
A base class can also implement interface members by using virtual members. In that
case, a derived class can change the interface behavior by overriding the virtual
members. For more information about virtual members, see Polymorphism.
Interfaces summary
An interface has the following properties:
In C# versions earlier than 8.0, an interface is like an abstract base class with only
abstract members. A class or struct that implements the interface must implement
all its members.
Beginning with C# 8.0, an interface may define default implementations for some
or all of its members. A class or struct that implements the interface doesn't have
to implement members that have default implementations. For more information,
see default interface methods.
An interface can't be instantiated directly. Its members are implemented by any
class or struct that implements the interface.
A class or struct can implement multiple interfaces. A class can inherit a base class
and also implement one or more interfaces.
Generic classes and methods
Article • 03/19/2024
Generics introduces the concept of type parameters to .NET. Generics make it possible
to design classes and methods that defer the specification of one or more type
parameters until you use the class or method in your code. For example, by using a
generic type parameter T , you can write a single class that other client code can use
without incurring the cost or risk of runtime casts or boxing operations, as shown here:
C#
Generic classes and methods combine reusability, type safety, and efficiency in a way
that their nongeneric counterparts can't. Generic type parameters are replaced with the
type arguments during compilation. In the preceding example, the compiler replaces T
with int . Generics are most frequently used with collections and the methods that
operate on them. The System.Collections.Generic namespace contains several generic-
based collection classes. The nongeneric collections, such as ArrayList aren't
recommended and are maintained only for compatibility purposes. For more
information, see Generics in .NET.
You can also create custom generic types and methods to provide your own generalized
solutions and design patterns that are type-safe and efficient. The following code
example shows a simple generic linked-list class for demonstration purposes. (In most
cases, you should use the List<T> class provided by .NET instead of creating your own.)
The type parameter T is used in several locations where a concrete type would
ordinarily be used to indicate the type of the item stored in the list:
C#
// constructor
public GenericList()
{
head = null;
}
The following code example shows how client code uses the generic GenericList<T>
class to create a list of integers. If you change the type argument, the following code
creates lists of strings or any other custom type:
C#
class TestGenericList
{
static void Main()
{
// int is the type argument
GenericList<int> list = new GenericList<int>();
Generic types aren't limited to classes. The preceding examples use class types,
but you can define generic interface and struct types, including record types.
Generics overview
Use generic types to maximize code reuse, type safety, and performance.
The most common use of generics is to create collection classes.
The .NET class library contains several generic collection classes in the
System.Collections.Generic namespace. The generic collections should be used
whenever possible instead of classes such as ArrayList in the System.Collections
namespace.
You can create your own generic interfaces, classes, methods, events, and
delegates.
Generic classes can be constrained to enable access to methods on particular data
types.
You can obtain information at run time on the types that are used in a generic data
type by using reflection.
C# language specification
For more information, see the C# Language Specification.
See also
Generics in .NET
System.Collections.Generic
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Anonymous types
Article • 11/29/2023
You create anonymous types by using the new operator together with an object
initializer. For more information about object initializers, see Object and Collection
Initializers.
The following example shows an anonymous type that is initialized with two properties
named Amount and Message .
C#
// Rest the mouse pointer over v.Amount and v.Message in the following
// statement to verify that their inferred types are int and string.
Console.WriteLine(v.Amount + v.Message);
Anonymous types are typically used in the select clause of a query expression to return
a subset of the properties from each object in the source sequence. For more
information about queries, see LINQ in C#.
Anonymous types contain one or more public read-only properties. No other kinds of
class members, such as methods or events, are valid. The expression that is used to
initialize a property cannot be null , an anonymous function, or a pointer type.
The most common scenario is to initialize an anonymous type with properties from
another type. In the following example, assume that a class exists that is named
Product . Class Product includes Color and Price properties, together with other
properties that you are not interested in. Variable products is a collection of Product
objects. The anonymous type declaration starts with the new keyword. The declaration
initializes a new type that uses only two properties from Product . Using anonymous
types causes a smaller amount of data to be returned in the query.
If you don't specify member names in the anonymous type, the compiler gives the
anonymous type members the same name as the property being used to initialize them.
You provide a name for a property that's being initialized with an expression, as shown
in the previous example. In the following example, the names of the properties of the
anonymous type are Color and Price .
C#
var productQuery =
from prod in products
select new { prod.Color, prod.Price };
Tip
You can use .NET style rule IDE0037 to enforce whether inferred or explicit member
names are preferred.
It is also possible to define a field by object of another type: class, struct or even another
anonymous type. It is done by using the variable holding this object just like in the
following example, where two anonymous types are created using already instantiated
user-defined types. In both cases the product field in the anonymous type shipment
and shipmentWithBonus will be of type Product containing its default values of each
field. And the bonus field will be of anonymous type created by the compiler.
C#
Typically, when you use an anonymous type to initialize a variable, you declare the
variable as an implicitly typed local variable by using var. The type name cannot be
specified in the variable declaration because only the compiler has access to the
underlying name of the anonymous type. For more information about var , see Implicitly
Typed Local Variables.
C#
var anonArray = new[] { new { name = "apple", diam = 4 }, new { name =
"grape", diam = 1 }};
Anonymous types are class types that derive directly from object, and that cannot be
cast to any type except object. The compiler provides a name for each anonymous type,
although your application cannot access it. From the perspective of the common
language runtime, an anonymous type is no different from any other reference type.
C#
You cannot declare a field, a property, an event, or the return type of a method as
having an anonymous type. Similarly, you cannot declare a formal parameter of a
method, property, constructor, or indexer as having an anonymous type. To pass an
anonymous type, or a collection that contains anonymous types, as an argument to a
method, you can declare the parameter as type object . However, using object for
anonymous types defeats the purpose of strong typing. If you must store query results
or pass them outside the method boundary, consider using an ordinary named struct or
class instead of an anonymous type.
Because the Equals and GetHashCode methods on anonymous types are defined in
terms of the Equals and GetHashCode methods of the properties, two instances of the
same anonymous type are equal only if all their properties are equal.
7 Note
Anonymous types do override the ToString method, concatenating the name and
ToString output of every property surrounded by curly braces.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Overview of object oriented techniques
in C#
Article • 09/21/2022
In C#, the definition of a type—a class, struct, or record—is like a blueprint that specifies
what the type can do. An object is basically a block of memory that has been allocated
and configured according to the blueprint. This article provides an overview of these
blueprints and their features. The next article in this series introduces objects.
Encapsulation
Encapsulation is sometimes referred to as the first pillar or principle of object-oriented
programming. A class or struct can specify how accessible each of its members is to
code outside of the class or struct. Methods and variables that aren't intended to be
used from outside of the class or assembly can be hidden to limit the potential for
coding errors or malicious exploits. For more information, see the Object-oriented
programming tutorial.
Members
The members of a type include all methods, fields, constants, properties, and events. In
C#, there are no global variables or methods as there are in some other languages. Even
a program's entry point, the Main method, must be declared within a class or struct
(implicitly when you use top-level statements).
The following list includes all the various kinds of members that may be declared in a
class, struct, or record.
Fields
Constants
Properties
Methods
Constructors
Events
Finalizers
Indexers
Operators
Nested Types
For more information, see Members.
Accessibility
Some methods and properties are meant to be called or accessed from code outside a
class or struct, known as client code. Other methods and properties might be only for
use in the class or struct itself. It's important to limit the accessibility of your code so
that only the intended client code can reach it. You specify how accessible your types
and their members are to client code by using the following access modifiers:
public
protected
internal
protected internal
private
private protected.
Inheritance
Classes (but not structs) support the concept of inheritance. A class that derives from
another class, called the base class, automatically contains all the public, protected, and
internal members of the base class except its constructors and finalizers.
Classes may be declared as abstract, which means that one or more of their methods
have no implementation. Although abstract classes cannot be instantiated directly, they
can serve as base classes for other classes that provide the missing implementation.
Classes can also be declared as sealed to prevent other classes from inheriting from
them.
Interfaces
Classes, structs, and records can implement multiple interfaces. To implement from an
interface means that the type implements all the methods defined in the interface. For
more information, see Interfaces.
Generic Types
Classes, structs, and records can be defined with one or more type parameters. Client
code supplies the type when it creates an instance of the type. For example, the List<T>
class in the System.Collections.Generic namespace is defined with one type parameter.
Client code creates an instance of a List<string> or List<int> to specify the type that
the list will hold. For more information, see Generics.
Static Types
Classes (but not structs or records) can be declared as static . A static class can contain
only static members and can't be instantiated with the new keyword. One copy of the
class is loaded into memory when the program loads, and its members are accessed
through the class name. Classes, structs, and records can contain static members. For
more information, see Static classes and static class members.
Nested Types
A class, struct, or record can be nested within another class, struct, or record. For more
information, see Nested Types.
Partial Types
You can define part of a class, struct, or method in one code file and another part in a
separate code file. For more information, see Partial Classes and Methods.
Object Initializers
You can instantiate and initialize class or struct objects, and collections of objects, by
assigning values to its properties. For more information, see How to initialize objects by
using an object initializer.
Anonymous Types
In situations where it isn't convenient or necessary to create a named class you use
anonymous types. Anonymous types are defined by their named data members. For
more information, see Anonymous types.
Extension Methods
You can "extend" a class without creating a derived class by creating a separate type.
That type contains methods that can be called as if they belonged to the original type.
For more information, see Extension methods.
Records
You can add the record modifier to a class or a struct. Records are types with built-in
behavior for value-based equality. A record (either record class or record struct )
provides the following features:
C# Language Specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
be found on GitHub, where you Select a link to provide feedback:
can also create and review
issues and pull requests. For Open a documentation issue
more information, see our
contributor guide. Provide product feedback
Objects - create instances of types
Article • 09/17/2021
A class or struct definition is like a blueprint that specifies what the type can do. An
object is basically a block of memory that has been allocated and configured according
to the blueprint. A program may create many objects of the same class. Objects are also
called instances, and they can be stored in either a named variable or in an array or
collection. Client code is the code that uses these variables to call the methods and
access the public properties of the object. In an object-oriented language such as C#, a
typical program consists of multiple objects interacting dynamically.
7 Note
Static types behave differently than what is described here. For more information,
see Static Classes and Static Class Members.
Instances of classes are created by using the new operator. In the following example,
Person is the type and person1 and person2 are instances, or objects, of that type.
C#
class Program
{
static void Main()
{
Person person1 = new Person("Leopold", 6);
Console.WriteLine("person1 Name = {0} Age = {1}", person1.Name,
person1.Age);
Because structs are value types, a variable of a struct object holds a copy of the entire
object. Instances of structs can also be created by using the new operator, but this isn't
required, as shown in the following example:
C#
namespace Example;
The memory for both p1 and p2 is allocated on the thread stack. That memory is
reclaimed along with the type or method in which it's declared. This is one reason why
structs are copied on assignment. By contrast, the memory that is allocated for a class
instance is automatically reclaimed (garbage collected) by the common language
runtime when all references to the object have gone out of scope. It isn't possible to
deterministically destroy a class object like you can in C++. For more information about
garbage collection in .NET, see Garbage Collection.
7 Note
To determine whether the instance fields in two struct instances have the same
values, use the ValueType.Equals method. Because all structs implicitly inherit from
System.ValueType, you call the method directly on your object as shown in the
following example:
C#
if (p2.Equals(p1))
Console.WriteLine("p2 and p1 have the same values.");
To determine whether the values of the fields in two class instances are equal, you
might be able to use the Equals method or the == operator. However, only use
them if the class has overridden or overloaded them to provide a custom definition
of what "equality" means for objects of that type. The class might also implement
the IEquatable<T> interface or the IEqualityComparer<T> interface. Both
interfaces provide methods that can be used to test value equality. When
designing your own classes that override Equals , make sure to follow the
guidelines stated in How to define value equality for a type and
Object.Equals(Object).
Related Sections
For more information:
Classes
Constructors
Finalizers
Events
object
Inheritance
class
Structure types
new Operator
Common Type System
Inheritance - derive types to create
more specialized behavior
Article • 02/16/2022
Inheritance, together with encapsulation and polymorphism, is one of the three primary
characteristics of object-oriented programming. Inheritance enables you to create new
classes that reuse, extend, and modify the behavior defined in other classes. The class
whose members are inherited is called the base class, and the class that inherits those
members is called the derived class. A derived class can have only one direct base class.
However, inheritance is transitive. If ClassC is derived from ClassB , and ClassB is
derived from ClassA , ClassC inherits the members declared in ClassB and ClassA .
7 Note
Conceptually, a derived class is a specialization of the base class. For example, if you
have a base class Animal , you might have one derived class that is named Mammal and
another derived class that is named Reptile . A Mammal is an Animal , and a Reptile is an
Animal , but each derived class represents different specializations of the base class.
Interface declarations may define a default implementation for its members. These
implementations are inherited by derived interfaces, and by classes that implement
those interfaces. For more information on default interface methods, see the article on
interfaces.
When you define a class to derive from another class, the derived class implicitly gains
all the members of the base class, except for its constructors and finalizers. The derived
class reuses the code in the base class without having to reimplement it. You can add
more members in the derived class. The derived class extends the functionality of the
base class.
The following illustration shows a class WorkItem that represents an item of work in
some business process. Like all classes, it derives from System.Object and inherits all its
methods. WorkItem adds six members of its own. These members include a constructor,
because constructors aren't inherited. Class ChangeRequest inherits from WorkItem and
represents a particular kind of work item. ChangeRequest adds two more members to the
members that it inherits from WorkItem and from Object. It must add its own
constructor, and it also adds originalItemID . Property originalItemID enables the
ChangeRequest instance to be associated with the original WorkItem to which the change
request applies.
The following example shows how the class relationships demonstrated in the previous
illustration are expressed in C#. The example also shows how WorkItem overrides the
virtual method Object.ToString, and how the ChangeRequest class inherits the WorkItem
implementation of the method. The first block defines the classes:
C#
//Properties.
protected int ID { get; set; }
protected string Title { get; set; }
protected string Description { get; set; }
protected TimeSpan jobLength { get; set; }
// Method Update enables you to update the title and job length of an
// existing WorkItem object.
public void Update(string title, TimeSpan joblen)
{
this.Title = title;
this.jobLength = joblen;
}
This next block shows how to use the base and derived classes:
C#
Interfaces
An interface is a reference type that defines a set of members. All classes and structs
that implement that interface must implement that set of members. An interface may
define a default implementation for any or all of these members. A class can implement
multiple interfaces even though it can derive from only a single direct base class.
Interfaces are used to define specific capabilities for classes that don't necessarily have
an "is a" relationship. For example, the System.IEquatable<T> interface can be
implemented by any class or struct to determine whether two objects of the type are
equivalent (however the type defines equivalence). IEquatable<T> doesn't imply the
same kind of "is a" relationship that exists between a base class and a derived class (for
example, a Mammal is an Animal ). For more information, see Interfaces.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Polymorphism
Article • 01/31/2023
At run time, objects of a derived class may be treated as objects of a base class in
places such as method parameters and collections or arrays. When this
polymorphism occurs, the object's declared type is no longer identical to its run-
time type.
Base classes may define and implement virtual methods, and derived classes can
override them, which means they provide their own definition and implementation.
At run-time, when client code calls the method, the CLR looks up the run-time type
of the object, and invokes that override of the virtual method. In your source code
you can call a method on a base class, and cause a derived class's version of the
method to be executed.
Virtual methods enable you to work with groups of related objects in a uniform way. For
example, suppose you have a drawing application that enables a user to create various
kinds of shapes on a drawing surface. You don't know at compile time which specific
types of shapes the user will create. However, the application has to keep track of all the
various types of shapes that are created, and it has to update them in response to user
mouse actions. You can use polymorphism to solve this problem in two basic steps:
1. Create a class hierarchy in which each specific shape class derives from a common
base class.
2. Use a virtual method to invoke the appropriate method on any derived class
through a single call to the base class method.
First, create a base class called Shape , and derived classes such as Rectangle , Circle ,
and Triangle . Give the Shape class a virtual method called Draw , and override it in each
derived class to draw the particular shape that the class represents. Create a
List<Shape> object and add a Circle , Triangle , and Rectangle to it.
C#
// Virtual method
public virtual void Draw()
{
Console.WriteLine("Performing base class drawing tasks");
}
}
To update the drawing surface, use a foreach loop to iterate through the list and call the
Draw method on each Shape object in the list. Even though each object in the list has a
declared type of Shape , it's the run-time type (the overridden version of the method in
each derived class) that will be invoked.
C#
In C#, every type is polymorphic because all types, including user-defined types, inherit
from Object.
Polymorphism overview
Virtual members
When a derived class inherits from a base class, it includes all the members of the base
class. All the behavior declared in the base class is part of the derived class. That enables
objects of the derived class to be treated as objects of the base class. Access modifiers
( public , protected , private and so on) determine if those members are accessible from
the derived class implementation. Virtual methods gives the designer different choices
for the behavior of the derived class:
The derived class may override virtual members in the base class, defining new
behavior.
The derived class may inherit the closest base class method without overriding it,
preserving the existing behavior but enabling further derived classes to override
the method.
The derived class may define new non-virtual implementation of those members
that hide the base class implementations.
A derived class can override a base class member only if the base class member is
declared as virtual or abstract. The derived member must use the override keyword to
explicitly indicate that the method is intended to participate in virtual invocation. The
following code provides an example:
C#
Fields can't be virtual; only methods, properties, events, and indexers can be virtual.
When a derived class overrides a virtual member, that member is called even when an
instance of that class is being accessed as an instance of the base class. The following
code provides an example:
C#
BaseClass A = B;
A.DoWork(); // Also calls the new method.
Virtual methods and properties enable derived classes to extend a base class without
needing to use the base class implementation of a method. For more information, see
Versioning with the Override and New Keywords. An interface provides another way to
define a method or set of methods whose implementation is left to derived classes.
C#
public class BaseClass
{
public void DoWork() { WorkField++; }
public int WorkField;
public int WorkProperty
{
get { return 0; }
}
}
Hidden base class members may be accessed from client code by casting the instance of
the derived class to an instance of the base class. For example:
C#
BaseClass A = (BaseClass)B;
A.DoWork(); // Calls the old method.
C#
public class A
{
public virtual void DoWork() { }
}
public class B : A
{
public override void DoWork() { }
}
A derived class can stop virtual inheritance by declaring an override as sealed. Stopping
inheritance requires putting the sealed keyword before the override keyword in the
class member declaration. The following code provides an example:
C#
public class C : B
{
public sealed override void DoWork() { }
}
In the previous example, the method DoWork is no longer virtual to any class derived
from C . It's still virtual for instances of C , even if they're cast to type B or type A . Sealed
methods can be replaced by derived classes by using the new keyword, as the following
example shows:
C#
public class D : C
{
public new void DoWork() { }
}
In this case, if DoWork is called on D using a variable of type D , the new DoWork is called.
If a variable of type C , B , or A is used to access an instance of D , a call to DoWork will
follow the rules of virtual inheritance, routing those calls to the implementation of
DoWork on class C .
C#
7 Note
It is recommended that virtual members use base to call the base class
implementation of that member in their own implementation. Letting the base class
behavior occur enables the derived class to concentrate on implementing behavior
specific to the derived class. If the base class implementation is not called, it is up
to the derived class to make their behavior compatible with the behavior of the
base class.
Pattern matching overview
Article • 03/13/2024
This article provides an overview of scenarios where you can use pattern matching.
These techniques can improve the readability and correctness of your code. For a full
discussion of all the patterns you can apply, see the article on patterns in the language
reference.
Null checks
One of the most common scenarios for pattern matching is to ensure values aren't
null . You can test and convert a nullable value type to its underlying type while testing
C#
The preceding code is a declaration pattern to test the type of the variable, and assign it
to a new variable. The language rules make this technique safer than many others. The
variable number is only accessible and assigned in the true portion of the if clause. If
you try to access it elsewhere, either in the else clause, or after the if block, the
compiler issues an error. Secondly, because you're not using the == operator, this
pattern works when a type overloads the == operator. That makes it an ideal way to
check null reference values, adding the not pattern:
C#
The preceding example used a constant pattern to compare the variable to null . The
not is a logical pattern that matches when the negated pattern doesn't match.
Type tests
Another common use for pattern matching is to test a variable to see if it matches a
given type. For example, the following code tests if a variable is non-null and
implements the System.Collections.Generic.IList<T> interface. If it does, it uses the
ICollection<T>.Count property on that list to find the middle index. The declaration
pattern doesn't match a null value, regardless of the compile-time type of the variable.
The code below guards against null , in addition to guarding against a type that doesn't
implement IList .
C#
The same tests can be applied in a switch expression to test a variable against multiple
different types. You can use that information to create better algorithms based on the
specific run-time type.
Compare discrete values
You can also test a variable to find a match on specific values. The following code shows
one example where you test a value against all possible values declared in an
enumeration:
C#
C#
The preceding example shows the same algorithm, but uses string values instead of an
enum. You would use this scenario if your application responds to text commands
instead of a regular data format. Starting with C# 11, you can also use a Span<char> or a
ReadOnlySpan<char> to test for constant string values, as shown in the following sample:
C#
In all these examples, the discard pattern ensures that you handle every input. The
compiler helps you by making sure every possible input value is handled.
Relational patterns
You can use relational patterns to test how a value compares to constants. For example,
the following code returns the state of water based on the temperature in Fahrenheit:
C#
The preceding code also demonstrates the conjunctive and logical pattern to check that
both relational patterns match. You can also use a disjunctive or pattern to check that
either pattern matches. The two relational patterns are surrounded by parentheses,
which you can use around any pattern for clarity. The final two switch arms handle the
cases for the melting point and the boiling point. Without those two arms, the compiler
warns you that your logic doesn't cover every possible input.
The preceding code also demonstrates another important feature the compiler provides
for pattern matching expressions: The compiler warns you if you don't handle every
input value. The compiler also issues a warning if the pattern for a switch arm is covered
by a previous pattern. That gives you freedom to refactor and reorder switch
expressions. Another way to write the same expression could be:
C#
The key lesson in the preceding sample, and any other refactoring or reordering, is that
the compiler validates that your code handles all possible inputs.
Multiple inputs
All the patterns covered so far have been checking one input. You can write patterns
that examine multiple properties of an object. Consider the following Order record:
C#
The preceding positional record type declares two members at explicit positions.
Appearing first is the Items , then the order's Cost . For more information, see Records.
The following code examines the number of items and the value of an order to calculate
a discounted price:
C#
The first two arms examine two properties of the Order . The third examines only the
cost. The next checks against null , and the final matches any other value. If the Order
type defines a suitable Deconstruct method, you can omit the property names from the
pattern and use deconstruction to examine properties:
C#
The preceding code demonstrates the positional pattern where the properties are
deconstructed for the expression.
List patterns
You can check elements in a list or an array using a list pattern. A list pattern provides a
means to apply a pattern to any element of a sequence. In addition, you can apply the
discard pattern ( _ ) to match any element, or apply a slice pattern to match zero or more
elements.
List patterns are a valuable tool when data doesn't follow a regular structure. You can
use pattern matching to test the shape and values of the data instead of transforming it
into a set of objects.
Consider the following excerpt from a text file containing bank transactions:
Output
It's a CSV format, but some of the rows have more columns than others. Even worse for
processing, one column in the WITHDRAWAL type contains user-generated text and can
contain a comma in the text. A list pattern that includes the discard pattern, constant
pattern, and var pattern to capture the value processes data in this format:
C#
The preceding example takes a string array, where each element is one field in the row.
The switch expression keys on the second field, which determines the kind of
transaction, and the number of remaining columns. Each row ensures the data is in the
correct format. The discard pattern ( _ ) skips the first field, with the date of the
transaction. The second field matches the type of transaction. Remaining element
matches skip to the field with the amount. The final match uses the var pattern to
capture the string representation of the amount. The expression calculates the amount
to add or subtract from the balance.
List patterns enable you to match on the shape of a sequence of data elements. You use
the discard and slice patterns to match the location of elements. You use other patterns
to match characteristics about individual elements.
This article provided a tour of the kinds of code you can write with pattern matching in
C#. The following articles show more examples of using patterns in scenarios, and the
full vocabulary of patterns available to use.
See also
Use pattern matching to avoid 'is' check followed by a cast (style rules IDE0020 and
IDE0038)
Exploration: Use pattern matching to build your class behavior for better code
Tutorial: Use pattern matching to build type-driven and data-driven algorithms
Reference: Pattern matching
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Discards - C# Fundamentals
Article • 11/14/2023
Discards are placeholder variables that are intentionally unused in application code.
Discards are equivalent to unassigned variables; they don't have a value. A discard
communicates intent to the compiler and others that read your code: You intended to
ignore the result of an expression. You may want to ignore the result of an expression,
one or more members of a tuple expression, an out parameter to a method, or the
target of a pattern matching expression.
Discards make the intent of your code clear. A discard indicates that our code never
uses the variable. They enhance its readability and maintainability.
You indicate that a variable is a discard by assigning it the underscore ( _ ) as its name.
For example, the following method call returns a tuple in which the first and second
values are discards. area is a previously declared variable set to the third component
returned by GetCityInformation :
C#
You can use discards to specify unused input parameters of a lambda expression. For
more information, see the Input parameters of a lambda expression section of the
Lambda expressions article.
C#
For more information on deconstructing tuples with discards, see Deconstructing tuples
and other types.
The Deconstruct method of a class, structure, or interface also allows you to retrieve
and deconstruct a specific set of data from an object. You can use discards when you're
interested in working with only a subset of the deconstructed values. The following
example deconstructs a Person object into four strings (the first and last names, the city,
and the state), but discards the last name and the state.
C#
using System;
namespace Discards
{
public class Person
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string City { get; set; }
public string State { get; set; }
C#
C#
A standalone discard
You can use a standalone discard to indicate any variable that you choose to ignore.
One typical use is to use an assignment to ensure that an argument isn't null. The
following code uses a discard to force an assignment. The right side of the assignment
uses the null coalescing operator to throw an System.ArgumentNullException when the
argument is null . The code doesn't need the result of the assignment, so it's discarded.
The expression forces a null check. The discard clarifies your intent: the result of the
assignment isn't needed or used.
C#
The following example uses a standalone discard to ignore the Task object returned by
an asynchronous operation. Assigning the task has the effect of suppressing the
exception that the operation throws as it is about to complete. It makes your intent
clear: You want to discard the Task , and ignore any errors generated from that
asynchronous operation.
C#
Without assigning the task to a discard, the following code generates a compiler
warning:
C#
7 Note
If you run either of the preceding two samples using a debugger, the debugger will
stop the program when the exception is thrown. Without a debugger attached, the
exception is silently ignored in both cases.
_ is also a valid identifier. When used outside of a supported context, _ is treated not
as a discard but as a valid variable. If an identifier named _ is already in scope, the use
of _ as a standalone discard can result in:
C#
C#
Compiler error CS0136, "A local or parameter named '_' cannot be declared in this
scope because that name is used in an enclosing local scope to define a local or
parameter." For example:
C#
public void DoSomething(int _)
{
var _ = GetValue(); // Error: cannot declare local _ when one is
already in scope
}
// The example displays the following compiler error:
// error CS0136:
// A local or parameter named '_' cannot be declared in this
scope
// because that name is used in an enclosing local scope
// to define a local or parameter
See also
Remove unnecessary expression value (style rule IDE0058)
Remove unnecessary value assignment (style rule IDE0059)
Remove unused parameter (style rule IDE0060)
Deconstructing tuples and other types
is operator
switch expression
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Deconstructing tuples and other types
Article • 09/29/2022
A tuple provides a lightweight way to retrieve multiple values from a method call. But
once you retrieve the tuple, you have to handle its individual elements. Working on an
element-by-element basis is cumbersome, as the following example shows. The
QueryCityData method returns a three-tuple, and each of its elements is assigned to a
C#
Retrieving multiple field and property values from an object can be equally
cumbersome: you must assign a field or property value to a variable on a member-by-
member basis.
You can retrieve multiple elements from a tuple or retrieve multiple field, property, and
computed values from an object in a single deconstruct operation. To deconstruct a
tuple, you assign its elements to individual variables. When you deconstruct an object,
you assign selected values to individual variables.
Tuples
C# features built-in support for deconstructing tuples, which lets you unpackage all the
items in a tuple in a single operation. The general syntax for deconstructing a tuple is
similar to the syntax for defining one: you enclose the variables to which each element is
to be assigned in parentheses in the left side of an assignment statement. For example,
the following statement assigns the elements of a four-tuple to four separate variables:
C#
You can explicitly declare the type of each field inside parentheses. The following
example uses this approach to deconstruct the three-tuple returned by the
QueryCityData method.
C#
You can use the var keyword so that C# infers the type of each variable. You place
the var keyword outside of the parentheses. The following example uses type
inference when deconstructing the three-tuple returned by the QueryCityData
method.
C#
You can also use the var keyword individually with any or all of the variable
declarations inside the parentheses.
C#
public static void Main()
{
(string city, var population, var area) = QueryCityData("New York
City");
Lastly, you may deconstruct the tuple into variables that have already been
declared.
C#
C#
You can't specify a specific type outside the parentheses even if every field in the tuple
has the same type. Doing so generates compiler error CS8136, "Deconstruction 'var (...)'
form disallows a specific type for 'var'.".
You must assign each element of the tuple to a variable. If you omit any elements, the
compiler generates error CS8132, "Can't deconstruct a tuple of 'x' elements into 'y'
variables."
The following example illustrates the use of tuples with discards. The
QueryCityDataForYears method returns a six-tuple with the name of a city, its area, a
year, the city's population for that year, a second year, and the city's population for that
second year. The example shows the change in population between those two years. Of
the data available from the tuple, we're unconcerned with the city area, and we know
the city name and the two dates at design-time. As a result, we're only interested in the
two population values stored in the tuple, and can handle its remaining values as
discards.
C#
using System;
User-defined types
C# doesn't offer built-in support for deconstructing non-tuple types other than the
record and DictionaryEntry types. However, as the author of a class, a struct, or an
interface, you can allow instances of the type to be deconstructed by implementing one
or more Deconstruct methods. The method returns void, and each value to be
deconstructed is indicated by an out parameter in the method signature. For example,
the following Deconstruct method of a Person class returns the first, middle, and last
name:
C#
public void Deconstruct(out string fname, out string mname, out string
lname)
You can then deconstruct an instance of the Person class named p with an assignment
like the following code:
C#
C#
using System;
public void Deconstruct(out string fname, out string mname, out string
lname)
{
fname = FirstName;
mname = MiddleName;
lname = LastName;
}
Multiple Deconstruct methods having the same number of parameters are ambiguous.
You must be careful to define Deconstruct methods with different numbers of
parameters, or "arity". Deconstruct methods with the same number of parameters
cannot be distinguished during overload resolution.
The following example deconstructs a Person object into four strings (the first and last
names, the city, and the state) but discards the last name and the state.
C#
The following example defines two Deconstruct extension methods for the
System.Reflection.PropertyInfo class. The first returns a set of values that indicate the
characteristics of the property, including its type, whether it's static or instance, whether
it's read-only, and whether it's indexed. The second indicates the property's accessibility.
Because the accessibility of get and set accessors can differ, Boolean values indicate
whether the property has separate get and set accessors and, if it does, whether they
have the same accessibility. If there's only one accessor or both the get and the set
accessor have the same accessibility, the access variable indicates the accessibility of
the property as a whole. Otherwise, the accessibility of the get and set accessors are
indicated by the getAccess and setAccess variables.
C#
using System;
using System.Collections.Generic;
using System.Reflection;
if (getter != null)
{
if (getter.IsPublic)
getAccessTemp = "public";
else if (getter.IsPrivate)
getAccessTemp = "private";
else if (getter.IsAssembly)
getAccessTemp = "internal";
else if (getter.IsFamily)
getAccessTemp = "protected";
else if (getter.IsFamilyOrAssembly)
getAccessTemp = "protected internal";
}
if (setter != null)
{
if (setter.IsPublic)
setAccessTemp = "public";
else if (setter.IsPrivate)
setAccessTemp = "private";
else if (setter.IsAssembly)
setAccessTemp = "internal";
else if (setter.IsFamily)
setAccessTemp = "protected";
else if (setter.IsFamilyOrAssembly)
setAccessTemp = "protected internal";
}
if (!hasGetAndSet | sameAccess)
{
Console.WriteLine(accessibility);
}
else
{
Console.WriteLine($"\n The get accessor: {getAccessibility}");
Console.WriteLine($" The set accessor: {setAccessibility}");
}
}
}
// The example displays the following output:
// The System.DateTime.Now property:
// PropertyType: DateTime
// Static: True
// Read-only: True
// Indexed: False
//
// Accessibility of the System.Collections.Generic.List`1.Item
property: public
C#
You can add a Deconstruct method to system types that don't have one. Consider the
following extension method:
C#
This extension method allows all Nullable<T> types to be deconstructed into a tuple of
(bool hasValue, T value) . The following example shows code that uses this extension
method:
C#
questionableDateTime = DateTime.Now;
(hasValue, value) = questionableDateTime;
Console.WriteLine(
$"{{ HasValue = {hasValue}, Value = {value} }}");
// Example outputs:
// { HasValue = False, Value = 1/1/0001 12:00:00 AM }
// { HasValue = True, Value = 11/10/2021 6:11:45 PM }
record types
When you declare a record type by using two or more positional parameters, the
compiler creates a Deconstruct method with an out parameter for each positional
parameter in the record declaration. For more information, see Positional syntax for
property definition and Deconstructor behavior in derived records.
See also
Deconstruct variable declaration (style rule IDE0042)
Discards
Tuple types
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Exceptions and Exception Handling
Article • 04/22/2023
The C# language's exception handling features help you deal with any unexpected or
exceptional situations that occur when a program is running. Exception handling uses
the try , catch , and finally keywords to try actions that may not succeed, to handle
failures when you decide that it's reasonable to do so, and to clean up resources
afterward. Exceptions can be generated by the common language runtime (CLR), by
.NET or third-party libraries, or by application code. Exceptions are created by using the
throw keyword.
In many cases, an exception may be thrown not by a method that your code has called
directly, but by another method further down in the call stack. When an exception is
thrown, the CLR will unwind the stack, looking for a method with a catch block for the
specific exception type, and it will execute the first such catch block that it finds. If it
finds no appropriate catch block anywhere in the call stack, it will terminate the process
and display a message to the user.
In this example, a method tests for division by zero and catches the error. Without the
exception handling, this program would terminate with a DivideByZeroException was
unhandled error.
C#
try
{
result = SafeDivision(a, b);
Console.WriteLine("{0} divided by {1} = {2}", a, b, result);
}
catch (DivideByZeroException)
{
Console.WriteLine("Attempted divide by zero.");
}
}
}
Exceptions Overview
Exceptions have the following properties:
C# Language Specification
For more information, see Exceptions in the C# Language Specification. The language
specification is the definitive source for C# syntax and usage.
See also
System.Exception
Exception-handling statements
Exceptions
Use exceptions
Article • 09/15/2021
In C#, errors in the program at run time are propagated through the program by using a
mechanism called exceptions. Exceptions are thrown by code that encounters an error
and caught by code that can correct the error. Exceptions can be thrown by the .NET
runtime or by code in a program. Once an exception is thrown, it propagates up the call
stack until a catch statement for the exception is found. Uncaught exceptions are
handled by a generic exception handler provided by the system that displays a dialog
box.
Exceptions are represented by classes derived from Exception. This class identifies the
type of exception and contains properties that have details about the exception.
Throwing an exception involves creating an instance of an exception-derived class,
optionally configuring properties of the exception, and then throwing the object by
using the throw keyword. For example:
C#
After an exception is thrown, the runtime checks the current statement to see whether it
is within a try block. If it is, any catch blocks associated with the try block are checked
to see whether they can catch the exception. Catch blocks typically specify exception
types; if the type of the catch block is the same type as the exception, or a base class of
the exception, the catch block can handle the method. For example:
C#
try
{
TestThrow();
}
catch (CustomException ex)
{
System.Console.WriteLine(ex.ToString());
}
If the statement that throws an exception isn't within a try block or if the try block
that encloses it has no matching catch block, the runtime checks the calling method for
a try statement and catch blocks. The runtime continues up the calling stack,
searching for a compatible catch block. After the catch block is found and executed,
control is passed to the next statement after that catch block.
A try statement can contain more than one catch block. The first catch statement that
can handle the exception is executed; any following catch statements, even if they're
compatible, are ignored. Order catch blocks from most specific (or most-derived) to
least specific. For example:
C#
using System;
using System.IO;
namespace Exceptions
{
public class CatchOrder
{
public static void Main()
{
try
{
using (var sw = new StreamWriter("./test.txt"))
{
sw.WriteLine("Hello");
}
}
// Put the more specific exceptions first.
catch (DirectoryNotFoundException ex)
{
Console.WriteLine(ex);
}
catch (FileNotFoundException ex)
{
Console.WriteLine(ex);
}
// Put the least specific exception last.
catch (IOException ex)
{
Console.WriteLine(ex);
}
Console.WriteLine("Done");
}
}
}
Before the catch block is executed, the runtime checks for finally blocks. Finally
blocks enable the programmer to clean up any ambiguous state that could be left over
from an aborted try block, or to release any external resources (such as graphics
handles, database connections, or file streams) without waiting for the garbage collector
in the runtime to finalize the objects. For example:
C#
try
{
file = fileInfo.OpenWrite();
file.WriteByte(0xF);
}
finally
{
// Closing the file allows you to reopen it immediately - otherwise
IOException is thrown.
file?.Close();
}
try
{
file = fileInfo.OpenWrite();
Console.WriteLine("OpenWrite() succeeded");
}
catch (IOException)
{
Console.WriteLine("OpenWrite() failed");
}
}
If WriteByte() threw an exception, the code in the second try block that tries to
reopen the file would fail if file.Close() isn't called, and the file would remain locked.
Because finally blocks are executed even if an exception is thrown, the finally block
in the previous example allows for the file to be closed correctly and helps avoid an
error.
If no compatible catch block is found on the call stack after an exception is thrown, one
of three things occurs:
If the exception is within a finalizer, the finalizer is aborted and the base finalizer, if
any, is called.
If the call stack contains a static constructor, or a static field initializer, a
TypeInitializationException is thrown, with the original exception assigned to the
InnerException property of the new exception.
If the start of the thread is reached, the thread is terminated.
Exception Handling (C# Programming
Guide)
Article • 03/14/2023
C#
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
// Only catch exceptions that you know how to handle.
// Never catch base class System.Exception without
// rethrowing it at the end of the catch block.
}
C#
try
{
// Code to try goes here.
}
finally
{
// Code to execute after the try block goes here.
}
C#
try
{
// Code to try goes here.
}
catch (SomeSpecificException ex)
{
// Code to handle the exception goes here.
}
finally
{
// Code to execute after the try (and possibly catch) blocks
// goes here.
}
Catch Blocks
A catch block can specify the type of exception to catch. The type specification is called
an exception filter. The exception type should be derived from Exception. In general,
don't specify Exception as the exception filter unless either you know how to handle all
exceptions that might be thrown in the try block, or you've included a throw statement
at the end of your catch block.
Multiple catch blocks with different exception classes can be chained together. The
catch blocks are evaluated from top to bottom in your code, but only one catch block
is executed for each exception that is thrown. The first catch block that specifies the
exact type or a base class of the thrown exception is executed. If no catch block
specifies a matching exception class, a catch block that doesn't have any type is
selected, if one is present in the statement. It's important to position catch blocks with
the most specific (that is, the most derived) exception classes first.
You have a good understanding of why the exception might be thrown, and you
can implement a specific recovery, such as prompting the user to enter a new file
name when you catch a FileNotFoundException object.
You can create and throw a new, more specific exception.
C#
You want to partially handle an exception before passing it on for more handling.
In the following example, a catch block is used to add an entry to an error log
before rethrowing the exception.
C#
try
{
// Try to access a resource.
}
catch (UnauthorizedAccessException e)
{
// Call a custom error logging procedure.
LogError(e);
// Re-throw the error.
throw;
}
You can also specify exception filters to add a boolean expression to a catch clause.
Exception filters indicate that a specific catch clause matches only when that condition is
true. In the following example, both catch clauses use the same exception class, but an
extra condition is checked to create a different error message:
C#
An exception filter that always returns false can be used to examine all exceptions but
not process them. A typical use is to log exceptions:
C#
The LogException method always returns false , no catch clause using this exception
filter matches. The catch clause can be general, using System.Exception, and later
clauses can process more specific exception classes.
Finally Blocks
A finally block enables you to clean up actions that are performed in a try block. If
present, the finally block executes last, after the try block and any matched catch
block. A finally block always runs, whether an exception is thrown or a catch block
matching the exception type is found.
The finally block can be used to release resources such as file streams, database
connections, and graphics handles without waiting for the garbage collector in the
runtime to finalize the objects.
In the following example, the finally block is used to close a file that is opened in the
try block. Notice that the state of the file handle is checked before the file is closed. If
the try block can't open the file, the file handle still has the value null and the finally
block doesn't try to close it. Instead, if the file is opened successfully in the try block,
the finally block closes the open file.
C#
C# Language Specification
For more information, see Exceptions and The try statement in the C# Language
Specification. The language specification is the definitive source for C# syntax and
usage.
See also
C# reference
Exception-handling statements
using statement
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Create and throw exceptions
Article • 11/03/2023
Exceptions are used to indicate that an error has occurred while running the program.
Exception objects that describe an error are created and then thrown with the throw
statement or expression. The runtime then searches for the most compatible exception
handler.
Programmers should throw exceptions when one or more of the following conditions
are true:
The method can't complete its defined functionality. For example, if a parameter to
a method has an invalid value:
C#
An inappropriate call to an object is made, based on the object state. One example
might be trying to write to a read-only file. In cases where an object state doesn't
allow an operation, throw an instance of InvalidOperationException or an object
based on a derivation of this class. The following code is an example of a method
that throws an InvalidOperationException object:
C#
C#
7 Note
The preceding example shows how to use the InnerException property. It's
intentionally simplified. In practice, you should check that an index is in range
before using it. You could use this technique of wrapping an exception when a
member of a parameter throws an exception you couldn't anticipate before
calling the member.
Exceptions contain a property named StackTrace. This string contains the name of the
methods on the current call stack, together with the file name and line number where
the exception was thrown for each method. A StackTrace object is created automatically
by the common language runtime (CLR) from the point of the throw statement, so that
exceptions must be thrown from the point where the stack trace should begin.
All exceptions contain a property named Message. This string should be set to explain
the reason for the exception. Information that is sensitive to security shouldn't be put in
the message text. In addition to Message, ArgumentException contains a property
named ParamName that should be set to the name of the argument that caused the
exception to be thrown. In a property setter, ParamName should be set to value .
Public and protected methods throw exceptions whenever they can't complete their
intended functions. The exception class thrown is the most specific exception available
that fits the error conditions. These exceptions should be documented as part of the
class functionality, and derived classes or updates to the original class should retain the
same behavior for backward compatibility.
We recommend that you validate arguments and throw any corresponding exceptions,
such as ArgumentException and ArgumentNullException, before entering the
asynchronous parts of your methods. That is, these validation exceptions should emerge
synchronously before the work starts. The following code snippet shows an example
where, if the exceptions are thrown, the ArgumentException exceptions would emerge
synchronously, whereas the InvalidOperationException would be stored in the returned
task.
C#
if (toastTime < 1)
{
throw new ArgumentException(
"Toast time is too short.", nameof(toastTime));
}
Console.WriteLine("Toast is ready!");
C#
[Serializable]
public class InvalidDepartmentException : Exception
{
public InvalidDepartmentException() : base() { }
public InvalidDepartmentException(string message) : base(message) { }
public InvalidDepartmentException(string message, Exception inner) :
base(message, inner) { }
}
Add new properties to the exception class when the data they provide is useful to
resolving the exception. If new properties are added to the derived exception class,
ToString() should be overridden to return the added information.
C# language specification
For more information, see Exceptions and The throw statement in the C# Language
Specification. The language specification is the definitive source for C# syntax and
usage.
See also
Exception Hierarchy
6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open
source. Provide feedback here.
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Compiler-generated exceptions
Article • 04/22/2023
Some exceptions are thrown automatically by the .NET runtime when basic operations
fail. These exceptions and their error conditions are listed in the following table.
Exception Description
ArrayTypeMismatchException Thrown when an array can't store a given element because the
actual type of the element is incompatible with the actual type of
the array.
See also
Exception-handling statements
C# identifier naming rules and
conventions
Article • 12/15/2023
An identifier is the name you assign to a type (class, interface, struct, delegate, or
enum), member, variable, or namespace.
Naming rules
Valid identifiers must follow these rules. The C# compiler produces an error for any
identifier that doesn't follow these rules:
You can declare identifiers that match C# keywords by using the @ prefix on the
identifier. The @ isn't part of the identifier name. For example, @if declares an identifier
named if . These verbatim identifiers are primarily for interoperability with identifiers
declared in other languages.
For a complete definition of valid identifiers, see the Identifiers article in the C#
Language Specification.
) Important
The C# language specification only allows letter (Lu, Ll, Lt, Lm, Lo or Nl), digit (Nd),
connecting (Pc), combining (Mn or Mc), and formatting (Cf) categories. Anything
outside that is automatically replaced using _ . This might impact certain Unicode
characters.
Naming conventions
In addition to the rules, conventions for identifier names are used throughout the .NET
APIs. These conventions provide consistency for names, but the compiler doesn't
enforce them. You're free to use different conventions in your projects.
By convention, C# programs use PascalCase for type names, namespaces, and all public
members. In addition, the dotnet/docs team uses the following conventions, adopted
from the .NET Runtime team's coding style :
Enum types use a singular noun for nonflags, and a plural noun for flags.
Use meaningful and descriptive names for variables, methods, and classes.
Use PascalCase for constant names, both fields and local constants.
Private instance fields start with an underscore ( _ ) and the remaining text is
camelCased.
Static fields start with s_ . This convention isn't the default Visual Studio behavior,
nor part of the Framework design guidelines, but is configurable in editorconfig.
Avoid using abbreviations or acronyms in names, except for widely known and
accepted abbreviations.
Use meaningful and descriptive namespaces that follow the reverse domain name
notation.
Choose assembly names that represent the primary purpose of the assembly.
Avoid using single-letter names, except for simple loop counters. Also, syntax
examples that describe the syntax of C# constructs often use the following single-
letter names that match the convention used in the C# language specification.
Syntax examples are an exception to the rule.
Use S for structs, C for classes.
Use M for methods.
Use v for variables, p for parameters.
Use r for ref parameters.
Tip
You can enforce naming conventions that concern capitalization, prefixes, suffixes,
and word separators by using code-style naming rules.
Pascal case
Use pascal casing ("PascalCasing") when naming a class , interface , struct , or
delegate type.
C#
C#
C#
C#
When naming an interface , use pascal casing in addition to prefixing the name with an
I . This prefix clearly indicates to consumers that it's an interface .
C#
public interface IWorkerQueue
{
}
When naming public members of types, such as fields, properties, events, use pascal
casing. Also, use pascal casing for all methods and local functions.
C#
// An init-only property
public IWorkerQueue WorkerQueue { get; init; }
// An event
public event Action EventProcessing;
// Method
public void StartEventProcessing()
{
// Local function
static int CountQueueItems() => WorkerQueue.Count;
// ...
}
}
When writing positional records, use pascal casing for parameters as they're the public
properties of the record.
C#
For more information on positional records, see Positional syntax for property definition.
Camel case
Use camel casing ("camelCasing") when naming private or internal fields and prefix
them with _ . Use camel casing when naming local variables, including instances of a
delegate type.
C#
Tip
When editing C# code that follows these naming conventions in an IDE that
supports statement completion, typing _ will show all of the object-scoped
members.
When working with static fields that are private or internal , use the s_ prefix and
for thread static use t_ .
C#
[ThreadStatic]
private static TimeSpan t_timeSpan;
}
C#
For more information on C# naming conventions, see the .NET Runtime team's coding
style .
./snippets/coding-conventions
Consider using T as the type parameter name for types with one single letter type
parameter.
./snippets/coding-conventions
./snippets/coding-conventions
The code analysis rule CA1715 can be used to ensure that type parameters are named
appropriately.
C#
You don't have to change the names of objects that were created by using the
Visual Studio designer tools to make them fit other guidelines.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Common C# code conventions
Article • 08/01/2023
Coding conventions are essential for maintaining code readability, consistency, and
collaboration within a development team. Code that follows industry practices and
established guidelines is easier to understand, maintain, and extend. Most projects
enforce a consistent style through code conventions. The dotnet/docs and
dotnet/samples projects are no exception. In this series of articles, you learn our
coding conventions and the tools we use to enforce them. You can take our conventions
as-is, or modify them to suit your team's needs.
1. Correctness: Our samples are copied and pasted into your applications. We expect
that, so we need to make code that's resilient and correct, even after multiple edits.
2. Teaching: The purpose of our samples is to teach all of .NET and C#. For that
reason, we don't place restrictions on any language feature or API. Instead, those
samples teach when a feature is a good choice.
3. Consistency: Readers expect a consistent experience across our content. All
samples should conform to the same style.
4. Adoption: We aggressively update our samples to use new language features. That
practice raises awareness of new features, and makes them more familiar to all C#
developers.
) Important
The teaching and adoption goals are why the docs coding convention differs from
the runtime and compiler conventions. Both the runtime and compiler have strict
performance metrics for hot paths. Many other applications don't. Our teaching
goal mandates that we don't prohibit any construct. Instead, samples show when
constructs should be used. We update samples more aggressively than most
production applications do. Our adoption goal mandates that we show code you
should write today, even when code written last year doesn't need changes.
This article explains our guidelines. The guidelines have evolved over time, and you'll
find samples that don't follow our guidelines. We welcome PRs that bring those samples
into compliance, or issues that draw our attention to samples we should update. Our
guidelines are Open Source and we welcome PRs and issues. However, if your
submission would change these recommendations, open an issue for discussion first.
You're welcome to use our guidelines, or adapt them to your needs.
These tools make it easier for your team to adopt your preferred guidelines. Visual
Studio applies the rules in all .editorconfig files in scope to format your code. You can
use multiple configurations to enforce corporate-wide conventions, team conventions,
and even granular project conventions.
Code analysis produces warnings and diagnostics when the enabled rules are violated.
You configure the rules you want applied to your project. Then, each CI build notifies
developers when they violate any of the rules.
Diagnostic IDs
Choose appropriate diagnostic IDs when building your own analyzers
Language guidelines
The following sections describe practices that the .NET docs team follows to prepare
code examples and samples. In general, follow these practices:
String data
Use string interpolation to concatenate short strings, as shown in the following
code.
C#
To append strings in loops, especially when you're working with large amounts of
text, use a System.Text.StringBuilder object.
C#
var phrase =
"lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
var manyPhrases = new StringBuilder();
for (var i = 0; i < 10000; i++)
{
manyPhrases.Append(phrase);
}
//Console.WriteLine("tra" + manyPhrases);
Arrays
Use the concise syntax when you initialize arrays on the declaration line. In the
following example, you can't use var instead of string[] .
C#
C#
Delegates
Use Func<> and Action<> instead of defining delegate types. In a class, define the
delegate method.
C#
Call the method using the signature defined by the Func<> or Action<> delegate.
C#
If you create instances of a delegate type, use the concise syntax. In a class, define
the delegate type and a method that has a matching signature.
C#
public delegate void Del(string message);
Create an instance of the delegate type and call it. The following declaration shows
the condensed syntax.
C#
C#
C#
Simplify your code by using the C# using statement. If you have a try-finally
statement in which the only code in the finally block is a call to the Dispose
method, use a using statement instead.
In the following example, the try-finally statement only calls Dispose in the
finally block.
C#
C#
C#
Use && instead of & and || instead of | when you perform comparisons, as shown
in the following example.
C#
If the divisor is 0, the second clause in the if statement would cause a run-time error.
But the && operator short-circuits when the first expression is false. That is, it doesn't
evaluate the second expression. The & operator would evaluate both, resulting in a run-
time error when divisor is 0.
new operator
Use one of the concise forms of object instantiation, as shown in the following
declarations.
C#
C#
C#
C#
C#
Event handling
Use a lambda expression to define an event handler that you don't need to
remove later:
C#
public Form2()
{
this.Click += (s, e) =>
{
MessageBox.Show(
((MouseEventArgs)e).Location.ToString());
};
}
C#
public Form1()
{
this.Click += new EventHandler(Form1_Click);
}
Static members
Call static members by using the class name: ClassName.StaticMember. This practice
makes code more readable by making static access clear. Don't qualify a static member
defined in a base class with the name of a derived class. While that code compiles, the
code readability is misleading, and the code may break in the future if you add a static
member with the same name to the derived class.
LINQ queries
Use meaningful names for query variables. The following example uses
seattleCustomers for customers who are located in Seattle.
C#
Use aliases to make sure that property names of anonymous types are correctly
capitalized, using Pascal casing.
C#
var localDistributors =
from customer in customers
join distributor in distributors on customer.City equals
distributor.City
select new { Customer = customer, Distributor = distributor };
Rename properties when the property names in the result would be ambiguous.
For example, if your query returns a customer name and a distributor ID, instead of
leaving them as Name and ID in the result, rename them to clarify that Name is the
name of a customer, and ID is the ID of a distributor.
C#
var localDistributors2 =
from customer in customers
join distributor in distributors on customer.City equals
distributor.City
select new { CustomerName = customer.Name, DistributorID =
distributor.ID };
Use implicit typing in the declaration of query variables and range variables. This
guidance on implicit typing in LINQ queries overrides the general rules for
implicitly typed local variables. LINQ queries often use projections that create
anonymous types. Other query expressions create results with nested generic
types. Implicit typed variables are often more readable.
C#
Align query clauses under the from clause, as shown in the previous examples.
Use where clauses before other query clauses to ensure that later query clauses
operate on the reduced, filtered set of data.
C#
Use multiple from clauses instead of a join clause to access inner collections. For
example, a collection of Student objects might each contain a collection of test
scores. When the following query is executed, it returns each score that is over 90,
along with the last name of the student who received the score.
C#
C#
Don't use var when the type isn't apparent from the right side of the assignment.
Don't assume the type is clear from a method name. A variable type is considered
clear if it's a new operator, an explicit cast or assignment to a literal value.
C#
Don't use variable names to specify the type of the variable. It might not be
correct. Instead, use the type to specify the type, and use the variable name to
indicate the semantic information of the variable. The following example should
use string for the type and something like iterations to indicate the meaning of
the information read from the console.
C#
Avoid the use of var in place of dynamic. Use dynamic when you want run-time
type inference. For more information, see Using type dynamic (C# Programming
Guide).
C#
var phrase =
"lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
var manyPhrases = new StringBuilder();
for (var i = 0; i < 10000; i++)
{
manyPhrases.Append(phrase);
}
//Console.WriteLine("tra" + manyPhrases);
Don't use implicit typing to determine the type of the loop variable in foreach
loops. In most cases, the type of elements in the collection isn't immediately
obvious. The collection's name shouldn't be solely relied upon for inferring the
type of its elements.
C#
use implicit type for the result sequences in LINQ queries. The section on LINQ
explains that many LINQ queries result in anonymous types where implicit types
must be used. Other queries result in nested generic types where var is more
readable.
7 Note
Some of our samples explain the natural type of an expression. Those samples must use
var so that the compiler picks the natural type. Even though those examples are less
obvious, the use of var is required for the sample. The text should explain the behavior.
C#
using Azure;
namespace CoolStuff.AwesomeFeature
{
public class Awesome
{
public void Stuff()
{
WaitUntil wait = WaitUntil.Completed;
// ...
}
}
}
C#
namespace CoolStuff.AwesomeFeature
{
using Azure;
And it compiles today. And tomorrow. But then sometime next week the preceding
(untouched) code fails with two errors:
Console
- error CS0246: The type or namespace name 'WaitUntil' could not be found
(are you missing a using directive or an assembly reference?)
- error CS0103: The name 'WaitUntil' does not exist in the current context
One of the dependencies has introduced this class in a namespace then ends with
.Azure :
C#
namespace CoolStuff.Azure
{
public class SecretsManagement
{
public string FetchFromKeyVault(string vaultId, string secretId) {
return null; }
}
}
Azure
could resolve it by adding the global:: modifier to the using declaration. However, it's
easier to place using declarations outside the namespace instead.
C#
namespace CoolStuff.AwesomeFeature
{
using global::Azure;
Style guidelines
In general, use the following format for code samples:
Comment style
Use single-line comments ( // ) for brief explanations.
Place the comment on a separate line, not at the end of a line of code.
Insert one space between the comment delimiter ( // ) and the comment text, as
shown in the following example.
C#
Layout conventions
Good layout uses formatting to emphasize the structure of your code and to make the
code easier to read. Microsoft examples and samples conform to the following
conventions:
Use the default Code Editor settings (smart indenting, four-character indents, tabs
saved as spaces). For more information, see Options, Text Editor, C#, Formatting.
If continuation lines aren't indented automatically, indent them one tab stop (four
spaces).
Add at least one blank line between method definitions and property definitions.
C#
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to display command-line
arguments
Article • 03/11/2022
executable.exe a b c "a"
"b"
"c"
"two"
"three"
7 Note
When you are running an application in Visual Studio, you can specify command-
line arguments in the Debug Page, Project Designer.
Example
This example displays the command-line arguments passed to a command-line
application. The output shown is for the first entry in the table above.
C#
See also
System.CommandLine overview
Tutorial: Get started with System.CommandLine
Explore object oriented programming
with classes and objects
Article • 04/22/2023
In this tutorial, you'll build a console application and see the basic object-oriented
features that are part of the C# language.
Prerequisites
We recommend Visual Studio for Windows. You can download a free version
from the Visual Studio downloads page . Visual Studio includes the .NET SDK.
You can also use the Visual Studio Code editor with the C# DevKit . You'll need
to install the latest .NET SDK separately.
If you prefer a different editor, you need to install the latest .NET SDK .
C#
In this tutorial, you're going to create new types that represent a bank account. Typically
developers define each class in a different text file. That makes it easier to manage as a
program grows in size. Create a new file named BankAccount.cs in the Classes directory.
This file will contain the definition of a bank account. Object Oriented programming
organizes code by creating types in the form of classes. These classes contain the code
that represents a specific entity. The BankAccount class represents a bank account. The
code implements specific operations through methods and properties. In this tutorial,
the bank account supports this behavior:
C#
namespace Classes;
Before going on, let's take a look at what you've built. The namespace declaration
provides a way to logically organize your code. This tutorial is relatively small, so you'll
put all the code in one namespace.
public class BankAccount defines the class, or type, you're creating. Everything inside
the { and } that follows the class declaration defines the state and behavior of the
class. There are five members of the BankAccount class. The first three are properties.
Properties are data elements and can have code that enforces validation or other rules.
The last two are methods. Methods are blocks of code that perform a single function.
Reading the names of each of the members should provide enough information for you
or another developer to understand what the class does.
Creating a new object of the BankAccount type means defining a constructor that
assigns those values. A constructor is a member that has the same name as the class. It's
used to initialize objects of that class type. Add the following constructor to the
BankAccount type. Place the following code above the declaration of MakeDeposit :
C#
The preceding code identifies the properties of the object being constructed by
including the this qualifier. That qualifier is usually optional and omitted. You could
also have written:
C#
The this qualifier is only required when a local variable or parameter has the same
name as that field or property. The this qualifier is omitted throughout the remainder
of this article unless it's necessary.
Constructors are called when you create an object using new. Replace the line
Console.WriteLine("Hello World!"); in Program.cs with the following code (replace
<name> with your name):
C#
using Classes;
Did you notice that the account number is blank? It's time to fix that. The account
number should be assigned when the object is constructed. But it shouldn't be the
responsibility of the caller to create it. The BankAccount class code should know how to
assign new account numbers. A simple way is to start with a 10-digit number. Increment
it when each new account is created. Finally, store the current account number when an
object is constructed.
Add a member declaration to the BankAccount class. Place the following line of code
after the opening brace { at the beginning of the BankAccount class:
C#
The accountNumberSeed is a data member. It's private , which means it can only be
accessed by code inside the BankAccount class. It's a way of separating the public
responsibilities (like having an account number) from the private implementation (how
account numbers are generated). It's also static , which means it's shared by all of the
BankAccount objects. The value of a non-static variable is unique to each instance of the
BankAccount object. The accountNumberSeed is a private static field and thus has the
number. Place them after the line that says this.Balance = initialBalance :
C#
Number = s_accountNumberSeed.ToString();
s_accountNumberSeed++;
Let's start by creating a new type to represent a transaction. The transaction is a simple
type that doesn't have any responsibilities. It needs a few properties. Create a new file
named Transaction.cs. Add the following code to it:
C#
namespace Classes;
Now, let's add a List<T> of Transaction objects to the BankAccount class. Add the
following declaration after the constructor in your BankAccount.cs file:
C#
Now, let's correctly compute the Balance . The current balance can be found by
summing the values of all transactions. As the code is currently, you can only get the
initial balance of the account, so you'll have to update the Balance property. Replace
the line public decimal Balance { get; } in BankAccount.cs with the following code:
C#
return balance;
}
}
This example shows an important aspect of properties. You're now computing the
balance when another programmer asks for the value. Your computation enumerates all
transactions, and provides the sum as the current balance.
Next, implement the MakeDeposit and MakeWithdrawal methods. These methods will
enforce the final two rules: the initial balance must be positive, and any withdrawal must
not create a negative balance.
These rules introduce the concept of exceptions. The standard way of indicating that a
method can't complete its work successfully is to throw an exception. The type of
exception and the message associated with it describe the error. Here, the MakeDeposit
method throws an exception if the amount of the deposit isn't greater than 0. The
MakeWithdrawal method throws an exception if the withdrawal amount isn't greater than
0, or if applying the withdrawal results in a negative balance. Add the following code
after the declaration of the _allTransactions list:
C#
The throw statement throws an exception. Execution of the current block ends, and
control transfers to the first matching catch block found in the call stack. You'll add a
catch block to test this code a little later on.
The constructor should get one change so that it adds an initial transaction, rather than
updating the balance directly. Since you already wrote the MakeDeposit method, call it
from your constructor. The finished constructor should look like this:
C#
Owner = name;
MakeDeposit(initialBalance, DateTime.Now, "Initial balance");
}
DateTime.Now is a property that returns the current date and time. Test this code by
adding a few deposits and withdrawals in your Main method, following the code that
creates a new BankAccount :
C#
Next, test that you're catching error conditions by trying to create an account with a
negative balance. Add the following code after the preceding code you just added:
C#
You use the try-catch statement to mark a block of code that may throw exceptions and
to catch those errors that you expect. You can use the same technique to test the code
that throws an exception for a negative balance. Add the following code before the
declaration of invalidAccount in your Main method:
C#
C#
decimal balance = 0;
report.AppendLine("Date\t\tAmount\tBalance\tNote");
foreach (var item in _allTransactions)
{
balance += item.Amount;
report.AppendLine($"
{item.Date.ToShortDateString()}\t{item.Amount}\t{balance}\t{item.Notes}");
}
return report.ToString();
}
The history uses the StringBuilder class to format a string that contains one line for each
transaction. You've seen the string formatting code earlier in these tutorials. One new
character is \t . That inserts a tab to format the output.
C#
Console.WriteLine(account.GetAccountHistory());
Next steps
If you got stuck, you can see the source for this tutorial in our GitHub repo .
Selection statements
Iteration statements
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Object-Oriented programming (C#)
Article • 07/11/2023
In the preceding tutorial, introduction to classes you saw both abstraction and
encapsulation. The BankAccount class provided an abstraction for the concept of a bank
account. You could modify its implementation without affecting any of the code that
used the BankAccount class. Both the BankAccount and Transaction classes provide
encapsulation of the components needed to describe those concepts in code.
In this tutorial, you'll extend that application to make use of inheritance and
polymorphism to add new features. You'll also add features to the BankAccount class,
taking advantage of the abstraction and encapsulation techniques you learned in the
preceding tutorial.
An interest earning account that accrues interest at the end of each month.
A line of credit that can have a negative balance, but when there's a balance,
there's an interest charge each month.
A pre-paid gift card account that starts with a single deposit, and only can be paid
off. It can be refilled once at the start of each month.
All of these different accounts are similar to BankAccount class defined in the earlier
tutorial. You could copy that code, rename the classes, and make modifications. That
technique would work in the short term, but it would be more work over time. Any
changes would be copied across all the affected classes.
Instead, you can create new bank account types that inherit methods and data from the
BankAccount class created in the preceding tutorial. These new classes can extend the
BankAccount class with the specific behavior needed for each type:
C#
Each of these classes inherits the shared behavior from their shared base class, the
BankAccount class. Write the implementations for new and different functionality in each
of the derived classes. These derived classes already have all the behavior defined in the
BankAccount class.
It's a good practice to create each new class in a different source file. In Visual Studio ,
you can right-click on the project, and select add class to add a new class in a new file. In
Visual Studio Code , select File then New to create a new source file. In either tool,
name the file to match the class: InterestEarningAccount.cs, LineOfCreditAccount.cs, and
GiftCardAccount.cs.
When you create the classes as shown in the preceding sample, you'll find that none of
your derived classes compile. A constructor is responsible for initializing an object. A
derived class constructor must initialize the derived class, and provide instructions on
how to initialize the base class object included in the derived class. The proper
initialization normally happens without any extra code. The BankAccount class declares
one public constructor with the following signature:
C#
The compiler doesn't generate a default constructor when you define a constructor
yourself. That means each derived class must explicitly call this constructor. You declare
a constructor that can pass arguments to the base class constructor. The following code
shows the constructor for the InterestEarningAccount :
C#
The parameters to this new constructor match the parameter type and names of the
base class constructor. You use the : base() syntax to indicate a call to a base class
constructor. Some classes define multiple constructors, and this syntax enables you to
pick which base class constructor you call. Once you've updated the constructors, you
can develop the code for each of the derived classes. The requirements for the new
classes can be stated as follows:
You can see that all three of these account types have an action that takes places at the
end of each month. However, each account type does different tasks. You use
polymorphism to implement this code. Create a single virtual method in the
BankAccount class:
C#
The preceding code shows how you use the virtual keyword to declare a method in
the base class that a derived class may provide a different implementation for. A
virtual method is a method where any derived class may choose to reimplement. The
derived classes use the override keyword to define the new implementation. Typically
you refer to this as "overriding the base class implementation". The virtual keyword
specifies that derived classes may override the behavior. You can also declare abstract
methods where derived classes must override the behavior. The base class does not
provide an implementation for an abstract method. Next, you need to define the
implementation for two of the new classes you've created. Start with the
InterestEarningAccount :
C#
Add the following code to the LineOfCreditAccount . The code negates the balance to
compute a positive interest charge that is withdrawn from the account:
C#
C#
The constructor provides a default value for the monthlyDeposit value so callers can
omit a 0 for no monthly deposit. Next, override the PerformMonthEndTransactions
method to add the monthly deposit, if it was set to a non-zero value in the constructor:
C#
The override applies the monthly deposit set in the constructor. Add the following code
to the Main method to test these changes for the GiftCardAccount and the
InterestEarningAccount :
C#
Verify the results. Now, add a similar set of test code for the LineOfCreditAccount :
C#
Console
7 Note
The actual output includes the full path to the folder with the project. The folder
names were omitted for brevity. Also, depending on your code format, the line
numbers may be slightly different.
This code fails because the BankAccount assumes that the initial balance must be greater
than 0. Another assumption baked into the BankAccount class is that the balance can't
go negative. Instead, any withdrawal that overdraws the account is rejected. Both of
those assumptions need to change. The line of credit account starts at 0, and generally
will have a negative balance. Also, if a customer borrows too much money, they incur a
fee. The transaction is accepted, it just costs more. The first rule can be implemented by
adding an optional argument to the BankAccount constructor that specifies the
minimum balance. The default is 0 . The second rule requires a mechanism that enables
derived classes to modify the default algorithm. In a sense, the base class "asks" the
derived type what should happen when there's an overdraft. The default behavior is to
reject the transaction by throwing an exception.
C#
private readonly decimal _minimumBalance;
Owner = name;
_minimumBalance = minimumBalance;
if (initialBalance > 0)
MakeDeposit(initialBalance, DateTime.Now, "Initial balance");
}
The preceding code shows two new techniques. First, the minimumBalance field is marked
as readonly . That means the value cannot be changed after the object is constructed.
Once a BankAccount is created, the minimumBalance can't change. Second, the
constructor that takes two parameters uses : this(name, initialBalance, 0) { } as its
implementation. The : this() expression calls the other constructor, the one with three
parameters. This technique allows you to have a single implementation for initializing an
object even though client code can choose one of many constructors.
This implementation calls MakeDeposit only if the initial balance is greater than 0 . That
preserves the rule that deposits must be positive, yet lets the credit account open with a
0 balance.
Now that the BankAccount class has a read-only field for the minimum balance, the final
change is to change the hard code 0 to minimumBalance in the MakeWithdrawal method:
C#
After extending the BankAccount class, you can modify the LineOfCreditAccount
constructor to call the new base constructor, as shown in the following code:
C#
One technique is to define a virtual function where you implement the required
behavior. The BankAccount class refactors the MakeWithdrawal method into two
methods. The new method does the specified action when the withdrawal takes the
balance below the minimum. The existing MakeWithdrawal method has the following
code:
C#
C#
The added method is protected , which means that it can be called only from derived
classes. That declaration prevents other clients from calling the method. It's also
virtual so that derived classes can change the behavior. The return type is a
Transaction? . The ? annotation indicates that the method may return null . Add the
C#
The override returns a fee transaction when the account is overdrawn. If the withdrawal
doesn't go over the limit, the method returns a null transaction. That indicates there's
no fee. Test these changes by adding the following code to your Main method in the
Program class:
C#
Summary
If you got stuck, you can see the source for this tutorial in our GitHub repo .
You used Abstraction when you defined classes for each of the different account
types. Those classes described the behavior for that type of account.
You used Encapsulation when you kept many details private in each class.
You used Inheritance when you leveraged the implementation already created in
the BankAccount class to save code.
You used Polymorphism when you created virtual methods that derived classes
could override to create specific behavior for that account type.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Inheritance in C# and .NET
Article • 02/03/2023
Prerequisites
We recommend Visual Studio for Windows. You can download a free version
from the Visual Studio downloads page . Visual Studio includes the .NET SDK.
You can also use the Visual Studio Code editor with the C# DevKit . You'll need
to install the latest .NET SDK separately.
If you prefer a different editor, you need to install the latest .NET SDK .
2. Enter the dotnet new console command at a command prompt to create a new
.NET Core project.
3. Copy and paste the code from the example into your code editor.
4. Enter the dotnet restore command from the command line to load or restore the
project's dependencies.
You don't have to run dotnet restore because it's run implicitly by all commands
that require a restore to occur, such as dotnet new , dotnet build , dotnet run ,
dotnet test , dotnet publish , and dotnet pack . To disable implicit restore, use the
--no-restore option.
The dotnet restore command is still useful in certain scenarios where explicitly
restoring makes sense, such as continuous integration builds in Azure DevOps
Services or in build systems that need to explicitly control when the restore occurs.
For information about how to manage NuGet feeds, see the dotnet restore
documentation.
5. Enter the dotnet run command to compile and execute the example.
C# and .NET support single inheritance only. That is, a class can only inherit from a single
class. However, inheritance is transitive, which allows you to define an inheritance
hierarchy for a set of types. In other words, type D can inherit from type C , which
inherits from type B , which inherits from the base class type A . Because inheritance is
transitive, the members of type A are available to type D .
Not all members of a base class are inherited by derived classes. The following members
are not inherited:
Instance constructors, which you call to create a new instance of the class. Each
class must define its own constructors.
Finalizers, which are called by the runtime's garbage collector to destroy instances
of a class.
While all other members of a base class are inherited by derived classes, whether they
are visible or not depends on their accessibility. A member's accessibility affects its
visibility for derived classes as follows:
Private members are visible only in derived classes that are nested in their base
class. Otherwise, they are not visible in derived classes. In the following example,
A.B is a nested class that derives from A , and C derives from A . The private
A._value field is visible in A.B. However, if you remove the comments from the
C#
public class A
{
private int _value = 10;
public class B : A
{
public int GetValue()
{
return _value;
}
}
}
public class C : A
{
// public int GetValue()
// {
// return _value;
// }
}
Internal members are visible only in derived classes that are located in the same
assembly as the base class. They are not visible in derived classes located in a
different assembly from the base class.
Public members are visible in derived classes and are part of the derived class'
public interface. Public inherited members can be called just as if they are defined
in the derived class. In the following example, class A defines a method named
Method1 , and class B inherits from class A . The example then calls Method1 as if it
C#
public class A
{
public void Method1()
{
// Method implementation.
}
}
public class B : A
{ }
C#
public class A
{
public void Method1()
{
// Do something.
}
}
public class B : A
{
public override void Method1() // Generates CS0506.
{
// Do something else.
}
}
In some cases, a derived class must override the base class implementation. Base class
members marked with the abstract keyword require that derived classes override them.
Attempting to compile the following example generates compiler error CS0534, "
<class> does not implement inherited abstract member <member>", because class B
provides no implementation for A.Method1 .
C#
public abstract class A
{
public abstract void Method1();
}
Inheritance applies only to classes and interfaces. Other type categories (structs,
delegates, and enums) do not support inheritance. Because of these rules, attempting to
compile code like the following example produces compiler error CS0527: "Type
'ValueType' in interface list is not an interface." The error message indicates that,
although you can define the interfaces that a struct implements, inheritance is not
supported.
C#
Implicit inheritance
Besides any types that they may inherit from through single inheritance, all types in the
.NET type system implicitly inherit from Object or a type derived from it. The common
functionality of Object is available to any type.
To see what implicit inheritance means, let's define a new class, SimpleClass , that is
simply an empty class definition:
C#
You can then use reflection (which lets you inspect a type's metadata to get information
about that type) to get a list of the members that belong to the SimpleClass type.
Although you haven't defined any members in your SimpleClass class, output from the
example indicates that it actually has nine members. One of these members is a
parameterless (or default) constructor that is automatically supplied for the SimpleClass
type by the C# compiler. The remaining eight are members of Object, the type from
which all classes and interfaces in the .NET type system ultimately implicitly inherit.
C#
using System.Reflection;
Implicit inheritance from the Object class makes these methods available to the
SimpleClass class:
The public ToString method, which converts a SimpleClass object to its string
representation, returns the fully qualified type name. In this case, the ToString
method returns the string "SimpleClass".
Three methods that test for equality of two objects: the public instance
Equals(Object) method, the public static Equals(Object, Object) method, and the
The public GetHashCode method, which computes a value that allows an instance of
the type to be used in hashed collections.
The public GetType method, which returns a Type object that represents the
SimpleClass type.
Because of implicit inheritance, you can call any inherited member from a SimpleClass
object just as if it was actually a member defined in the SimpleClass class. For instance,
the following example calls the SimpleClass.ToString method, which SimpleClass
inherits from Object.
C#
The following table lists the categories of types that you can create in C# and the types
from which they implicitly inherit. Each base type makes a different set of members
available through inheritance to implicitly derived types.
ノ Expand table
class Object
7 Note
Note that "is a" also expresses the relationship between a type and a specific
instantiation of that type. In the following example, Automobile is a class that has three
unique read-only properties: Make , the manufacturer of the automobile; Model , the kind
of automobile; and Year , its year of manufacture. Your Automobile class also has a
constructor whose arguments are assigned to the property values, and it overrides the
Object.ToString method to produce a string that uniquely identifies the Automobile
instance rather than the Automobile class.
C#
if (model == null)
throw new ArgumentNullException(nameof(model), "The model cannot
be null.");
else if (string.IsNullOrWhiteSpace(model))
throw new ArgumentException("model cannot be an empty string or
have space characters only.");
Model = model;
In this case, you shouldn't rely on inheritance to represent specific car makes and
models. For example, you don't need to define a Packard type to represent automobiles
manufactured by the Packard Motor Car Company. Instead, you can represent them by
creating an Automobile object with the appropriate values passed to its class
constructor, as the following example does.
C#
using System;
An is-a relationship based on inheritance is best applied to a base class and to derived
classes that add additional members to the base class or that require additional
functionality not present in the base class.
What members to include in your base Publication class, and whether the
Publication members provide method implementations or whether Publication
is an abstract base class that serves as a template for its derived classes.
In this case, the Publication class will provide method implementations. The
Designing abstract base classes and their derived classes section contains an
example that uses an abstract base class to define the methods that derived
classes must override. Derived classes are free to provide any implementation that
is suitable for the derived type.
The ability to reuse code (that is, multiple derived classes share the declaration and
implementation of base class methods and do not need to override them) is an
advantage of non-abstract base classes. Therefore, you should add members to
Publication if their code is likely to be shared by some or most specialized
Publication types. If you fail to provide base class implementations efficiently,
Both to maximize code reuse and to create a logical and intuitive inheritance
hierarchy, you want to be sure that you include in the Publication class only the
data and functionality that is common to all or to most publications. Derived
classes then implement members that are unique to the particular kinds of
publication that they represent.
How far to extend your class hierarchy. Do you want to develop a hierarchy of
three or more classes, rather than simply a base class and one or more derived
classes? For example, Publication could be a base class of Periodical , which in
turn is a base class of Magazine , Journal and Newspaper .
For your example, you'll use the small hierarchy of a Publication class and a single
derived class, Book . You could easily extend the example to create a number of
additional classes that derive from Publication , such as Magazine and Article .
Whether it makes sense to instantiate the base class. If it does not, you should
apply the abstract keyword to the class. Otherwise, your Publication class can be
instantiated by calling its class constructor. If an attempt is made to instantiate a
class marked with the abstract keyword by a direct call to its class constructor, the
C# compiler generates error CS0144, "Cannot create an instance of the abstract
class or interface." If an attempt is made to instantiate the class by using reflection,
the reflection method throws a MemberAccessException.
By default, a base class can be instantiated by calling its class constructor. You do
not have to explicitly define a class constructor. If one is not present in the base
class' source code, the C# compiler automatically provides a default
(parameterless) constructor.
For your example, you'll mark the Publication class as abstract so that it cannot
be instantiated. An abstract class without any abstract methods indicates that
this class represents an abstract concept that is shared among several concrete
classes (like a Book , Journal ).
Whether derived classes must inherit the base class implementation of particular
members, whether they have the option to override the base class implementation,
or whether they must provide an implementation. You use the abstract keyword to
force derived classes to provide an implementation. You use the virtual keyword to
allow derived classes to override a base class method. By default, methods defined
in the base class are not overridable.
The Publication class does not have any abstract methods, but the class itself is
abstract .
Whether a derived class represents the final class in the inheritance hierarchy and
cannot itself be used as a base class for additional derived classes. By default, any
class can serve as a base class. You can apply the sealed keyword to indicate that a
class cannot serve as a base class for any additional classes. Attempting to derive
from a sealed class generated compiler error CS0509, "cannot derive from sealed
type <typeName>."
The following example shows the source code for the Publication class, as well as a
PublicationType enumeration that is returned by the Publication.PublicationType
property. In addition to the members that it inherits from Object, the Publication class
defines the following unique members and member overrides:
C#
if (string.IsNullOrWhiteSpace(title))
throw new ArgumentException("The title is required.");
Title = title;
Type = type;
}
A constructor
However, its instance constructor can be called directly from derived class
constructors, as the source code for the Book class shows.
Publication constructor.
Pages is a read-write Int32 property that indicates how many total pages the
publication has. The value is stored in a private field named totalPages . It must be
a positive number or an ArgumentOutOfRangeException is thrown.
Publisher-related members
Two read-only properties, Publisher and Type . The values are originally supplied
by the call to the Publication class constructor.
Publishing-related members
Two methods, Publish and GetPublicationDate , set and return the publication
date. The Publish method sets a private published flag to true when it is called
and assigns the date passed to it as an argument to the private datePublished
field. The GetPublicationDate method returns the string "NYP" if the published
flag is false , and the value of the datePublished field if it is true .
Copyright-related members
The Copyright method takes the name of the copyright holder and the year of the
copyright as arguments and assigns them to the CopyrightName and CopyrightDate
properties.
If a type does not override the Object.ToString method, it returns the fully qualified
name of the type, which is of little use in differentiating one instance from another.
The Publication class overrides Object.ToString to return the value of the Title
property.
The following figure illustrates the relationship between your base Publication class
and its implicitly inherited Object class.
C#
using System;
Author = author;
}
if (currency.Length != 3)
throw new ArgumentException("The ISO currency symbol is a 3-
character string.");
Currency = currency;
return oldValue;
}
In addition to the members that it inherits from Publication , the Book class defines the
following unique members and member overrides:
Two constructors
The two Book constructors share three common parameters. Two, title and
publisher, correspond to parameters of the Publication constructor. The third is
author, which is stored to a public immutable Author property. One constructor
includes an isbn parameter, which is stored in the ISBN auto-property.
The first constructor uses the this keyword to call the other constructor.
Constructor chaining is a common pattern in defining constructors. Constructors
with fewer parameters provide default values when calling the constructor with the
greatest number of parameters.
The second constructor uses the base keyword to pass the title and publisher
name to the base class constructor. If you don't make an explicit call to a base class
constructor in your source code, the C# compiler automatically supplies a call to
the base class' default or parameterless constructor.
A read-only ISBN property, which returns the Book object's International Standard
Book Number, a unique 10- or 13-digit number. The ISBN is supplied as an
argument to one of the Book constructors. The ISBN is stored in a private backing
field, which is auto-generated by the compiler.
Two read-only price-related properties, Price and Currency . Their values are
provided as arguments in a SetPrice method call. The Currency property is the
three-digit ISO currency symbol (for example, USD for the U.S. dollar). ISO currency
symbols can be retrieved from the ISOCurrencySymbol property. Both of these
properties are externally read-only, but both can be set by code in the Book class.
A SetPrice method, which sets the values of the Price and Currency properties.
Those values are returned by those same properties.
When you override the Object.Equals(Object) method, you must also override the
GetHashCode method, which returns a value that the runtime uses to store items
in hashed collections for efficient retrieval. The hash code should return a value
that's consistent with the test for equality. Since you've overridden
Object.Equals(Object) to return true if the ISBN properties of two Book objects are
equal, you return the hash code computed by calling the GetHashCode method of
the string returned by the ISBN property.
The following figure illustrates the relationship between the Book class and Publication ,
its base class.
You can now instantiate a Book object, invoke both its unique and inherited members,
and pass it as an argument to a method that expects a parameter of type Publication
or of type Book , as the following example shows.
C#
For example, each closed two-dimensional geometric shape includes two properties:
area, the inner extent of the shape; and perimeter, or the distance along the edges of
the shape. The way in which these properties are calculated, however, depends
completely on the specific shape. The formula for calculating the perimeter (or
circumference) of a circle, for example, is different from that of a square. The Shape class
is an abstract class with abstract methods. That indicates derived classes share the
same functionality, but those derived classes implement that functionality differently.
The following example defines an abstract base class named Shape that defines two
properties: Area and Perimeter . In addition to marking the class with the abstract
keyword, each instance member is also marked with the abstract keyword. In this case,
Shape also overrides the Object.ToString method to return the name of the type, rather
than its fully qualified name. And it defines two static members, GetArea and
GetPerimeter , that allow callers to easily retrieve the area and perimeter of an instance
of any derived class. When you pass an instance of a derived class to either of these
methods, the runtime calls the method override of the derived class.
C#
You can then derive some classes from Shape that represent specific shapes. The
following example defines three classes, Square , Rectangle , and Circle . Each uses a
formula unique for that particular shape to compute the area and perimeter. Some of
the derived classes also define properties, such as Rectangle.Diagonal and
Circle.Diameter , that are unique to the shape that they represent.
C#
using System;
The following example uses objects derived from Shape . It instantiates an array of
objects derived from Shape and calls the static methods of the Shape class, which wraps
return Shape property values. The runtime retrieves values from the overridden
properties of the derived types. The example also casts each Shape object in the array to
its derived type and, if the cast succeeds, retrieves properties of that particular subclass
of Shape .
C#
using System;
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to safely cast by using pattern
matching and the is and as operators
Article • 03/11/2022
Because objects are polymorphic, it's possible for a variable of a base class type to hold
a derived type. To access the derived type's instance members, it's necessary to cast the
value back to the derived type. However, a cast creates the risk of throwing an
InvalidCastException. C# provides pattern matching statements that perform a cast
conditionally only when it will succeed. C# also provides the is and as operators to test if
a value is of a certain type.
The following example shows how to use the pattern matching is statement:
C#
class Animal
{
public void Eat() { Console.WriteLine("Eating."); }
public override string ToString()
{
return "I am an animal.";
}
}
class Mammal : Animal { }
class Giraffe : Mammal { }
class SuperNova { }
The preceding sample demonstrates a few features of pattern matching syntax. The if
(a is Mammal m) statement combines the test with an initialization assignment. The
assignment occurs only when the test succeeds. The variable m is only in scope in the
embedded if statement where it has been assigned. You can't access m later in the
same method. The preceding example also shows how to use the as operator to convert
an object to a specified type.
You can also use the same syntax for testing if a nullable value type has a value, as
shown in the following example:
C#
int i = 5;
PatternMatchingNullable(i);
int? j = null;
PatternMatchingNullable(j);
double d = 9.78654;
PatternMatchingNullable(d);
PatternMatchingSwitch(i);
PatternMatchingSwitch(j);
PatternMatchingSwitch(d);
The preceding sample demonstrates other features of pattern matching to use with
conversions. You can test a variable for the null pattern by checking specifically for the
null value. When the runtime value of the variable is null , an is statement checking
for a type always returns false . The pattern matching is statement doesn't allow a
nullable value type, such as int? or Nullable<int> , but you can test for any other value
type. The is patterns from the preceding example aren't limited to the nullable value
types. You can also use those patterns to test if a variable of a reference type has a value
or it's null .
The preceding sample also shows how you use the type pattern in a switch statement
where the variable may be one of many different types.
If you want to test if a variable is a given type, but not assign it to a new variable, you
can use the is and as operators for reference types and nullable value types. The
following code shows how to use the is and as statements that were part of the C#
language before pattern matching was introduced to test if a variable is of a given type:
C#
double d = 9.78654;
UseAsWithNullable(d);
class SuperNova { }
As you can see by comparing this code with the pattern matching code, the pattern
matching syntax provides more robust features by combining the test and the
assignment in a single statement. Use the pattern matching syntax whenever possible.
Tutorial: Use pattern matching to build
type-driven and data-driven algorithms
Article • 02/15/2023
You can write functionality that behaves as though you extended types that may be in
other libraries. Another use for patterns is to create functionality your application
requires that isn't a fundamental feature of the type being extended.
Prerequisites
We recommend Visual Studio for Windows or Mac. You can download a free
version from the Visual Studio downloads page . Visual Studio includes the .NET
SDK.
You can also use the Visual Studio Code editor. You'll need to install the latest
.NET SDK separately.
If you prefer a different editor, you need to install the latest .NET SDK .
This tutorial assumes you're familiar with C# and .NET, including either Visual Studio or
the .NET CLI.
The classic object-oriented design would call for creating types in your application that
represent each data type from those multiple data sources. Then, your application
would work with those new types, build inheritance hierarchies, create virtual methods,
and implement abstractions. Those techniques work, and sometimes they're the best
tools. Other times you can write less code. You can write more clear code using
techniques that separate the data from the operations that manipulate that data.
In this tutorial, you'll create and explore an application that takes incoming data from
several external sources for a single scenario. You'll see how pattern matching provides
an efficient way to consume and process that data in ways that weren't part of the
original system.
Consider a major metropolitan area that is using tolls and peak time pricing to manage
traffic. You write an application that calculates tolls for a vehicle based on its type. Later
enhancements incorporate pricing based on the number of occupants in the vehicle.
Further enhancements add pricing based on the time and the day of the week.
From that brief description, you may have quickly sketched out an object hierarchy to
model this system. However, your data is coming from multiple sources like other
vehicle registration management systems. These systems provide different classes to
model that data and you don't have a single object model you can use. In this tutorial,
you'll use these simplified classes to model for the vehicle data from these external
systems, as shown in the following code:
C#
namespace ConsumerVehicleRegistration
{
public class Car
{
public int Passengers { get; set; }
}
}
namespace CommercialRegistration
{
public class DeliveryTruck
{
public int GrossWeightClass { get; set; }
}
}
namespace LiveryRegistration
{
public class Taxi
{
public int Fares { get; set; }
}
The objects you need to work with aren't in an object hierarchy that matches your
goals. You may be working with classes that are part of unrelated systems.
The functionality you're adding isn't part of the core abstraction for these classes.
The toll paid by a vehicle changes for different types of vehicles, but the toll isn't a
core function of the vehicle.
When the shape of the data and the operations on that data aren't described together,
the pattern matching features in C# make it easier to work with.
A Car is $2.00.
A Taxi is $3.50.
A Bus is $5.00.
A DeliveryTruck is $10.00
Create a new TollCalculator class, and implement pattern matching on the vehicle type
to get the toll amount. The following code shows the initial implementation of the
TollCalculator .
C#
using System;
using CommercialRegistration;
using ConsumerVehicleRegistration;
using LiveryRegistration;
namespace Calculators;
The preceding code uses a switch expression (not the same as a switch statement) that
tests the declaration pattern. A switch expression begins with the variable, vehicle in
the preceding code, followed by the switch keyword. Next comes all the switch arms
inside curly braces. The switch expression makes other refinements to the syntax that
surrounds the switch statement. The case keyword is omitted, and the result of each
arm is an expression. The last two arms show a new language feature. The { } case
matches any non-null object that didn't match an earlier arm. This arm catches any
incorrect types passed to this method. The { } case must follow the cases for each
vehicle type. If the order were reversed, the { } case would take precedence. Finally, the
null constant pattern detects when null is passed to this method. The null pattern
can be last because the other patterns match only a non-null object of the correct type.
You can test this code using the following code in Program.cs :
C#
using System;
using CommercialRegistration;
using ConsumerVehicleRegistration;
using LiveryRegistration;
using toll_calculator;
try
{
tollCalc.CalculateToll("this will fail");
}
catch (ArgumentException e)
{
Console.WriteLine("Caught an argument exception when using the wrong
type");
}
try
{
tollCalc.CalculateToll(null!);
}
catch (ArgumentNullException e)
{
Console.WriteLine("Caught an argument exception when using null");
}
That code is included in the starter project, but is commented out. Remove the
comments, and you can test what you've written.
You're starting to see how patterns can help you create algorithms where the code and
the data are separate. The switch expression tests the type and produces different
values based on the results. That's only the beginning.
These rules can be implemented using a property pattern in the same switch expression.
A property pattern compares a property value to a constant value. The property pattern
examines properties of the object once the type has been determined. The single case
for a Car expands to four different cases:
C#
vehicle switch
{
Car {Passengers: 0} => 2.00m + 0.50m,
Car {Passengers: 1} => 2.0m,
Car {Passengers: 2} => 2.0m - 0.50m,
Car => 2.00m - 1.0m,
// ...
};
The first three cases test the type as a Car , then check the value of the Passengers
property. If both match, that expression is evaluated and returned.
You would also expand the cases for taxis in a similar manner:
C#
vehicle switch
{
// ...
// ...
};
Next, implement the occupancy rules by expanding the cases for buses, as shown in the
following example:
C#
vehicle switch
{
// ...
// ...
};
The toll authority isn't concerned with the number of passengers in the delivery trucks.
Instead, they adjust the toll amount based on the weight class of the trucks as follows:
C#
vehicle switch
{
// ...
The preceding code shows the when clause of a switch arm. You use the when clause to
test conditions other than equality on a property. When you've finished, you'll have a
method that looks much like the following code:
C#
vehicle switch
{
Car {Passengers: 0} => 2.00m + 0.50m,
Car {Passengers: 1} => 2.0m,
Car {Passengers: 2} => 2.0m - 0.50m,
Car => 2.00m - 1.0m,
Many of these switch arms are examples of recursive patterns. For example, Car {
Passengers: 1} shows a constant pattern inside a property pattern.
You can make this code less repetitive by using nested switches. The Car and Taxi both
have four different arms in the preceding examples. In both cases, you can create a
declaration pattern that feeds into a constant pattern. This technique is shown in the
following code:
C#
In the preceding sample, using a recursive expression means you don't repeat the Car
and Taxi arms containing child arms that test the property value. This technique isn't
used for the Bus and DeliveryTruck arms because those arms are testing ranges for the
property, not discrete values.
C#
The preceding code does work correctly, but isn't readable. You have to chain through
all the input cases and the nested if statements to reason about the code. Instead,
you'll use pattern matching for this feature, but you'll integrate it with other techniques.
You could build a single pattern match expression that would account for all the
combinations of direction, day of the week, and time. The result would be a complicated
expression. It would be hard to read and difficult to understand. That makes it hard to
ensure correctness. Instead, combine those methods to build a tuple of values that
concisely describes all those states. Then use pattern matching to calculate a multiplier
for the toll. The tuple contains three discrete conditions:
The following table shows the combinations of input values and the peak pricing
multiplier:
ノ Expand table
There are 16 different combinations of the three variables. By combining some of the
conditions, you'll simplify the final switch expression.
The system that collects the tolls uses a DateTime structure for the time when the toll
was collected. Build member methods that create the variables from the preceding
table. The following function uses a pattern matching switch expression to express
whether a DateTime represents a weekend or a weekday:
C#
That method is correct, but it's repetitious. You can simplify it, as shown in the following
code:
C#
Next, add a similar function to categorize the time into the blocks:
C#
You add a private enum to convert each range of time to a discrete value. Then, the
GetTimeBand method uses relational patterns, and conjunctive or patterns. A relational
pattern lets you test a numeric value using < , > , <= , or >= . The or pattern tests if an
expression matches one or more patterns. You can also use an and pattern to ensure
that an expression matches two distinct patterns, and a not pattern to test that an
expression doesn't match a pattern.
After you create those methods, you can use another switch expression with the tuple
pattern to calculate the pricing premium. You could build a switch expression with all
16 arms:
C#
The above code works, but it can be simplified. All eight combinations for the weekend
have the same toll. You can replace all eight with the following line:
C#
Both inbound and outbound traffic have the same multiplier during the weekday
daytime and overnight hours. Those four switch arms can be replaced with the following
two lines:
C#
The code should look like the following code after those two changes:
C#
Finally, you can remove the two rush hour times that pay the regular price. Once you
remove those arms, you can replace the false with a discard ( _ ) in the final switch arm.
You'll have the following finished method:
C#
This example highlights one of the advantages of pattern matching: the pattern
branches are evaluated in order. If you rearrange them so that an earlier branch handles
one of your later cases, the compiler warns you about the unreachable code. Those
language rules made it easier to do the preceding simplifications with confidence that
the code didn't change.
Pattern matching makes some types of code more readable and offers an alternative to
object-oriented techniques when you can't add code to your classes. The cloud is
causing data and functionality to live apart. The shape of the data and the operations on
it aren't necessarily described together. In this tutorial, you consumed existing data in
entirely different ways from its original function. Pattern matching gave you the ability
to write functionality that overrode those types, even though you couldn't extend them.
Next steps
You can download the finished code from the dotnet/samples GitHub repository.
Explore patterns on your own and add this technique into your regular coding activities.
Learning these techniques gives you another way to approach problems and create new
functionality.
See also
Patterns
switch expression
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to handle an exception using
try/catch
Article • 04/22/2023
Example
In this example, IndexOutOfRangeException isn't the most appropriate exception:
ArgumentOutOfRangeException makes more sense for the method because the error is
caused by the index argument passed in by the caller.
C#
Comments
The code that causes an exception is enclosed in the try block. A catch statement is
added immediately after it to handle IndexOutOfRangeException , if it occurs. The catch
block handles the IndexOutOfRangeException and throws the more appropriate
ArgumentOutOfRangeException instead. In order to provide the caller with as much
The purpose of a finally statement is to ensure that the necessary cleanup of objects,
usually objects that are holding external resources, occurs immediately, even if an
exception is thrown. One example of such cleanup is calling Close on a FileStream
immediately after use instead of waiting for the object to be garbage collected by the
common language runtime, as follows:
C#
file = fileInfo.OpenWrite();
file.WriteByte(0xF);
file.Close();
}
Example
To turn the previous code into a try-catch-finally statement, the cleanup code is
separated from the working code, as follows.
C#
try
{
fileInfo = new FileInfo("./file.txt");
file = fileInfo.OpenWrite();
file.WriteByte(0xF);
}
catch (UnauthorizedAccessException e)
{
Console.WriteLine(e.Message);
}
finally
{
file?.Close();
}
}
Because an exception can occur at any time within the try block before the
OpenWrite() call, or the OpenWrite() call itself could fail, we aren't guaranteed that the
file is open when we try to close it. The finally block adds a check to make sure that
the FileStream object isn't null before you call the Close method. Without the null
check, the finally block could throw its own NullReferenceException, but throwing
exceptions in finally blocks should be avoided if it's possible.
A database connection is another good candidate for being closed in a finally block.
Because the number of connections allowed to a database server is sometimes limited,
you should close database connections as quickly as possible. If an exception is thrown
before you can close your connection, using the finally block is better than waiting for
garbage collection.
See also
using statement
Exception-handling statements
What's new in C# 13
Article • 07/30/2024
C# 13 includes the following new features. You can try these features using the latest
Visual Studio 2022 version or the .NET 9 Preview SDK .
params collections
New lock type and semantics.
New escape sequence - \e.
Method group natural type improvements
Implicit indexer access in object initializers
Enable ref locals and unsafe contexts in iterators and async methods
Enable ref struct types to implement interfaces.
Allow ref struct types as arguments for type parameters in generics.
You can download the latest .NET 9 preview SDK from the .NET downloads page . You
can also download Visual Studio 2022 - preview , which includes the .NET 9 Preview
SDK.
New features are added to the "What's new in C#" page when they're available in public
preview releases. The working set section of the roslyn feature status page tracks
when upcoming features are merged into the main branch.
You can find any breaking changes introduced in C# 13 in our article on breaking
changes.
7 Note
We're interested in your feedback on these features. If you find issues with any of
these new features, create a new issue in the dotnet/roslyn repository.
params collections
The params modifier isn't limited to array types. You can now use params with any
recognized collection type, including System.Span<T>, System.ReadOnlySpan<T>, and
types that implement System.Collections.Generic.IEnumerable<T> and have an Add
method. In addition to concrete types, the interfaces
System.Collections.Generic.IEnumerable<T>,
System.Collections.Generic.IReadOnlyCollection<T>,
System.Collections.Generic.IReadOnlyList<T>,
System.Collections.Generic.ICollection<T>, and System.Collections.Generic.IList<T> can
also be used.
When an interface type is used, the compiler synthesizes the storage for the arguments
supplied. You can learn more in the feature specification for params collections.
The C# lock statement recognizes if the target of the lock is a Lock object. If so, it uses
the updated API, rather than the traditional API using System.Threading.Monitor. The
compiler also recognizes if you convert a Lock object to another type and the Monitor
based code would be generated. You can read more in the feature specification for the
new lock object.
if the next characters following 1b were valid hexadecimal digits, those characters
became part of the escape sequence.
The new behavior is to prune the set of candidate methods at each scope, removing
those candidate methods that aren't applicable. Typically, the removed methods are
generic methods with the wrong arity, or constraints that aren't satisfied. The process
continues to the next outer scope only if no candidate methods are found. This process
more closely follows the general algorithm for overload resolution. If all candidate
methods found at a given scope don't match, the method group doesn't have a natural
type.
You can read the details of the changes in the proposal specification.
C#
The preceding example creates an array that counts down from 9 to 0. In versions
before C# 13, the ^ operator can't be used in an object initializer. You need to index the
elements from the front.
In C# 13, async methods can declare ref local variables, or local variables of a ref
struct type. However, those variables can't be accessed across an await boundary.
Neither can they be accessed across a yield return boundary.
This relaxed restriction enables the compiler to allow verifiably safe use of ref local
variables and ref struct types in more places. You can safely use types like
System.ReadOnlySpan<T> in these methods. The compiler tells you if you violate safety
rules.
In the same fashion, C# 13 allows unsafe contexts in iterator methods. However, all
yield return and yield break statements must be in safe contexts.
that type parameter can be a ref struct type. The compiler enforces ref safety rules on
all instances of that type parameter.
See also
What's new in .NET 9
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
What's new in C# 12
Article • 06/04/2024
C# 12 includes the following new features. You can try these features using the latest
Visual Studio 2022 version or the .NET 8 SDK .
ref readonly parameters - Introduced in Visual Studio 2022 version 17.8 Preview 2.
Alias any type - Introduced in Visual Studio 2022 version 17.6 Preview 3.
You can download the latest .NET 8 SDK from the .NET downloads page . You can also
download Visual Studio 2022 , which includes the .NET 8 SDK.
7 Note
We're interested in your feedback on these features. If you find issues with any of
these new features, create a new issue in the dotnet/roslyn repository.
Primary constructors
You can now create primary constructors in any class and struct . Primary constructors
are no longer restricted to record types. Primary constructor parameters are in scope
for the entire body of the class. To ensure that all primary constructor parameters are
definitely assigned, all explicitly declared constructors must call the primary constructor
using this() syntax. Adding a primary constructor to a class prevents the compiler
from declaring an implicit parameterless constructor. In a struct , the implicit
parameterless constructor initializes all fields, including primary constructor parameters
to the 0-bit pattern.
The compiler generates public properties for primary constructor parameters only in
record types, either record class or record struct types. Nonrecord classes and
structs might not always want this behavior for primary constructor parameters.
You can learn more about primary constructors in the tutorial for exploring primary
constructors and in the article on instance constructors.
Collection expressions
Collection expressions introduce a new terse syntax to create common collection values.
Inlining other collections into these values is possible using a spread element ..e .
Several collection-like types can be created without requiring external BCL support.
These types are:
C#
// Create an array:
int[] a = [1, 2, 3, 4, 5, 6, 7, 8];
// Create a list:
List<string> b = ["one", "two", "three"];
// Create a span
Span<char> c = ['a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'];
C#
The spread element evaluates each element of the enumerations expression. Each
element is included in the output collection.
You can use collection expressions anywhere you need a collection of elements. They
can specify the initial value for a collection or be passed as arguments to methods that
take collection types. You can learn more about collection expressions in the language
reference article on collection expressions or the feature specification.
The addition of ref readonly parameters enables more clarity for APIs that might be
using ref parameters or in parameters:
APIs created before in was introduced might use ref even though the argument
isn't modified. Those APIs can be updated with ref readonly . It won't be a
breaking change for callers, as would be if the ref parameter was changed to in .
An example is System.Runtime.InteropServices.Marshal.QueryInterface.
APIs that take an in parameter, but logically require a variable. A value expression
doesn't work. An example is System.ReadOnlySpan<T>.ReadOnlySpan<T>(T).
APIs that use ref because they require a variable, but don't mutate that variable.
An example is System.Runtime.CompilerServices.Unsafe.IsNullRef.
To learn more about ref readonly parameters, see the article on parameter modifiers in
the language reference, or the ref readonly parameters feature specification.
Default lambda parameters
You can now define default values for parameters on lambda expressions. The syntax
and rules are the same as adding default values for arguments to any method or local
function.
You can learn more about default parameters on lambda expressions in the article on
lambda expressions.
Inline arrays
Inline arrays are used by the runtime team and other library authors to improve
performance in your apps. Inline arrays enable a developer to create an array of fixed
size in a struct type. A struct with an inline buffer should provide performance
characteristics similar to an unsafe fixed size buffer. You likely won't declare your own
inline arrays, but you use them transparently when they're exposed as System.Span<T>
or System.ReadOnlySpan<T> objects from runtime APIs.
C#
[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer
{
private int _element0;
}
C#
The difference is that the compiler can take advantage of known information about an
inline array. You likely consume inline arrays as you would any other array. For more
information on how to declare inline arrays, see the language reference on struct types.
Experimental attribute
Types, methods, or assemblies can be marked with the
System.Diagnostics.CodeAnalysis.ExperimentalAttribute to indicate an experimental
feature. The compiler issues a warning if you access a method or type annotated with
the ExperimentalAttribute. All types included in an assembly marked with the
Experimental attribute are experimental. You can read more in the article on General
Interceptors
2 Warning
Interceptors are an experimental feature, available in preview mode with C# 12. The
feature may be subject to breaking changes or removal in a future release.
Therefore, it is not recommended for production or released applications.
In order to use interceptors, the user project must specify the property
<InterceptorsPreviewNamespaces> . This is a list of namespaces which are allowed to
contain interceptors.
For example:
<InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.AspN
etCore.Http.Generated;MyLibrary.Generated</InterceptorsPreviewNamespaces>
If you're interested in experimenting with interceptors, you can learn more by reading
the feature specification . If you use the feature, make sure to stay current with any
changes in the feature specification for this experimental feature. If the feature is
finalized, we'll add more guidance on this site.
See also
What's new in .NET 8
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
What's new in C# 11
Article • 03/15/2024
You can download the latest .NET 7 SDK from the .NET downloads page . You can also
download Visual Studio 2022 , which includes the .NET 7 SDK.
7 Note
We're interested in your feedback on these features. If you find issues with any of
these new features, create a new issue in the dotnet/roslyn repository.
Generic attributes
You can declare a generic class whose base class is System.Attribute. This feature
provides a more convenient syntax for attributes that require a System.Type parameter.
Previously, you'd need to create an attribute that takes a Type as its constructor
parameter:
C#
// Before C# 11:
public class TypeAttribute : Attribute
{
public TypeAttribute(Type t) => ParamType = t;
C#
[TypeAttribute(typeof(string))]
public string Method() => default;
Using this new feature, you can create a generic attribute instead:
C#
// C# 11 feature:
public class GenericAttribute<T> : Attribute { }
C#
[GenericAttribute<string>()]
public string Method() => default;
You must supply all type parameters when you apply the attribute. In other words, the
generic type must be fully constructed. In the example above, the empty parentheses ( (
and ) ) can be omitted as the attribute does not have any arguments.
C#
The type arguments must satisfy the same restrictions as the typeof operator. Types that
require metadata annotations aren't allowed. For example, the following types aren't
allowed as the type parameter:
dynamic
string? (or any nullable reference type)
(int X, int Y) (or any other tuple types using C# tuple syntax).
These types aren't directly represented in metadata. They include annotations that
describe the type. In all cases, you can use the underlying type instead:
You can add static abstract or static virtual members in interfaces to define
interfaces that include overloadable operators, other static members, and static
properties. The primary scenario for this feature is to use mathematical operators in
generic types. For example, you can implement the System.IAdditionOperators<TSelf,
TOther, TResult> interface in a type that implements operator + . Other interfaces
define other mathematical operations or well-defined values. You can learn about the
new syntax in the article on interfaces. Interfaces that include static virtual methods
are typically generic interfaces. Furthermore, most will declare a constraint that the type
parameter implements the declared interface.
You can learn more and try the feature yourself in the tutorial Explore static abstract
interface members, or the Preview features in .NET 6 – generic math blog post.
unsigned right shift operator: Before C# 11, to force an unsigned right-shift, you
would need to cast any signed integer type to an unsigned type, perform the shift,
then cast the result back to a signed type. Beginning in C# 11, you can use the
>>> , the unsigned shift operator.
You can learn more about the newlines feature in the string interpolations article in the
language reference.
List patterns
List patterns extend pattern matching to match sequences of elements in a list or an
array. For example, sequence is [1, 2, 3] is true when the sequence is an array or a
list of three integers (1, 2, and 3). You can match elements using any pattern, including
constant, type, property and relational patterns. The discard pattern ( _ ) matches any
single element, and the new range pattern ( .. ) matches any sequence of zero or more
elements.
You can learn more details about list patterns in the pattern matching article in the
language reference.
C#
Any whitespace to the left of the closing double quotes will be removed from the string
literal. Raw string literals can be combined with string interpolation to include braces in
the output text. Multiple $ characters denote how many consecutive braces start and
end the interpolation:
C#
The preceding example specifies that two braces start and end an interpolation. The
third repeated opening and closing brace are included in the output string.
You can learn more about raw string literals in the article on strings in the programming
guide, and the language reference articles on string literals and interpolated strings.
Auto-default struct
The C# 11 compiler ensures that all fields of a struct type are initialized to their default
value as part of executing a constructor. This change means any field or auto property
not initialized by a constructor is automatically initialized by the compiler. Structs where
the constructor doesn't definitely assign all fields now compile, and any fields not
explicitly initialized are set to their default value. You can read more about how this
change affects struct initialization in the article on structs.
You can learn more about UTF-8 string literals in the string literal section of the article
on builtin reference types.
Required members
You can add the required modifier to properties and fields to enforce constructors and
callers to initialize those values. The
System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute can be added to
constructors to inform the compiler that a constructor initializes all required members.
For more information on required members, See the init-only section of the properties
article.
You can add the scoped modifier to any ref declaration. This limits the scope where the
reference can escape to.
See also
What's new in .NET 7
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
What's new in C# 10
Article • 02/21/2023
Record structs
Improvements of structure types
Interpolated string handlers
global using directives
File-scoped namespace declaration
Extended property patterns
Improvements on lambda expressions
Allow const interpolated strings
Record types can seal ToString()
Improved definite assignment
Allow both assignment and declaration in the same deconstruction
Allow AsyncMethodBuilder attribute on methods
CallerArgumentExpression attribute
Enhanced #line pragma
Warning wave 6
You can download the latest .NET 6 SDK from the .NET downloads page . You can also
download Visual Studio 2022 , which includes the .NET 6 SDK.
7 Note
We're interested in your feedback on these features. If you find issues with any of
these new features, create a new issue in the dotnet/roslyn repository.
Record structs
You can declare value type records using the record struct or readonly record struct
declarations. You can now clarify that a record is a reference type with the record class
declaration.
C#
namespace MyNamespace;
This new syntax saves both horizontal and vertical space for namespace declarations.
C#
{ Prop1.Prop2: pattern }
is valid in C# 10 and later and equivalent to
C#
For more information, see the Extended property patterns feature proposal note. For
more information about a property pattern, see the Property pattern section of the
Patterns article.
Lambda expressions may have a natural type, where the compiler can infer a
delegate type from the lambda expression or method group.
Lambda expressions may declare a return type when the compiler can't infer it.
Attributes can be applied to lambda expressions.
These features make lambda expressions more similar to methods and local functions.
They make it easier to use lambda expressions without declaring a variable of a delegate
type, and they work more seamlessly with the new ASP.NET Core Minimal APIs.
C#
// Initialization:
(int x, int y) = point;
// assignment:
int x1 = 0;
int y1 = 0;
(x1, y1) = point;
C#
int x = 0;
(x, int y) = point;
C#
// Or, using ?.
if (c?.GetDependentValue(out object obj) == true)
{
representation = obj.ToString(); // undesired error
}
// Or, using ??
if (c?.GetDependentValue(out object obj) ?? false)
{
representation = obj.ToString(); // undesired error
}
The main impact of this improvement is that the warnings for definite assignment and
null-state analysis are more accurate.
To learn more, see the section on AsyncMethodBuilder in the article on attributes read
by the compiler.
C#
You can learn more about this feature in the article on Caller information attributes in
the language reference section.
Enhanced #line pragma
C# 10 supports a new format for the #line pragma. You likely won't use the new format,
but you'll see its effects. The enhancements enable more fine-grained output in domain-
specific languages (DSLs) like Razor. The Razor engine uses these enhancements to
improve the debugging experience. You'll find debuggers can highlight your Razor
source more accurately. To learn more about the new syntax, see the article on
Preprocessor directives in the language reference. You can also read the feature
specification for Razor based examples.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Learn about any breaking changes in
the C# compiler
Article • 11/18/2022
The Roslyn team maintains a list of breaking changes in the C# and Visual Basic
compilers. You can find information on those changes at these links on their GitHub
repository:
This article provides a history of each major release of the C# language. The C# team is
continuing to innovate and add new features. Detailed language feature status,
including features considered for upcoming releases can be found on the dotnet/roslyn
repository on GitHub.
) Important
The C# language relies on types and methods in what the C# specification defines
as a standard library for some of the features. The .NET platform delivers those
types and methods in a number of packages. One example is exception processing.
Every throw statement or expression is checked to ensure the object being thrown
is derived from Exception. Similarly, every catch is checked to ensure that the type
being caught is derived from Exception. Each version may add new requirements.
To use the latest language features in older environments, you may need to install
specific libraries. These dependencies are documented in the page for each specific
version. You can learn more about the relationships between language and library
for background on this dependency.
C# version 12
Released November 2023
Primary constructors - You can create primary constructors in any class or struct
type.
Collection expressions - A new syntax to specify collection expressions, including
the spread element, ( ..e ), to expand any collection.
Inline arrays - Inline arrays enable you to create an array of fixed size in a struct
type.
Optional parameters in lambda expressions - You can define default values for
parameters on lambda expressions.
ref readonly parameters - ref readonly parameters enables more clarity for APIs
that might be using ref parameters or in parameters.
Alias any type - You can use the using alias directive to alias any type, not just
named types.
Experimental attribute - Indicate an experimental feature.
Overall, C# 12 provides new features that make you more productive writing C# code.
Syntax you already knew is available in more places. Other syntax enables consistency
for related concepts.
C# version 11
Released November 2022
C# 11 introduces generic math and several features that support that goal. You can write
numeric algorithms once for all number types. There's more features to make working
with struct types easier, like required members and auto-default structs. Working with
strings gets easier with Raw string literals, newline in string interpolations, and UTF-8
string literals. Features like file local types enable source generators to be simpler.
Finally, list patterns add more support for pattern matching.
C# version 10
Released November 2021
More features were available in preview mode. In order to use these features, you must
set <LangVersion> to Preview in your project:
Many of the features mean you type less code to express the same concepts. Record
structs synthesize many of the same methods that record classes do. Structs and
anonymous types support with expressions. Global using directives and file scoped
namespace declarations mean you express dependencies and namespace organization
more clearly. Lambda improvements make it easier to declare lambda expressions where
they're used. New property patterns and deconstruction improvements create more
concise code.
The new interpolated string handlers and AsyncMethodBuilder behavior can improve
performance. These language features were applied in the .NET Runtime to achieve
performance improvements in .NET 6.
C# 10 also marks more of a shift to the yearly cadence for .NET releases. Because not
every feature can be completed in a yearly timeframe, you can try a couple of "preview"
features in C# 10. Both generic attributes and static abstract members in interfaces can be
used, but these preview features might change before their final release.
C# version 9
Released November 2020
C# 9 was released with .NET 5. It's the default language version for any assembly that
targets the .NET 5 release. It contains the following new and enhanced features:
Records
Init only setters
Top-level statements
Pattern matching enhancements: relational patterns and logical patterns
Performance and interop
Native sized integers
Function pointers
Suppress emitting localsinit flag
Module initializers
New features for partial methods
Fit and finish features
Target-typed new expressions
static anonymous functions
Target-typed conditional expressions
Covariant return types
Extension GetEnumerator support for foreach loops
Lambda discard parameters
Attributes on local functions
Top level statements means your main program is simpler to read. There's less need for
ceremony: a namespace, a Program class, and static void Main() are all unnecessary.
The introduction of records provides a concise syntax for reference types that follow
value semantics for equality. You use these types to define data containers that typically
define minimal behavior. Init-only setters provide the capability for nondestructive
mutation ( with expressions) in records. C# 9 also adds covariant return types so that
derived records can override virtual methods and return a type derived from the base
method's return type.
The pattern matching capabilities expanded in several ways. Numeric types now support
range patterns. Patterns can be combined using and , or , and not patterns. Parentheses
can be added to clarify more complex patterns:
These patterns enrich the syntax for patterns. Consider these examples:
C#
With optional parentheses to make it clear that and has higher precedence than or :
C#
One of the most common uses is a new syntax for a null check:
C#
if (e is not null)
{
// ...
}
Any of these patterns can be used in any context where patterns are allowed: is pattern
expressions, switch expressions, nested patterns, and the pattern of a switch
statement's case label.
The nint and nuint types model the native-size integer types on the target CPU.
Function pointers provide delegate-like functionality while avoiding the allocations
necessary to create a delegate object.
The localsinit instruction can be omitted to save instructions.
Performance and interop
Another set of improvements supports scenarios where code generators add
functionality:
Module initializers are methods that the runtime calls when an assembly loads.
Partial methods support new accessibly modifiers and non-void return types. In
those cases, an implementation must be provided.
C# version 8.0
Released September 2019
C# 8.0 is the first major C# release that specifically targets .NET Core. Some features rely
on new Common Language Runtime (CLR) capabilities, others on library types added
only in .NET Core. C# 8.0 adds the following features and enhancements to the C#
language:
Readonly members
Default interface methods
Pattern matching enhancements:
Switch expressions
Property patterns
Tuple patterns
Positional patterns
Using declarations
Static local functions
Disposable ref structs
Nullable reference types
Asynchronous streams
Indices and ranges
Null-coalescing assignment
Unmanaged constructed types
Stackalloc in nested expressions
Enhancement of interpolated verbatim strings
Default interface members require enhancements in the CLR. Those features were added
in the CLR for .NET Core 3.0. Ranges and indexes, and asynchronous streams require
new types in the .NET Core 3.0 libraries. Nullable reference types, while implemented in
the compiler, is much more useful when libraries are annotated to provide semantic
information regarding the null state of arguments and return values. Those annotations
are being added in the .NET Core libraries.
C# version 7.3
Released May 2018
There are two main themes to the C# 7.3 release. One theme provides features that
enable safe code to be as performant as unsafe code. The second theme provides
incremental improvements to existing features. New compiler options were also added
in this release.
The following new features support the theme of better performance for safe code:
C# version 7.2
Released November 2017
C# version 7.1
Released August 2017
C# started releasing point releases with C# 7.1. This version added the language version
selection configuration element, three new language features, and new compiler
behavior.
Finally, the compiler has two options -refout and -refonly that control reference
assembly generation.
C# version 7.0
Released March 2017
C# version 7.0 was released with Visual Studio 2017. This version has some evolutionary
and cool stuff in the vein of C# 6.0. Here are some of the new features:
Out variables
Tuples and deconstruction
Pattern matching
Local functions
Expanded expression bodied members
Ref locals
Ref returns
Discards
Binary Literals and Digit Separators
Throw expressions
All of these features offer new capabilities for developers and the opportunity to write
cleaner code than ever. A highlight is condensing the declaration of variables to use
with the out keyword and by allowing multiple return values via tuple. .NET Core now
targets any operating system and has its eyes firmly on the cloud and on portability.
These new capabilities certainly occupy the language designers' thoughts and time, in
addition to coming up with new features.
C# version 6.0
Released July 2015
Version 6.0, released with Visual Studio 2015, released many smaller features that made
C# programming more productive. Here are some of them:
Static imports
Exception filters
Auto-property initializers
Expression bodied members
Null propagator
String interpolation
nameof operator
Index initializers
Await in catch/finally blocks
Default values for getter-only properties
If you look at these features together, you see an interesting pattern. In this version, C#
started to eliminate language boilerplate to make code more terse and readable. So for
fans of clean, simple code, this language version was a huge win.
They did one other thing along with this version, though it's not a traditional language
feature in itself. They released Roslyn the compiler as a service . The C# compiler is
now written in C#, and you can use the compiler as part of your programming efforts.
C# version 5.0
Released August 2012
C# version 5.0, released with Visual Studio 2012, was a focused version of the language.
Nearly all of the effort for that version went into another groundbreaking language
concept: the async and await model for asynchronous programming. Here's the major
features list:
Asynchronous members
Caller info attributes
Code Project: Caller Info Attributes in C# 5.0
The caller info attribute lets you easily retrieve information about the context in which
you're running without resorting to a ton of boilerplate reflection code. It has many uses
in diagnostics and logging tasks.
But async and await are the real stars of this release. When these features came out in
2012, C# changed the game again by baking asynchrony into the language as a first-
class participant.
C# version 4.0
Released April 2010
C# version 4.0, released with Visual Studio 2010, introduced some interesting new
features:
Dynamic binding
Named/optional arguments
Generic covariant and contravariant
Embedded interop types
Embedded interop types eased the deployment pain of creating COM interop
assemblies for your application. Generic covariance and contravariance give you more
power to use generics, but they're a bit academic and probably most appreciated by
framework and library authors. Named and optional parameters let you eliminate many
method overloads and provide convenience. But none of those features are exactly
paradigm altering.
The major feature was the introduction of the dynamic keyword. The dynamic keyword
introduced into C# version 4.0 the ability to override the compiler on compile-time
typing. By using the dynamic keyword, you can create constructs similar to dynamically
typed languages like JavaScript. You can create a dynamic x = "a string" and then add
six to it, leaving it up to the runtime to sort out what should happen next.
Dynamic binding gives you the potential for errors but also great power within the
language.
C# version 3.0
Released November 2007
C# version 3.0 came in late 2007, along with Visual Studio 2008, though the full boat of
language features would actually come with .NET Framework version 3.5. This version
marked a major change in the growth of C#. It established C# as a truly formidable
programming language. Let's take a look at some major features in this version:
Auto-implemented properties
Anonymous types
Query expressions
Lambda expressions
Expression trees
Extension methods
Implicitly typed local variables
Partial methods
Object and collection initializers
In retrospect, many of these features seem both inevitable and inseparable. They all fit
together strategically. This C# version's killer feature was the query expression, also
known as Language-Integrated Query (LINQ).
A more nuanced view examines expression trees, lambda expressions, and anonymous
types as the foundation upon which LINQ is constructed. But, in either case, C# 3.0
presented a revolutionary concept. C# 3.0 began to lay the groundwork for turning C#
into a hybrid Object-Oriented / Functional language.
Specifically, you could now write SQL-style, declarative queries to perform operations on
collections, among other things. Instead of writing a for loop to compute the average
of a list of integers, you could now do that as simply as list.Average() . The
combination of query expressions and extension methods made a list of integers a
whole lot smarter.
C# version 2.0
Released November 2005
Let's take a look at some major features of C# 2.0, released in 2005, along with Visual
Studio 2005:
Generics
Partial types
Anonymous methods
Nullable value types
Iterators
Covariance and contravariance
iterate through them. Using generics is better than creating a ListInt type that derives
from ArrayList or casting from Object for every operation.
C# version 2.0 brought iterators. To put it succinctly, iterators let you examine all the
items in a List (or other Enumerable types) with a foreach loop. Having iterators as a
first-class part of the language dramatically enhanced readability of the language and
people's ability to reason about the code.
C# version 1.2
Released April 2003
C# version 1.2 shipped with Visual Studio .NET 2003. It contained a few small
enhancements to the language. Most notable is that starting with this version, the code
generated in a foreach loop called Dispose on an IEnumerator when that IEnumerator
implemented IDisposable.
C# version 1.0
Released January 2002
When you go back and look, C# version 1.0, released with Visual Studio .NET 2002,
looked a lot like Java. As part of its stated design goals for ECMA , it sought to be a
"simple, modern, general-purpose object-oriented language." At the time, looking like
Java meant it achieved those early design goals.
But if you look back on C# 1.0 now, you'd find yourself a little dizzy. It lacked the built-in
async capabilities and some of the slick functionality around generics you take for
granted. As a matter of fact, it lacked generics altogether. And LINQ? Not available yet.
Those additions would take some years to come out.
C# version 1.0 looked stripped of features, compared to today. You'd find yourself
writing some verbose code. But yet, you have to start somewhere. C# version 1.0 was a
viable alternative to Java on the Windows platform.
Classes
Structs
Interfaces
Events
Properties
Delegates
Operators and expressions
Statements
Attributes
Article originally published on the NDepend blog , courtesy of Erik Dietrich and Patrick
Smacchia.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Relationships between language
features and library types
Article • 10/26/2023
The C# language definition requires a standard library to have certain types and certain
accessible members on those types. The compiler generates code that uses these
required types and members for many different language features. For this reason, C#
versions are supported only for the corresponding .NET version and newer. That ensures
the correct run-time behavior and the availability of all required types and members.
This dependency on standard library functionality has been part of the C# language
since its first version. In that version, examples included:
That first version was simple: the compiler and the standard library shipped together,
and there was only one version of each.
The language design team works to minimize the surface area of the types and
members required in a compliant standard library. That goal is balanced against a clean
design where new library features are incorporated seamlessly into the language. There
will be new features in future versions of C# that require new types and members in a
standard library. C# compiler tools are now decoupled from the release cycle of the .NET
libraries on supported platforms.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
issues and pull requests. For Open a documentation issue
more information, see our
contributor guide. Provide product feedback
Version and update considerations for
C# developers
Article • 06/29/2023
The language version used to compile your app typically matches the runtime target
framework moniker (TFM) referenced in your project. For more information on changing
the default language version, see the article titled configure your language version. This
default behavior ensures maximum compatibility.
When a binary breaking change affects your app, you must recompile your app, but you
don't need to edit any source code. When a source breaking change affects your app,
the existing binary still runs correctly in environments with the updated runtime and
libraries. However, you must make source changes to recompile with the new language
version and runtime. If a change is both source breaking and binary breaking, you must
recompile your application with the latest version and make source updates.
Because of the goal to avoid breaking changes by the C# language team and runtime
team, updating your application is typically a matter of updating the TFM and rebuilding
the app. However, for libraries that are distributed publicly, you should carefully evaluate
your policy for supported TFMs and supported language versions. You may be creating
a new library with features found in the latest version and need to ensure apps built
using previous versions of the compiler can use it. Or you may be upgrading an existing
library and many of your users might not have upgraded versions yet.
Introducing breaking changes in your libraries
When you adopt new language features in your library's public API, you should evaluate
if adopting the feature introduces either a binary or source breaking change for the
users of your library. Any changes to your internal implementation that don't appear in
the public or protected interfaces are compatible.
7 Note
A binary breaking change requires your users to recompile their code in order to use the
new version. For example, consider this public method:
C#
If you add the in modifier to the method, that's a binary breaking change:
C#
Users must recompile any application that uses the CalculateSquare method for the
new library to work correctly.
A source breaking change requires your users to change their code before they
recompile. For example, consider this type:
C#
C#
The previous change requires changes for any type derived from Person . All those
declarations must add the record modifier to their declarations.
When you make a source breaking change to your library, you require all projects to
make source changes in order to use your new library. If the necessary change requires
new language features, you force those projects to upgrade to the same language
version and TFM you're now using. You've required more work for your users, and
possibly forced them to upgrade as well.
The impact of any breaking change you make depends on the number of projects that
have a dependency on your library. If your library is used internally by a few
applications, you can react to any breaking changes in all impacted projects. However, if
your library is publicly downloaded, you should evaluate the potential impact and
consider alternatives:
Prerequisites
You need to set up your machine to run .NET 8 or later, including the C# 12 or later
compiler. The C# 12 compiler is available starting with Visual Studio 2022 version 17.7
or the .NET 8 SDK .
Primary constructors
You can add parameters to a struct or class declaration to create a primary
constructor. Primary constructor parameters are in scope throughout the class definition.
It's important to view primary constructor parameters as parameters even though they
are in scope throughout the class definition. Several rules clarify that they're parameters:
These rules are the same as parameters to any method, including other constructor
declarations.
Initialize property
The following code initializes two readonly properties that are computed from primary
constructor parameters:
C#
C#
The new feature makes it easier to use field initializers when you need arguments to
initialize a field or property.
C#
In the preceding example, the Translate method changes the dx and dy components.
That requires the Magnitude and Direction properties be computed when accessed. The
=> operator designates an expression-bodied get accessor, whereas the = operator
designates an initializer. This version adds a parameterless constructor to the struct. The
parameterless constructor must invoke the primary constructor, so that all the primary
constructor parameters are initialized.
In the previous example, the primary constructor properties are accessed in a method.
Therefore the compiler creates hidden fields to represent each parameter. The following
code shows approximately what the compiler generates. The actual field names are valid
CIL identifiers, but not valid C# identifiers.
C#
It's important to understand that the first example didn't require the compiler to create
a field to store the value of the primary constructor parameters. The second example
used the primary constructor parameter inside a method, and therefore required the
compiler to create storage for them. The compiler creates storage for any primary
constructors only when that parameter is accessed in the body of a member of your
type. Otherwise, the primary constructor parameters aren't stored in the object.
Dependency injection
Another common use for primary constructors is to specify parameters for dependency
injection. The following code creates a simple controller that requires a service interface
for its use:
C#
The primary constructor clearly indicates the parameters needed in the class. You use
the primary constructor parameters as you would any other variable in the class.
C#
All bank accounts, regardless of the type, have properties for the account number and
an owner. In the completed application, other common functionality would be added to
the base class.
Many types require more specific validation on constructor parameters. For example, the
BankAccount has specific requirements for the owner and accountID parameters: The
owner must not be null or whitespace, and the accountID must be a string containing
10 digits. You can add this validation when you assign the corresponding properties:
C#
The previous example shows how you can validate the constructor parameters before
assigning them to the properties. You can use builtin methods, like
String.IsNullOrWhiteSpace(String), or your own validation method, like
ValidAccountNumber . In the previous example, any exceptions are thrown from the
constructor, when it invokes the initializers. If a constructor parameter isn't used to
assign a field, any exceptions are thrown when the constructor parameter is first
accessed.
C#
The derived CheckingAccount class has a primary constructor that takes all the
parameters needed in the base class, and another parameter with a default value. The
primary constructor calls the base constructor using the : BankAccount(accountID,
owner) syntax. This expression specifies both the type for the base class, and the
Your derived class isn't required to use a primary constructor. You can create a
constructor in the derived class that invokes the base class' primary constructor, as
shown in the following example:
C#
There's one potential concern with class hierarchies and primary constructors: it's
possible to create multiple copies of a primary constructor parameter as it's used in
both derived and base classes. The following code example creates two copies each of
the owner and accountID field:
C#
public class SavingsAccount(string accountID, string owner, decimal
interestRate) : BankAccount(accountID, owner)
{
public SavingsAccount() : this("default", "default", 0.01m) { }
public decimal CurrentBalance { get; private set; } = 0;
The highlighted line shows that the ToString method uses the primary constructor
parameters ( owner and accountID ) rather than the base class properties ( Owner and
AccountID ). The result is that the derived class, SavingsAccount creates storage for those
copies. The copy in the derived class is different than the property in the base class. If
the base class property could be modified, the instance of the derived class won't see
that modification. The compiler issues a warning for primary constructor parameters
that are used in a derived class and passed to a base class constructor. In this instance,
the fix is to use the properties of the base class.
Summary
You can use the primary constructors as best suits your design. For classes and structs,
primary constructor parameters are parameters to a constructor that must be invoked.
You can use them to initialize properties. You can initialize fields. Those properties or
fields can be immutable, or mutable. You can use them in methods. They're parameters,
and you use them in what manner suits your design best. You can learn more about
primary constructors in the C# programming guide article on instance constructors and
the proposed primary constructor specification.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Tutorial: Explore C# 11 feature - static
virtual members in interfaces
Article • 08/03/2022
C# 11 and .NET 7 include static virtual members in interfaces. This feature enables you to
define interfaces that include overloaded operators or other static members. Once
you've defined interfaces with static members, you can use those interfaces as
constraints to create generic types that use operators or other static methods. Even if
you don't create interfaces with overloaded operators, you'll likely benefit from this
feature and the generic math classes enabled by the language update.
Prerequisites
You'll need to set up your machine to run .NET 7, which supports C# 11. The C# 11
compiler is available starting with Visual Studio 2022, version 17.3 or the .NET 7
SDK .
C#
The same logic would work for any numeric type: int , short , long , float decimal , or
any type that represents a number. You need to have a way to use the + and /
operators, and to define a value for 2 . You can use the
System.Numerics.INumber<TSelf> interface to write the preceding method as the
following generic method:
C#
Any type that implements the INumber<TSelf> interface must include a definition for
operator + , and for operator / . The denominator is defined by T.CreateChecked(2) to
create the value 2 for any numeric type, which forces the denominator to be the same
type as the two parameters. INumberBase<TSelf>.CreateChecked<TOther>(TOther)
creates an instance of the type from the specified value and throws an
OverflowException if the value falls outside the representable range. (This
implementation has the potential for overflow if left and right are both large enough
values. There are alternative algorithms that can avoid this potential issue.)
You define static abstract members in an interface using familiar syntax: You add the
static and abstract modifiers to any static member that doesn't provide an
implementation. The following example defines an IGetNext<T> interface that can be
applied to any type that overrides operator ++ :
C#
The constraint that the type argument, T , implements IGetNext<T> ensures that the
signature for the operator includes the containing type, or its type argument. Many
operators enforce that its parameters must match the type, or be the type parameter
constrained to implement the containing type. Without this constraint, the ++ operator
couldn't be defined in the IGetNext<T> interface.
You can create a structure that creates a string of 'A' characters where each increment
adds another character to the string using the following code:
C#
public RepeatSequence() {}
public static RepeatSequence operator ++(RepeatSequence other)
=> other with { Text = other.Text + Ch };
More generally, you can build any algorithm where you might want to define ++ to
mean "produce the next value of this type". Using this interface produces clear code and
results:
C#
PowerShell
A
AA
AAA
AAAA
AAAAA
AAAAAA
AAAAAAA
AAAAAAAA
AAAAAAAAA
AAAAAAAAAA
This small example demonstrates the motivation for this feature. You can use natural
syntax for operators, constant values, and other static operations. You can explore these
techniques when you create multiple types that rely on static members, including
overloaded operators. Define the interfaces that match your types' capabilities and then
declare those types' support for the new interface.
Generic math
The motivating scenario for allowing static methods, including operators, in interfaces is
to support generic math algorithms. The .NET 7 base class library contains interface
definitions for many arithmetic operators, and derived interfaces that combine many
arithmetic operators in an INumber<T> interface. Let's apply those types to build a
Point<T> record that can use any numeric type for T . The point can be moved by some
XOffset and YOffset using the + operator.
Start by creating a new Console application, either by using dotnet new or Visual Studio.
The public interface for the Translation<T> and Point<T> should look like the following
code:
C#
You use the record type for both the Translation<T> and Point<T> types: Both store
two values, and they represent data storage rather than sophisticated behavior. The
implementation of operator + would look like the following code:
C#
For the previous code to compile, you'll need to declare that T supports the
IAdditionOperators<TSelf, TOther, TResult> interface. That interface includes the
operator + static method. It declares three type parameters: One for the left operand,
one for the right operand, and one for the result. Some types implement + for different
operand and result types. Add a declaration that the type argument, T implements
IAdditionOperators<T, T, T> :
C#
After you add that constraint, your Point<T> class can use the + for its addition
operator. Add the same constraint on the Translation<T> declaration:
C#
public record Translation<T>(T XOffset, T YOffset) where T :
IAdditionOperators<T, T, T>;
The IAdditionOperators<T, T, T> constraint prevents a developer using your class from
creating a Translation using a type that doesn't meet the constraint for the addition to
a point. You've added the necessary constraints to the type parameter for
Translation<T> and Point<T> so this code works. You can test by adding code like the
following above the declarations of Translation and Point in your Program.cs file:
C#
Console.WriteLine(pt);
Console.WriteLine(translate);
Console.WriteLine(final);
You can make this code more reusable by declaring that these types implement the
appropriate arithmetic interfaces. The first change to make is to declare that Point<T,
T> implements the IAdditionOperators<Point<T>, Translation<T>, Point<T>> interface.
The Point type makes use of different types for operands and the result. The Point
type already implements an operator + with that signature, so adding the interface to
the declaration is all you need:
C#
Finally, when you're performing addition, it's useful to have a property that defines the
additive identity value for that type. There's a new interface for that feature:
IAdditiveIdentity<TSelf,TResult>. A translation of {0, 0} is the additive identity: The
resulting point is the same as the left operand. The IAdditiveIdentity<TSelf, TResult>
interface defines one readonly property, AdditiveIdentity , that returns the identity
value. The Translation<T> needs a few changes to implement this interface:
C#
using System.Numerics;
There are a few changes here, so let's walk through them one by one. First, you declare
that the Translation type implements the IAdditiveIdentity interface:
C#
You next might try implementing the interface member as shown in the following code:
C#
The preceding code won't compile, because 0 depends on the type. The answer: Use
IAdditiveIdentity<T>.AdditiveIdentity for 0 . That change means that your constraints
C#
Now that you've added that constraint on Translation<T> , you need to add the same
constraint to Point<T> :
C#
using System.Numerics;
This sample has given you a look at how the interfaces for generic math compose. You
learned how to:
" Write a method that relied on the INumber<T> interface so that method could be
used with any numeric type.
" Build a type that relies on the addition interfaces to implement a type that only
supports one mathematical operation. That type declares its support for those same
interfaces so it can be composed in other ways. The algorithms are written using
the most natural syntax of mathematical operators.
Experiment with these features and register feedback. You can use the Send Feedback
menu item in Visual Studio, or create a new issue in the roslyn repository on GitHub.
Build generic algorithms that work with any numeric type. Build algorithms using these
interfaces where the type argument may only implement a subset of number-like
capabilities. Even if you don't build new interfaces that use these capabilities, you can
experiment with using them in your algorithms.
See also
Generic math
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Use pattern matching to build your class
behavior for better code
Article • 12/06/2022
The pattern matching features in C# provide syntax to express your algorithms. You can
use these techniques to implement the behavior in your classes. You can combine
object-oriented class design with a data-oriented implementation to provide concise
code while modeling real-world objects.
Prerequisites
You'll need to set up your machine to run .NET. Download Visual Studio 2022 or the
.NET SDK .
In its normal operation, a boat enters one of the gates while the water level in the lock
matches the water level on the side the boat enters. Once in the lock, the water level is
changed to match the water level where the boat will leave the lock. Once the water
level matches that side, the gate on the exit side opens. Safety measures make sure an
operator can't create a dangerous situation in the canal. The water level can be changed
only when both gates are closed. At most one gate can be open. To open a gate, the
water level in the lock must match the water level outside the gate being opened.
You can build a C# class to model this behavior. A CanalLock class would support
commands to open or close either gate. It would have other commands to raise or lower
the water. The class should also support properties to read the current state of both
gates and the water level. Your methods implement the safety measures.
Define a class
You'll build a console application to test your CanalLock class. Create a new console
project for .NET 5 using either Visual Studio or the .NET CLI. Then, add a new class and
name it CanalLock . Next, design your public API, but leave the methods not
implemented:
C#
The preceding code initializes the object so both gates are closed, and the water level is
low. Next, write the following test code in your Main method to guide you as you create
a first implementation of the class:
C#
canalGate.SetLowGate(open: true);
Console.WriteLine($"Open the lower gate: {canalGate}");
canalGate.SetLowGate(open: false);
Console.WriteLine($"Close the lower gate: {canalGate}");
canalGate.SetWaterLevel(WaterLevel.High);
Console.WriteLine($"Raise the water level: {canalGate}");
canalGate.SetHighGate(open: true);
Console.WriteLine($"Open the higher gate: {canalGate}");
canalGate.SetHighGate(open: false);
Console.WriteLine($"Close the higher gate: {canalGate}");
canalGate.SetWaterLevel(WaterLevel.Low);
Console.WriteLine($"Lower the water level: {canalGate}");
canalGate.SetLowGate(open: true);
Console.WriteLine($"Open the lower gate: {canalGate}");
canalGate.SetLowGate(open: false);
Console.WriteLine($"Close the lower gate: {canalGate}");
Next, add a first implementation of each method in the CanalLock class. The following
code implements the methods of the class without concern to the safety rules. You'll
add safety tests later:
C#
The tests you've written so far pass. You've implemented the basics. Now, write a test for
the first failure condition. At the end of the previous tests, both gates are closed, and
the water level is set to low. Add a test to try opening the upper gate:
C#
Console.WriteLine("=============================================");
Console.WriteLine(" Test invalid commands");
// Open "wrong" gate (2 tests)
try
{
canalGate = new CanalLock();
canalGate.SetHighGate(open: true);
}
catch (InvalidOperationException)
{
Console.WriteLine("Invalid operation: Can't open the high gate. Water is
low.");
}
Console.WriteLine($"Try to open upper gate: {canalGate}");
This test fails because the gate opens. As a first implementation, you could fix it with the
following code:
C#
Your tests pass. But, as you add more tests, you'll add more and more if clauses and
test different properties. Soon, these methods will get too complicated as you add more
conditionals.
ノ Expand table
The fourth and last rows in the table have strike through text because they're invalid.
The code you're adding now should make sure the high water gate is never opened
when the water is low. Those states can be coded as a single switch expression
(remember that false indicates "Closed"):
C#
C#
The preceding switch arm must be last in your switch expression because it matches all
inputs. Experiment by moving it earlier in the order. That causes a compiler error CS8510
for unreachable code in a pattern. The natural structure of switch expressions enables
the compiler to generate errors and warnings for possible mistakes. The compiler "safety
net" makes it easier for you to create correct code in fewer iterations, and the freedom
to combine switch arms with wildcards. The compiler will issue errors if your
combination results in unreachable arms you didn't expect, and warnings if you remove
an arm that's needed.
The first change is to combine all the arms where the command is to close the gate;
that's always allowed. Add the following code as the first arm in your switch expression:
C#
After you add the previous switch arm, you'll get four compiler errors, one on each of
the arms where the command is false . Those arms are already covered by the newly
added arm. You can safely remove those four lines. You intended this new switch arm to
replace those conditions.
Next, you can simplify the four arms where the command is to open the gate. In both
cases where the water level is high, the gate can be opened. (In one, it's already open.)
One case where the water level is low throws an exception, and the other shouldn't
happen. It should be safe to throw the same exception if the water lock is already in an
invalid state. You can make the following simplifications for those arms:
C#
Run your tests again, and they pass. Here's the final version of the SetHighGate method:
C#
C#
Console.WriteLine();
Console.WriteLine();
try
{
canalGate = new CanalLock();
canalGate.SetWaterLevel(WaterLevel.High);
canalGate.SetLowGate(open: true);
}
catch (InvalidOperationException)
{
Console.WriteLine("invalid operation: Can't open the lower gate. Water
is high.");
}
Console.WriteLine($"Try to open lower gate: {canalGate}");
// change water level with gate open (2 tests)
Console.WriteLine();
Console.WriteLine();
try
{
canalGate = new CanalLock();
canalGate.SetLowGate(open: true);
canalGate.SetWaterLevel(WaterLevel.High);
}
catch (InvalidOperationException)
{
Console.WriteLine("invalid operation: Can't raise water when the lower
gate is open.");
}
Console.WriteLine($"Try to raise water with lower gate open: {canalGate}");
Console.WriteLine();
Console.WriteLine();
try
{
canalGate = new CanalLock();
canalGate.SetWaterLevel(WaterLevel.High);
canalGate.SetHighGate(open: true);
canalGate.SetWaterLevel(WaterLevel.Low);
}
catch (InvalidOperationException)
{
Console.WriteLine("invalid operation: Can't lower water when the high
gate is open.");
}
Console.WriteLine($"Try to lower water with high gate open: {canalGate}");
Run your application again. You can see the new tests fail, and the canal lock gets into
an invalid state. Try to implement the remaining methods yourself. The method to set
the lower gate should be similar to the method to set the upper gate. The method that
changes the water level has different checks, but should follow a similar structure. You
may find it helpful to use the same process for the method that sets the water level.
Start with all four inputs: The state of both gates, the current state of the water level,
and the requested new water level. The switch expression should start with:
C#
You'll have 16 total switch arms to fill in. Then, test and simplify.
Your tests should pass, and the canal lock should operate safely.
Summary
In this tutorial, you learned to use pattern matching to check the internal state of an
object before applying any changes to that state. You can check combinations of
properties. Once you've built tables for any of those transitions, you test your code, then
simplify for readability and maintainability. These initial refactorings may suggest further
refactorings that validate internal state or manage other API changes. This tutorial
combined classes and objects with a more data-oriented, pattern-based approach to
implement those classes.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Tutorial: Write a custom string
interpolation handler
Article • 04/06/2023
Prerequisites
You'll need to set up your machine to run .NET 6, including the C# 10 compiler. The C#
10 compiler is available starting with Visual Studio 2022 or .NET 6 SDK .
This tutorial assumes you're familiar with C# and .NET, including either Visual Studio or
the .NET CLI.
New outline
C# 10 adds support for a custom interpolated string handler. An interpolated string
handler is a type that processes the placeholder expression in an interpolated string.
Without a custom handler, placeholders are processed similar to String.Format. Each
placeholder is formatted as text, and then the components are concatenated to form
the resulting string.
You can write a handler for any scenario where you use information about the resulting
string. Will it be used? What constraints are on the format? Some examples include:
You may require none of the resulting strings are greater than some limit, such as
80 characters. You can process the interpolated strings to fill a fixed-length buffer,
and stop processing once that buffer length is reached.
You may have a tabular format, and each placeholder must have a fixed length. A
custom handler can enforce that, rather than forcing all client code to conform.
In this tutorial, you'll create a string interpolation handler for one of the core
performance scenarios: logging libraries. Depending on the configured log level, the
work to construct a log message isn't needed. If logging is off, the work to construct a
string from an interpolated string expression isn't needed. The message is never printed,
so any string concatenation can be skipped. In addition, any expressions used in the
placeholders, including generating stack traces, doesn't need to be done.
An interpolated string handler can determine if the formatted string will be used, and
only perform the necessary work if needed.
Initial implementation
Let's start from a basic Logger class that supports different levels:
C#
This Logger supports six different levels. When a message won't pass the log level filter,
there's no output. The public API for the logger accepts a (fully formatted) string as the
message. All the work to create the string has already been done.
Internally, the builder creates the formatted string, and provides a member for a client
to retrieve that string. The following code shows a LogInterpolatedStringHandler type
that meets these requirements:
C#
[InterpolatedStringHandler]
public ref struct LogInterpolatedStringHandler
{
// Storage for the built-up string
StringBuilder builder;
builder.Append(s);
Console.WriteLine($"\tAppended the literal string");
}
builder.Append(t?.ToString());
Console.WriteLine($"\tAppended the formatted object");
}
You can now add an overload to LogMessage in the Logger class to try your new
interpolated string handler:
C#
public void LogMessage(LogLevel level, LogInterpolatedStringHandler builder)
{
if (EnabledLevel < level) return;
Console.WriteLine(builder.GetFormattedText());
}
You don't need to remove the original LogMessage method, the compiler will prefer a
method with an interpolated handler parameter over a method with a string parameter
when the argument is an interpolated string expression.
You can verify that the new handler is invoked using the following code as the main
program:
C#
PowerShell
Tracing through the output, you can see how the compiler adds code to call the handler
and build the string:
The compiler adds a call to construct the handler, passing the total length of the
literal text in the format string, and the number of placeholders.
The compiler adds calls to AppendLiteral and AppendFormatted for each section of
the literal string and for each placeholder.
The compiler invokes the LogMessage method using the
CoreInterpolatedStringHandler as the argument.
Finally, notice that the last warning doesn't invoke the interpolated string handler. The
argument is a string , so that call invokes the other overload with a string parameter.
Let's start with changes to the Handler. First, add a field to track if the handler is
enabled. Add two parameters to the constructor: one to specify the log level for this
message, and the other a reference to the log object:
C#
C#
builder.Append(s);
Console.WriteLine($"\tAppended the literal string");
}
builder.Append(t?.ToString());
Console.WriteLine($"\tAppended the formatted object");
}
Next, you'll need to update the LogMessage declaration so that the compiler passes the
additional parameters to the handler's constructor. That's handled using the
System.Runtime.CompilerServices.InterpolatedStringHandlerArgumentAttribute on the
handler argument:
C#
This attribute specifies the list of arguments to LogMessage that map to the parameters
that follow the required literalLength and formattedCount parameters. The empty
string (""), specifies the receiver. The compiler substitutes the value of the Logger object
represented by this for the next argument to the handler's constructor. The compiler
substitutes the value of level for the following argument. You can provide any number
of arguments for any handler you write. The arguments that you add are string
arguments.
You can run this version using the same test code. This time, you'll see the following
results:
PowerShell
You can see that the AppendLiteral and AppendFormat methods are being called, but
they aren't doing any work. The handler has determined that the final string won't be
needed, so the handler doesn't build it. There are still a couple of improvements to
make.
First, you can add an overload of AppendFormatted that constrains the argument to a
type that implements System.IFormattable. This overload enables callers to add format
strings in the placeholders. While making this change, let's also change the return type
of the other AppendFormatted and AppendLiteral methods, from void to bool (if any of
these methods have different return types, then you'll get a compilation error). That
change enables short circuiting. The methods return false to indicate that processing of
the interpolated string expression should be stopped. Returning true indicates that it
should continue. In this example, you're using it to stop processing when the resulting
string isn't needed. Short circuiting supports more fine-grained actions. You could stop
processing the expression once it reaches a certain length, to support fixed-length
buffers. Or some condition could indicate remaining elements aren't needed.
C#
With that addition, you can specify format strings in your interpolated string expression:
C#
The :t on the first message specifies the "short time format" for the current time. The
previous example showed one of the overloads to the AppendFormatted method that you
can create for your handler. You don't need to specify a generic argument for the object
being formatted. You may have more efficient ways to convert types you create to
string. You can write overloads of AppendFormatted that takes those types instead of a
generic argument. The compiler will pick the best overload. The runtime uses this
technique to convert System.Span<T> to string output. You can add an integer
parameter to specify the alignment of the output, with or without an IFormattable. The
System.Runtime.CompilerServices.DefaultInterpolatedStringHandler that ships with .NET
6 contains nine overloads of AppendFormatted for different uses. You can use it as a
reference while building a handler for your purposes.
Run the sample now, and you'll see that for the Trace message, only the first
AppendLiteral is called:
PowerShell
You can make one final update to the handler's constructor that improves efficiency. The
handler can add a final out bool parameter. Setting that parameter to false indicates
that the handler shouldn't be called at all to process the interpolated string expression:
C#
That change means you can remove the enabled field. Then, you can change the return
type of AppendLiteral and AppendFormatted to void . Now, when you run the sample,
you'll see the following output:
PowerShell
The only output when LogLevel.Trace was specified is the output from the constructor.
The handler indicated that it's not enabled, so none of the Append methods were
invoked.
This example illustrates an important point for interpolated string handlers, especially
when logging libraries are used. Any side-effects in the placeholders may not occur. Add
the following code to your main program and see this behavior in action:
C#
int index = 0;
int numberOfIncrements = 0;
for (var level = LogLevel.Critical; level <= LogLevel.Trace; level++)
{
Console.WriteLine(level);
logger.LogMessage(level, $"{level}: Increment index a few times
{index++}, {index++}, {index++}, {index++}, {index++}");
numberOfIncrements += 5;
}
Console.WriteLine($"Value of index {index}, value of numberOfIncrements:
{numberOfIncrements}");
You can see the index variable is incremented five times each iteration of the loop.
Because the placeholders are evaluated only for Critical , Error and Warning levels,
not for Information and Trace , the final value of index doesn't match the expectation:
PowerShell
Critical
Critical: Increment index a few times 0, 1, 2, 3, 4
Error
Error: Increment index a few times 5, 6, 7, 8, 9
Warning
Warning: Increment index a few times 10, 11, 12, 13, 14
Information
Trace
Value of index 15, value of numberOfIncrements: 25
Interpolated string handlers provide greater control over how an interpolated string
expression is converted to a string. The .NET runtime team has already used this feature
to improve performance in several areas. You can make use of the same capability in
your own libraries. To explore further, look at the
System.Runtime.CompilerServices.DefaultInterpolatedStringHandler. It provides a more
complete implementation than you built here. You'll see many more overloads that are
possible for the Append methods.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Create record types
Article • 11/14/2023
Records are types that use value-based equality. C# 10 adds record structs so that you
can define records as value types. Two variables of a record type are equal if the record
type definitions are identical, and if for every field, the values in both records are equal.
Two variables of a class type are equal if the objects referred to are the same class type
and the variables refer to the same object. Value-based equality implies other
capabilities you'll probably want in record types. The compiler generates many of those
members when you declare a record instead of a class . The compiler generates those
same methods for record struct types.
Prerequisites
You'll need to set up your machine to run .NET 6 or later, including the C# 10 or later
compiler. The C# 10 compiler is available starting with Visual Studio 2022 or the .NET
6 SDK .
Characteristics of records
You define a record by declaring a type with the record keyword, modifying a class or
struct declaration. Optionally, you can omit the class keyword to create a record
class . A record follows value-based equality semantics. To enforce value semantics, the
compiler generates several methods for your record type (both for record class types
and record struct types):
An override of Object.Equals(Object).
A virtual Equals method whose parameter is the record type.
An override of Object.GetHashCode().
Methods for operator == and operator != .
Record types implement System.IEquatable<T>.
You can also declare positional records using a more concise syntax. The compiler
synthesizes more methods for you when you declare positional records:
The formula is based on the mean temperature on a given day and a baseline
temperature. To compute degree days over time, you'll need the high and low
temperature each day for a period of time. Let's start by creating a new application.
Make a new console application. Create a new record type in a new file named
"DailyTemperature.cs":
C#
immutable. The HighTemp and LowTemp properties are init only properties, meaning they
can be set in the constructor or using a property initializer. If you wanted the positional
parameters to be read-write, you declare a record struct instead of a readonly record
struct . The DailyTemperature type also has a primary constructor that has two
parameters that match the two properties. You use the primary constructor to initialize a
DailyTemperature record. The following code creates and initializes several
DailyTemperature records. The first uses named parameters to clarify the HighTemp and
LowTemp . The remaining initializers use positional parameters to initialize the HighTemp
and LowTemp :
C#
You can add your own properties or methods to records, including positional records.
You'll need to compute the mean temperature for each day. You can add that property
to the DailyTemperature record:
C#
Let's make sure you can use this data. Add the following code to your Main method:
C#
.NET CLI
The preceding code shows the output from the override of ToString synthesized by the
compiler. If you prefer different text, you can write your own version of ToString that
prevents the compiler from synthesizing a version for you.
You can express these formulas as a small hierarchy of record types: an abstract degree
day type and two concrete types for heating degree days and cooling degree days.
These types can also be positional records. They take a baseline temperature and a
sequence of daily temperature records as arguments to the primary constructor:
C#
The abstract DegreeDays record is the shared base class for both the HeatingDegreeDays
and CoolingDegreeDays records. The primary constructor declarations on the derived
records show how to manage base record initialization. Your derived record declares
parameters for all the parameters in the base record primary constructor. The base
record declares and initializes those properties. The derived record doesn't hide them,
but only creates and initializes properties for parameters that aren't declared in its base
record. In this example, the derived records don't add new primary constructor
parameters. Test your code by adding the following code to your Main method:
C#
.NET CLI
provide your own version of a synthesized method, the signature must match the
synthesized method.
The TempRecords element in the console output isn't useful. It displays the type, but
nothing else. You can change this behavior by providing your own implementation of
the synthesized PrintMembers method. The signature depends on modifiers applied to
the record declaration:
If a record type isn't sealed and derives from object (that is, it doesn't declare a
base record), the signature is protected virtual bool PrintMembers(StringBuilder
builder);
If a record type isn't sealed and derives from another record, the signature is
protected override bool PrintMembers(StringBuilder builder);
string. The contract requires base records to add their members to the display and
assumes derived members will add their members. Each record type synthesizes a
ToString override that looks similar to the following example for HeatingDegreeDays :
C#
You declare a PrintMembers method in the DegreeDays record that doesn't print the type
of the collection:
C#
protected virtual bool PrintMembers(StringBuilder stringBuilder)
{
stringBuilder.Append($"BaseTemperature = {BaseTemperature}");
return true;
}
The signature declares a virtual protected method to match the compiler's version.
Don't worry if you get the accessors wrong; the language enforces the correct signature.
If you forget the correct modifiers for any synthesized method, the compiler issues
warnings or errors that help you get the right signature.
In C# 10 and later, you can declare the ToString method as sealed in a record type.
That prevents derived records from providing a new implementation. Derived records
will still contain the PrintMembers override. You would seal ToString if you didn't want it
to display the runtime type of the record. In the preceding example, you'd lose the
information on where the record was measuring heating or cooling degree days.
Non-destructive mutation
The synthesized members in a positional record class don't modify the state of the
record. The goal is that you can more easily create immutable records. Remember that
you declare a readonly record struct to create an immutable record struct. Look again
at the preceding declarations for HeatingDegreeDays and CoolingDegreeDays . The
members added perform computations on the values for the record, but don't mutate
state. Positional records make it easier for you to create immutable reference types.
Creating immutable reference types means you'll want to use non-destructive mutation.
You create new record instances that are similar to existing record instances using with
expressions. These expressions are a copy construction with additional assignments that
modify the copy. The result is a new record instance where each property has been
copied from the existing record and optionally modified. The original record is
unchanged.
Let's add a couple features to your program that demonstrate with expressions. First,
let's create a new record to compute growing degree days using the same data.
Growing degree days typically uses 41F as the baseline and measures temperatures
above the baseline. To use the same data, you can create a new record that is similar to
the coolingDegreeDays , but with a different base temperature:
C#
// Growing degree days measure warming to determine plant growing rates
var growingDegreeDays = coolingDegreeDays with { BaseTemperature = 41 };
Console.WriteLine(growingDegreeDays);
You can compare the number of degrees computed to the numbers generated with a
higher baseline temperature. Remember that records are reference types and these
copies are shallow copies. The array for the data isn't copied, but both records refer to
the same data. That fact is an advantage in one other scenario. For growing degree
days, it's useful to keep track of the total for the previous five days. You can create new
records with different source data using with expressions. The following code builds a
collection of these accumulations, then displays the values:
C#
You can also use with expressions to create copies of records. Don't specify any
properties between the braces for the with expression. That means create a copy, and
don't change any properties:
C#
Summary
This tutorial showed several aspects of records. Records provide concise syntax for types
where the fundamental use is storing data. For object-oriented classes, the fundamental
use is defining responsibilities. This tutorial focused on positional records, where you can
use a concise syntax to declare the properties for a record. The compiler synthesizes
several members of the record for copying and comparing records. You can add any
other members you need for your record types. You can create immutable record types
knowing that none of the compiler-generated members would mutate state. And with
expressions make it easy to support non-destructive mutation.
Records add another way to define types. You use class definitions to create object-
oriented hierarchies that focus on the responsibilities and behavior of objects. You
create struct types for data structures that store data and are small enough to copy
efficiently. You create record types when you want value-based equality and
comparison, don't want to copy values, and want to use reference variables. You create
record struct types when you want the features of records for a type that is small
You can learn more about records in the C# language reference article for the record
type and the proposed record type specification and record struct specification.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Tutorial: Explore ideas using top-level
statements to build code as you learn
Article • 11/14/2023
Prerequisites
You'll need to set up your machine to run .NET 6, which includes the C# 10 compiler. The
C# 10 compiler is available starting with Visual Studio 2022 or .NET 6 SDK .
This tutorial assumes you're familiar with C# and .NET, including either Visual Studio or
the .NET CLI.
Start exploring
Top-level statements enable you to avoid the extra ceremony required by placing your
program's entry point in a static method in a class. The typical starting point for a new
console application looks like the following code:
C#
using System;
namespace Application
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
The preceding code is the result of running the dotnet new console command and
creating a new console application. Those 11 lines contain only one line of executable
code. You can simplify that program with the new top-level statements feature. That
enables you to remove all but two of the lines in this program:
C#
) Important
The C# templates for .NET 6 use top level statements. Your application may not
match the code in this article, if you've already upgraded to the .NET 6. For more
information see the article on New C# templates generate top level statements
The .NET 6 SDK also adds a set of implicit global using directives for projects that
use the following SDKs:
Microsoft.NET.Sdk
Microsoft.NET.Sdk.Web
Microsoft.NET.Sdk.Worker
These implicit global using directives include the most common namespaces for
the project type.
This feature simplifies what's needed to begin exploring new ideas. You can use top-
level statements for scripting scenarios, or to explore. Once you've got the basics
working, you can start refactoring the code and create methods, classes, or other
assemblies for reusable components you've built. Top-level statements do enable quick
experimentation and beginner tutorials. They also provide a smooth path from
experimentation to full programs.
Top-level statements are executed in the order they appear in the file. Top-level
statements can only be used in one source file in your application. The compiler
generates an error if you use them in more than one file.
A good starting point is to write the question back to the console. You can start by
writing the following code:
C#
Console.WriteLine(args);
You don't declare an args variable. For the single source file that contains your top-level
statements, the compiler recognizes args to mean the command-line arguments. The
type of args is a string[] , as in all C# programs.
You can test your code by running the following dotnet run command:
.NET CLI
The arguments after the -- on the command line are passed to the program. You can
see the type of the args variable, because that's what's printed to the console:
Console
System.String[]
To write the question to the console, you'll need to enumerate the arguments and
separate them with a space. Replace the WriteLine call with the following code:
C#
Console.WriteLine();
foreach(var s in args)
{
Console.Write(s);
Console.Write(' ');
}
Console.WriteLine();
Now, when you run the program, it will correctly display the question as a string of
arguments.
Respond with a random answer
After echoing the question, you can add the code to generate the random answer. Start
by adding an array of possible answers:
C#
string[] answers =
[
"It is certain.", "Reply hazy, try again.", "Don’t count on
it.",
"It is decidedly so.", "Ask again later.", "My reply is no.",
"Without a doubt.", "Better not tell you now.", "My sources say
no.",
"Yes – definitely.", "Cannot predict now.", "Outlook not so
good.",
"You may rely on it.", "Concentrate and ask again.", "Very doubtful.",
"As I see it, yes.",
"Most likely.",
"Outlook good.",
"Yes.",
"Signs point to yes.",
];
This array has ten answers that are affirmative, five that are non-committal, and five that
are negative. Next, add the following code to generate and display a random answer
from the array:
C#
You can run the application again to see the results. You should see something like the
following output:
.NET CLI
This code answers the questions, but let's add one more feature. You'd like your
question app to simulate thinking about the answer. You can do that by adding a bit of
ASCII animation, and pausing while working. Add the following code after the line that
echoes the question:
C#
You'll also need to add a using directive to the top of the source file:
C#
using System.Threading.Tasks;
The using directives must be before any other statements in the file. Otherwise, it's a
compiler error. You can run the program again and see the animation. That makes a
better experience. Experiment with the length of the delay to match your taste.
The preceding code creates a set of spinning lines separated by a space. Adding the
await keyword instructs the compiler to generate the program entry point as a method
that has the async modifier, and returns a System.Threading.Tasks.Task. This program
doesn't return a value, so the program entry point returns a Task . If your program
returns an integer value, you would add a return statement to the end of your top-level
statements. That return statement would specify the integer value to return. If your top-
level statements include an await expression, the return type becomes
System.Threading.Tasks.Task<TResult>.
C#
Console.WriteLine();
foreach(var s in args)
{
Console.Write(s);
Console.Write(' ');
}
Console.WriteLine();
string[] answers =
[
"It is certain.", "Reply hazy, try again.", "Don't count on
it.",
"It is decidedly so.", "Ask again later.", "My reply is no.",
"Without a doubt.", "Better not tell you now.", "My sources say
no.",
"Yes – definitely.", "Cannot predict now.", "Outlook not so
good.",
"You may rely on it.", "Concentrate and ask again.", "Very doubtful.",
"As I see it, yes.",
"Most likely.",
"Outlook good.",
"Yes.",
"Signs point to yes.",
];
The preceding code is reasonable. It works. But it isn't reusable. Now that you have the
application working, it's time to pull out reusable parts.
One candidate is the code that displays the waiting animation. That snippet can become
a method:
You can start by creating a local function in your file. Replace the current animation with
the following code:
C#
await ShowConsoleAnimation();
The preceding code creates a local function inside your main method. That's still not
reusable. So, extract that code into a class. Create a new file named utilities.cs and add
the following code:
C#
namespace MyNamespace
{
public static class Utilities
{
public static async Task ShowConsoleAnimation()
{
for (int i = 0; i < 20; i++)
{
Console.Write("| -");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("/ \\");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("- |");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("\\ /");
await Task.Delay(50);
Console.Write("\b\b\b");
}
Console.WriteLine();
}
}
}
A file that has top-level statements can also contain namespaces and types at the end of
the file, after the top-level statements. But for this tutorial you put the animation
method in a separate file to make it more readily reusable.
Finally, you can clean the animation code to remove some duplication, by using foreach
loop to iterate through set of animations elements defined in animations array.
The full ShowConsoleAnimation method after refactor should look similar to the
following:
C#
Now you have a complete application, and you've refactored the reusable parts for later
use. You can call the new utility method from your top-level statements, as shown below
in the finished version of the main program:
C#
using MyNamespace;
Console.WriteLine();
foreach(var s in args)
{
Console.Write(s);
Console.Write(' ');
}
Console.WriteLine();
await Utilities.ShowConsoleAnimation();
string[] answers =
[
"It is certain.", "Reply hazy, try again.", "Don’t count on
it.",
"It is decidedly so.", "Ask again later.", "My reply is no.",
"Without a doubt.", "Better not tell you now.", "My sources say
no.",
"Yes – definitely.", "Cannot predict now.", "Outlook not so
good.",
"You may rely on it.", "Concentrate and ask again.", "Very doubtful.",
"As I see it, yes.",
"Most likely.",
"Outlook good.",
"Yes.",
"Signs point to yes.",
];
Summary
Top-level statements make it easier to create simple programs for use to explore new
algorithms. You can experiment with algorithms by trying different snippets of code.
Once you've learned what works, you can refactor the code to be more maintainable.
Top-level statements simplify programs that are based on console applications. These
include Azure functions, GitHub actions, and other small utilities. For more information,
see Top-level statements (C# Programming Guide).
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Indices and ranges
Article • 11/14/2023
Ranges and indices provide a succinct syntax for accessing single elements or ranges in
a sequence.
This language support relies on two new types and two new operators:
Let's start with the rules for indices. Consider an array sequence . The 0 index is the same
as sequence[0] . The ^0 index is the same as sequence[sequence.Length] . The expression
sequence[^0] throws an exception, just as sequence[sequence.Length] does. For any
C#
You can retrieve the last word with the ^1 index. Add the following code below the
initialization:
C#
A range specifies the start and end of a range. The start of the range is inclusive, but the
end of the range is exclusive, meaning the start is included in the range but the end isn't
included in the range. The range [0..^0] represents the entire range, just as
[0..sequence.Length] represents the entire range.
The following code creates a subrange with the words "second", "third", and "fourth". It
includes words[1] through words[3] . The element words[4] isn't in the range.
C#
The following code returns the range with "ninth" and "tenth". It includes words[^2] and
words[^1] . The end index words[^0] isn't included.
C#
The following examples create ranges that are open ended for the start, end, or both:
C#
string[] allWords = words[..]; // contains "first" through "tenth".
string[] firstPhrase = words[..4]; // contains "first" through "fourth"
string[] lastPhrase = words[6..]; // contains "seventh", "eight", "ninth"
and "tenth"
// < first >< second >< third >< fourth >< fifth >< sixth >< seventh ><
eighth >< ninth >< tenth >
foreach (var word in allWords)
Console.Write($"< {word} >");
Console.WriteLine();
You can also declare ranges or indices as variables. The variable can then be used inside
the [ and ] characters:
C#
The following sample shows many of the reasons for those choices. Modify x , y , and z
to try different combinations. When you experiment, use values where x is less than y ,
and y is less than z for valid combinations. Add the following code in a new method.
Try different combinations:
C#
Not only arrays support indices and ranges. You can also use indices and ranges with
string, Span<T>, or ReadOnlySpan<T>.
C#
if (implicitRange.Equals(explicitRange))
{
Console.WriteLine(
$"The implicit range '{implicitRange}' equals the explicit range
'{explicitRange}'");
}
// Sample output:
// The implicit range '3..^5' equals the explicit range '3..^5'
) Important
Any type that provides an indexer with an Index or Range parameter explicitly supports
indices or ranges respectively. An indexer that takes a single Range parameter may
return a different sequence type, such as System.Span<T>.
) Important
The performance of code using the range operator depends on the type of the
sequence operand.
The time complexity of the range operator depends on the sequence type. For
example, if the sequence is a string or an array, then the result is a copy of the
specified section of the input, so the time complexity is O(N) (where N is the length
of the range). On the other hand, if it's a System.Span<T> or a
System.Memory<T>, the result references the same backing store, which means
there is no copy and the operation is O(1).
In addition to the time complexity, this causes extra allocations and copies,
impacting performance. In performance sensitive code, consider using Span<T> or
Memory<T> as the sequence type, since the range operator does not allocate for
them.
A type is countable if it has a property named Length or Count with an accessible getter
and a return type of int . A countable type that doesn't explicitly support indices or
ranges may provide an implicit support for them. For more information, see the Implicit
Index support and Implicit Range support sections of the feature proposal note. Ranges
using implicit range support return the same sequence type as the source sequence.
For example, the following .NET types support both indices and ranges: String,
Span<T>, and ReadOnlySpan<T>. The List<T> supports indices but doesn't support
ranges.
Array has more nuanced behavior. Single dimension arrays support both indices and
ranges. Multi-dimensional arrays don't support indexers or ranges. The indexer for a
multi-dimensional array has multiple parameters, not a single parameter. Jagged arrays,
also referred to as an array of arrays, support both ranges and indexers. The following
example shows how to iterate a rectangular subsection of a jagged array. It iterates the
section in the center, excluding the first and last three rows, and the first and last two
columns from each selected row:
C#
int[][] jagged =
[
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[10,11,12,13,14,15,16,17,18,19],
[20,21,22,23,24,25,26,27,28,29],
[30,31,32,33,34,35,36,37,38,39],
[40,41,42,43,44,45,46,47,48,49],
[50,51,52,53,54,55,56,57,58,59],
[60,61,62,63,64,65,66,67,68,69],
[70,71,72,73,74,75,76,77,78,79],
[80,81,82,83,84,85,86,87,88,89],
[90,91,92,93,94,95,96,97,98,99],
];
In all cases, the range operator for Array allocates an array to store the elements
returned.
C#
For example:
C#
var arrayOfFiveItems = new[] { 1, 2, 3, 4, 5 };
Console.WriteLine(string.Join(",", firstThreeItems));
Console.WriteLine(string.Join(",", arrayOfFiveItems));
// output:
// 11,2,3
// 1,2,3,4,5
See also
Member access operators and expressions
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Tutorial: Express your design intent
more clearly with nullable and non-
nullable reference types
Article • 11/03/2022
Nullable reference types complement reference types the same way nullable value types
complement value types. You declare a variable to be a nullable reference type by
appending a ? to the type. For example, string? represents a nullable string . You can
use these new types to more clearly express your design intent: some variables must
always have a value, others may be missing a value.
Prerequisites
You'll need to set up your machine to run .NET, including the C# compiler. The C#
compiler is available with Visual Studio 2022 , or the .NET SDK .
This tutorial assumes you're familiar with C# and .NET, including either Visual Studio or
the .NET CLI.
The code you'll write for this sample expresses that intent, and the compiler enforces
that intent.
Create the application and enable nullable
reference types
Create a new console application either in Visual Studio or from the command line using
dotnet new console . Name the application NullableIntroduction . Once you've created
the application, you'll need to specify that the entire project compiles in an enabled
nullable annotation context. Open the .csproj file and add a Nullable element to the
PropertyGroup element. Set its value to enable . You must opt into the nullable
reference types feature in projects earlier than C# 11. That's because once the feature is
turned on, existing reference variable declarations become non-nullable reference
types. While that decision will help find issues where existing code may not have proper
null-checks, it may not accurately reflect your original design intent:
XML
<Nullable>enable</Nullable>
Prior to .NET 6, new projects do not include the Nullable element. Beginning with .NET
6, new projects include the <Nullable>enable</Nullable> element in the project file.
These types will make use of both nullable and non-nullable reference types to express
which members are required and which members are optional. Nullable reference types
communicate that design intent clearly:
The questions that are part of the survey can never be null: It makes no sense to
ask an empty question.
The respondents can never be null. You'll want to track people you contacted, even
respondents that declined to participate.
Any response to a question may be null. Respondents can decline to answer some
or all questions.
If you've programmed in C#, you may be so accustomed to reference types that allow
null values that you may have missed other opportunities to declare non-nullable
instances:
As you write the code, you'll see that a non-nullable reference type as the default for
references avoids common mistakes that could lead to NullReferenceExceptions. One
lesson from this tutorial is that you made decisions about which variables could or could
not be null . The language didn't provide syntax to express those decisions. Now it
does.
C#
namespace NullableIntroduction
{
public class SurveyQuestion
{
}
}
C#
namespace NullableIntroduction
{
public enum QuestionType
{
YesNo,
Number,
Text
}
Because you haven't initialized QuestionText , the compiler issues a warning that a non-
nullable property hasn't been initialized. Your design requires the question text to be
non-null, so you add a constructor to initialize it and the QuestionType value as well. The
finished class definition looks like the following code:
C#
namespace NullableIntroduction;
Adding the constructor removes the warning. The constructor argument is also a non-
nullable reference type, so the compiler doesn't issue any warnings.
Next, create a public class named SurveyRun . This class contains a list of
SurveyQuestion objects and methods to add questions to the survey, as shown in the
following code:
C#
using System.Collections.Generic;
namespace NullableIntroduction
{
public class SurveyRun
{
private List<SurveyQuestion> surveyQuestions = new
List<SurveyQuestion>();
As before, you must initialize the list object to a non-null value or the compiler issues a
warning. There are no null checks in the second overload of AddQuestion because they
aren't needed: You've declared that variable to be non-nullable. Its value can't be null .
Switch to Program.cs in your editor and replace the contents of Main with the following
lines of code:
C#
Because the entire project is in an enabled nullable annotation context, you'll get
warnings when you pass null to any method expecting a non-nullable reference type.
Try it by adding the following line to Main :
C#
surveyRun.AddQuestion(QuestionType.Text, default);
1. Build a method that generates respondent objects. These represent people asked
to fill out the survey.
2. Build logic to simulate asking the questions to a respondent and collecting
answers or noting that a respondent didn't answer.
3. Repeat until enough respondents have answered the survey.
You'll need a class to represent a survey response, so add that now. Enable nullable
support. Add an Id property and a constructor that initializes it, as shown in the
following code:
C#
namespace NullableIntroduction
{
public class SurveyResponse
{
public int Id { get; }
Next, add a static method to create new participants by generating a random ID:
C#
The main responsibility of this class is to generate the responses for a participant to the
questions in the survey. This responsibility has a few steps:
1. Ask for participation in the survey. If the person doesn't consent, return a missing
(or null) response.
2. Ask each question and record the answer. Each answer may also be missing (or
null).
C#
private Dictionary<int, string>? surveyResponses;
public bool AnswerSurvey(IEnumerable<SurveyQuestion> questions)
{
if (ConsentToSurvey())
{
surveyResponses = new Dictionary<int, string>();
int index = 0;
foreach (var question in questions)
{
var answer = GenerateAnswer(question);
if (answer != null)
{
surveyResponses.Add(index, answer);
}
index++;
}
}
return surveyResponses != null;
}
The storage for the survey answers is a Dictionary<int, string>? , indicating that it may
be null. You're using the new language feature to declare your design intent, both to the
compiler and to anyone reading your code later. If you ever dereference
surveyResponses without checking for the null value first, you'll get a compiler warning.
You don't get a warning in the AnswerSurvey method because the compiler can
determine the surveyResponses variable was set to a non-null value above.
Using null for missing answers highlights a key point for working with nullable
reference types: your goal isn't to remove all null values from your program. Rather,
your goal is to ensure that the code you write expresses the intent of your design.
Missing values are a necessary concept to express in your code. The null value is a clear
way to express those missing values. Trying to remove all null values only leads to
defining some other way to express those missing values without null .
Next, you need to write the PerformSurvey method in the SurveyRun class. Add the
following code in the SurveyRun class:
C#
Here again, your choice of a nullable List<SurveyResponse>? indicates the response may
be null. That indicates the survey hasn't been given to any respondents yet. Notice that
respondents are added until enough have consented.
The last step to run the survey is to add a call to perform the survey at the end of the
Main method:
C#
surveyRun.PerformSurvey(50);
C#
Because surveyResponses is a nullable reference type, null checks are necessary before
de-referencing it. The Answer method returns a non-nullable string, so we have to cover
the case of a missing answer by using the null-coalescing operator.
C#
The AllParticipants member must take into account that the respondents variable
might be null, but the return value can't be null. If you change that expression by
removing the ?? and the empty sequence that follows, the compiler warns you the
method might return null and its return signature returns a non-nullable type.
Finally, add the following loop at the bottom of the Main method:
C#
You don't need any null checks in this code because you've designed the underlying
interfaces so that they all return non-nullable reference types.
Next steps
Learn how to use nullable reference type when using Entity Framework:
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Use string interpolation to construct
formatted strings
Article • 09/15/2021
This tutorial teaches you how to use C# string interpolation to insert values into a single
result string. You write C# code and see the results of compiling and running it. The
tutorial contains a series of lessons that show you how to insert values into a string and
format those values in different ways.
This tutorial expects that you have a machine you can use for development. The .NET
tutorial Hello World in 10 minutes has instructions for setting up your local
development environment on Windows, Linux, or macOS. You can also complete the
interactive version of this tutorial in your browser.
.NET CLI
This command creates a new .NET Core console application in the current directory.
Open Program.cs in your favorite editor, and replace the line Console.WriteLine("Hello
World!"); with the following code, where you replace <name> with your name:
C#
Try this code by typing dotnet run in your console window. When you run the program,
it displays a single string that includes your name in the greeting. The string included in
the WriteLine method call is an interpolated string expression. It's a kind of template that
lets you construct a single string (called the result string) from a string that includes
embedded code. Interpolated strings are particularly useful for inserting values into a
string or concatenating (joining together) strings.
This simple example contains the two elements that every interpolated string must have:
A string literal that begins with the $ character before its opening quotation mark
character. There can't be any spaces between the $ symbol and the quotation
mark character. (If you'd like to see what happens if you include one, insert a space
after the $ character, save the file, and run the program again by typing dotnet
run in the console window. The C# compiler displays an error message, "error
Let's try a few more string interpolation examples with some other data types.
In the following example, we first define a class data type Vegetable that has a Name
property and a ToString method, which overrides the behavior of the Object.ToString()
method. The public access modifier makes that method available to any client code to
get the string representation of a Vegetable instance. In the example the
Vegetable.ToString method returns the value of the Name property that is initialized at
C#
Then we create an instance of the Vegetable class named item by using the new
operator and providing a name for the constructor Vegetable :
C#
Finally, we include the item variable into an interpolated string that also contains a
DateTime value, a Decimal value, and a Unit enumeration value. Replace all of the C#
code in your editor with the following code, and then use the dotnet run command to
run it:
C#
using System;
Note that the interpolation expression item in the interpolated string resolves to the
text "eggplant" in the result string. That's because, when the type of the expression
result is not a string, the result is resolved to a string in the following way:
In the output from this example, the date is too precise (the price of eggplant doesn't
change every second), and the price value doesn't indicate a unit of currency. In the next
section, you'll learn how to fix those issues by controlling the format of string
representations of the expression results.
C#
You specify a format string by following the interpolation expression with a colon (":")
and the format string. "d" is a standard date and time format string that represents the
short date format. "C2" is a standard numeric format string that represents a number as
a currency value with two digits after the decimal point.
A number of types in the .NET libraries support a predefined set of format strings. These
include all the numeric types and the date and time types. For a complete list of types
that support format strings, see Format Strings and .NET Class Library Types in the
Formatting Types in .NET article.
Try modifying the format strings in your text editor and, each time you make a change,
rerun the program to see how the changes affect the formatting of the date and time
and the numeric value. Change the "d" in {date:d} to "t" (to display the short time
format), "y" (to display the year and month), and "yyyy" (to display the year as a four-
digit number). Change the "C2" in {price:C2} to "e" (for exponential notation) and "F3"
(for a numeric value with three digits after the decimal point).
In addition to controlling formatting, you can also control the field width and alignment
of the formatted strings that are included in the result string. In the next section, you'll
learn how to do this.
C#
using System;
using System.Collections.Generic;
The names of authors are left-aligned, and the titles they wrote are right-aligned. You
specify the alignment by adding a comma (",") after an interpolation expression and
designating the minimum field width. If the specified value is a positive number, the
field is right-aligned. If it is a negative number, the field is left-aligned.
Try removing the negative signs from the {"Author",-25} and {title.Key,-25} code
and run the example again, as the following code does:
C#
Console.WriteLine($"|{"Author",25}|{"Title",30}|");
foreach (var title in titles)
Console.WriteLine($"|{title.Key,25}|{title.Value,30}|");
You can combine an alignment specifier and a format string for a single interpolation
expression. To do that, specify the alignment first, followed by a colon and the format
string. Replace all of the code inside the Main method with the following code, which
displays three formatted strings with defined field widths. Then run the program by
entering the dotnet run command.
C#
Console
For more information, see the String interpolation topic and the String interpolation in
C# tutorial.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
String interpolation in C#
Article • 08/29/2023
This tutorial shows you how to use string interpolation to format and include expression
results in a result string. The examples assume that you are familiar with basic C#
concepts and .NET type formatting. If you are new to string interpolation or .NET type
formatting, check out the interactive string interpolation tutorial first. For more
information about formatting types in .NET, see Formatting types in .NET.
Introduction
To identify a string literal as an interpolated string, prepend it with the $ symbol. You
can embed any valid C# expression that returns a value in an interpolated string. In the
following example, as soon as an expression is evaluated, its result is converted into a
string and included in a result string:
C#
double a = 3;
double b = 4;
Console.WriteLine($"Area of the right triangle with legs of {a} and {b} is
{0.5 * a * b}");
Console.WriteLine($"Length of the hypotenuse of the right triangle with legs
of {a} and {b} is {CalculateHypotenuse(a, b)}");
double CalculateHypotenuse(double leg1, double leg2) => Math.Sqrt(leg1 *
leg1 + leg2 * leg2);
// Output:
// Area of the right triangle with legs of 3 and 4 is 6
// Length of the hypotenuse of the right triangle with legs of 3 and 4 is 5
C#
{<interpolationExpression>}
Interpolated strings support all the capabilities of the string composite formatting
feature. That makes them a more readable alternative to the use of the String.Format
method.
How to specify a format string for an
interpolation expression
To specify a format string that is supported by the type of the expression result, follow
the interpolation expression with a colon (":") and the format string:
C#
{<interpolationExpression>:<formatString>}
The following example shows how to specify standard and custom format strings for
expressions that produce date and time or numeric results:
C#
For more information, see the Format string component section of the Composite
formatting article.
C#
{<interpolationExpression>,<alignment>}
If you need to specify both alignment and a format string, start with the alignment
component:
C#
{<interpolationExpression>,<alignment>:<formatString>}
The following example shows how to specify alignment and uses pipe characters ("|") to
delimit text fields:
C#
As the example output shows, if the length of the formatted expression result exceeds
specified field width, the alignment value is ignored.
For more information, see the Alignment component section of the Composite
formatting article.
To include a brace, "{" or "}", in a result string, use two braces, "{{" or "}}". For more
information, see the Escaping braces section of the Composite formatting article.
The following example shows how to include braces in a result string and construct a
verbatim interpolated string:
C#
Beginning with C# 11, you can use interpolated raw string literals.
C#
C#
C#
As the example shows, you can use one FormattableString instance to generate multiple
result strings for various cultures.
C#
C#
Conclusion
This tutorial describes common scenarios of string interpolation usage. For more
information about string interpolation, see String interpolation. For more information
about formatting types in .NET, see the Formatting types in .NET and Composite
formatting articles.
See also
String.Format
System.FormattableString
System.IFormattable
Strings
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Console app
Article • 03/14/2023
This tutorial teaches you a number of features in .NET and the C# language. You'll learn:
You'll build an application that reads a text file, and echoes the contents of that text file
to the console. The output to the console is paced to match reading it aloud. You can
speed up or slow down the pace by pressing the '<' (less than) or '>' (greater than) keys.
You can run this application on Windows, Linux, macOS, or in a Docker container.
There are a lot of features in this tutorial. Let's build them one by one.
Prerequisites
.NET 6 SDK .
A code editor.
Before you start making modifications, let's run the simple Hello World application. After
creating the application, type dotnet run at the command prompt. This command runs
the NuGet package restore process, creates the application executable, and runs the
executable.
The simple Hello World application code is all in Program.cs. Open that file with your
favorite text editor. Replace the code in Program.cs with the following code:
C#
namespace TeleprompterConsole;
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
At the top of the file, see a namespace statement. Like other Object Oriented languages
you may have used, C# uses namespaces to organize types. This Hello World program is
no different. You can see that the program is in the namespace with the name
TeleprompterConsole .
Next, add the following method in your Program class (right below the Main method):
C#
This method is a special type of C# method called an iterator method. Iterator methods
return sequences that are evaluated lazily. That means each item in the sequence is
generated as it is requested by the code consuming the sequence. Iterator methods are
methods that contain one or more yield return statements. The object returned by the
ReadFrom method contains the code to generate each item in the sequence. In this
example, that involves reading the next line of text from the source file, and returning
that string. Each time the calling code requests the next item from the sequence, the
code reads the next line of text from the file and returns it. When the file is completely
read, the sequence indicates that there are no more items.
There are two C# syntax elements that may be new to you. The using statement in this
method manages resource cleanup. The variable that is initialized in the using
statement ( reader , in this example) must implement the IDisposable interface. That
interface defines a single method, Dispose , that should be called when the resource
should be released. The compiler generates that call when execution reaches the closing
brace of the using statement. The compiler-generated code ensures that the resource is
released even if an exception is thrown from the code in the block defined by the using
statement.
The reader variable is defined using the var keyword. var defines an implicitly typed
local variable. That means the type of the variable is determined by the compile-time
type of the object assigned to the variable. Here, that is the return value from the
OpenText(String) method, which is a StreamReader object.
Now, let's fill in the code to read the file in the Main method:
C#
Run the program (using dotnet run ) and you can see every line printed out to the
console.
There are two steps to this section. First, you'll update the iterator method to return
single words instead of entire lines. That's done with these modifications. Replace the
yield return line; statement with the following code:
C#
var words = line.Split(' ');
foreach (var word in words)
{
yield return word + " ";
}
yield return Environment.NewLine;
Next, you need to modify how you consume the lines of the file, and add a delay after
writing each word. Replace the Console.WriteLine(line) statement in the Main method
with the following block:
C#
Console.Write(line);
if (!string.IsNullOrWhiteSpace(line))
{
var pause = Task.Delay(200);
// Synchronously waiting on a task is an
// anti-pattern. This will get fixed in later
// steps.
pause.Wait();
}
Run the sample, and check the output. Now, each single word is printed, followed by a
200 ms delay. However, the displayed output shows some issues because the source
text file has several lines that have more than 80 characters without a line break. That
can be hard to read while it's scrolling by. That's easy to fix. You'll just keep track of the
length of each line, and generate a new line whenever the line length reaches a certain
threshold. Declare a local variable after the declaration of words in the ReadFrom method
that holds the line length:
C#
var lineLength = 0;
Then, add the following code after the yield return word + " "; statement (before the
closing brace):
C#
lineLength += word.Length + 1;
if (lineLength > 70)
{
yield return Environment.NewLine;
lineLength = 0;
}
Run the sample, and you'll be able to read aloud at its pre-configured pace.
Async Tasks
In this final step, you'll add the code to write the output asynchronously in one task,
while also running another task to read input from the user if they want to speed up or
slow down the text display, or stop the text display altogether. This has a few steps in it
and by the end, you'll have all the updates that you need. The first step is to create an
asynchronous Task returning method that represents the code you've created so far to
read and display the file.
Add this method to your Program class (it's taken from the body of your Main method):
C#
You'll notice two changes. First, in the body of the method, instead of calling Wait() to
synchronously wait for a task to finish, this version uses the await keyword. In order to
do that, you need to add the async modifier to the method signature. This method
returns a Task . Notice that there are no return statements that return a Task object.
Instead, that Task object is created by code the compiler generates when you use the
await operator. You can imagine that this method returns when it reaches an await .
The returned Task indicates that the work has not completed. The method resumes
when the awaited task completes. When it has executed to completion, the returned
Task indicates that it is complete. Calling code can monitor that returned Task to
await ShowTeleprompter();
C#
Learn more about the async Main method in our fundamentals section.
Next, you need to write the second asynchronous method to read from the Console and
watch for the '<' (less than), '>' (greater than) and 'X' or 'x' keys. Here's the method you
add for that task:
C#
This creates a lambda expression to represent an Action delegate that reads a key from
the Console and modifies a local variable representing the delay when the user presses
the '<' (less than) or '>' (greater than) keys. The delegate method finishes when user
presses the 'X' or 'x' keys, which allow the user to stop the text display at any time. This
method uses ReadKey() to block and wait for the user to press a key.
To finish this feature, you need to create a new async Task returning method that starts
both of these tasks ( GetInput and ShowTeleprompter ), and also manages the shared data
between these two tasks.
It's time to create a class that can handle the shared data between these two tasks. This
class contains two public properties: the delay, and a flag Done to indicate that the file
has been completely read:
C#
namespace TeleprompterConsole;
Put that class in a new file, and include that class in the TeleprompterConsole namespace
as shown. You'll also need to add a using static statement at the top of the file so that
you can reference the Min and Max methods without the enclosing class or namespace
names. A using static statement imports the methods from one class. This is in contrast
with the using statement without static , which imports all classes from a namespace.
C#
Next, you need to update the ShowTeleprompter and GetInput methods to use the new
config object. Write one final Task returning async method to start both tasks and exit
C#
The one new method here is the WhenAny(Task[]) call. That creates a Task that finishes
as soon as any of the tasks in its argument list completes.
Next, you need to update both the ShowTeleprompter and GetInput methods to use the
config object for the delay:
C#
await RunTeleprompter();
Conclusion
This tutorial showed you a number of the features around the C# language and the .NET
Core libraries related to working in Console applications. You can build on this
knowledge to explore more about the language, and the classes introduced here. You've
seen the basics of File and Console I/O, blocking and non-blocking use of the Task-
based asynchronous programming, a tour of the C# language and how C# programs are
organized, and the .NET CLI.
For more information about File I/O, see File and Stream I/O. For more information
about asynchronous programming model used in this tutorial, see Task-based
Asynchronous Programming and Asynchronous programming.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Tutorial: Make HTTP requests in a .NET
console app using C#
Article • 10/29/2022
This tutorial builds an app that issues HTTP requests to a REST service on GitHub. The
app reads information in JSON format and converts the JSON into C# objects.
Converting from JSON to C# objects is known as deserialization.
If you prefer to follow along with the final sample for this tutorial, you can download it.
For download instructions, see Samples and Tutorials.
Prerequisites
.NET SDK 6.0 or later
A code editor such as [Visual Studio Code (an open-source, cross-platform
editor). You can run the sample app on Windows, Linux, or macOS, or in a Docker
container.
.NET CLI
This command creates the starter files for a basic "Hello World" app. The project
name is "WebAPIClient".
.NET CLI
cd WebAPIClient
.NET CLI
dotnet run
dotnet run automatically runs dotnet restore to restore any dependencies that the
app needs. It also runs dotnet build if needed. You should see the app output
"Hello, World!" . In your terminal, press Ctrl + C to stop the app.
Use the HttpClient class to make HTTP requests. HttpClient supports only async
methods for its long-running APIs. So the following steps create an async method and
call it from the Main method.
1. Open the Program.cs file in your project directory and replace its contents with the
following:
C#
await ProcessRepositoriesAsync();
This code:
using System.Net.Http.Headers;
await ProcessRepositoriesAsync(client);
This code:
C#
Console.Write(json);
}
This code:
dotnet run
C#
The preceding code defines a class to represent the JSON object returned from the
GitHub API. You'll use this class to display a list of repository names.
The JSON for a repository object contains dozens of properties, but only the name
property will be deserialized. The serializer automatically ignores JSON properties
for which there is no match in the target class. This feature makes it easier to
create types that work with only a subset of fields in a large JSON packet.
The C# convention is to capitalize the first letter of property names, but the name
property here starts with a lowercase letter because that matches exactly what's in
the JSON. Later you'll see how to use C# property names that don't match the
JSON property names.
2. Use the serializer to convert JSON into C# objects. Replace the call to
GetStringAsync(String) in the ProcessRepositoriesAsync method with the following
lines:
C#
The DeserializeAsync method is generic, which means you supply type arguments
for what kind of objects should be created from the JSON text. In this example,
you're deserializing to a List<Repository> , which is another generic object, a
System.Collections.Generic.List<T>. The List<T> class stores a collection of
objects. The type argument declares the type of objects stored in the List<T> . The
type argument is your Repository record, because the JSON text represents a
collection of repository objects.
3. Add code to display the name of each repository. Replace the lines that read:
C#
Console.Write(json);
C#
4. The following using directives should be present at the top of the file:
C#
using System.Net.Http.Headers;
using System.Text.Json;
.NET CLI
dotnet run
The output is a list of the names of the repositories that are part of the .NET
Foundation.
Configure deserialization
1. In Repository.cs, replace the file contents with the following C#.
C#
using System.Text.Json.Serialization;
This code:
2. In Program.cs, update the code to use the new capitalization of the Name property:
C#
C#
C#
The compiler generates the Task<T> object for the return value because you've
marked this method as async .
3. Modify the Program.cs file, replacing the call to ProcessRepositoriesAsync with the
following to capture the results and write each repository name to the console.
C#
1. Replace the contents of Repository class, with the following record definition:
C#
using System.Text.Json.Serialization;
The Uri and int types have built-in functionality to convert to and from string
representation. No extra code is needed to deserialize from JSON string format to
those target types. If the JSON packet contains data that doesn't convert to a
target type, the serialization action throws an exception.
2. Update the foreach loop in the Program.cs file to display the property values:
C#
JSON
2016-02-08T21:27:00Z
This format is for Coordinated Universal Time (UTC), so the result of deserialization is a
DateTime value whose Kind property is Utc.
To get a date and time represented in your time zone, you have to write a custom
conversion method.
1. In Repository.cs, add a property for the UTC representation of the date and time
and a readonly LastPush property that returns the date converted to local time,
the file should look like the following:
C#
using System.Text.Json.Serialization;
The LastPush property is defined using an expression-bodied member for the get
accessor. There's no set accessor. Omitting the set accessor is one way to define
a read-only property in C#. (Yes, you can create write-only properties in C#, but
their value is limited.)
C#
C#
using System.Net.Http.Headers;
using System.Text.Json;
The output includes the date and time of the last push to each repository.
Next steps
In this tutorial, you created an app that makes web requests and parses the results. Your
version of the app should now match the finished sample.
Learn more about how to configure JSON serialization in How to serialize and
deserialize (marshal and unmarshal) JSON in .NET.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Work with Language-Integrated Query
(LINQ)
Article • 09/15/2021
Introduction
This tutorial teaches you features in .NET Core and the C# language. You’ll learn how to:
You'll learn these techniques by building an application that demonstrates one of the
basic skills of any magician: the faro shuffle . Briefly, a faro shuffle is a technique where
you split a card deck exactly in half, then the shuffle interleaves each one card from each
half to rebuild the original deck.
Magicians use this technique because every card is in a known location after each
shuffle, and the order is a repeating pattern.
For your purposes, it is a light hearted look at manipulating sequences of data. The
application you'll build constructs a card deck and then performs a sequence of shuffles,
writing the sequence out each time. You'll also compare the updated order to the
original order.
This tutorial has multiple steps. After each step, you can run the application and see the
progress. You can also see the completed sample in the dotnet/samples GitHub
repository. For download instructions, see Samples and Tutorials.
Prerequisites
You’ll need to set up your machine to run .NET core. You can find the installation
instructions on the .NET Core Download page. You can run this application on
Windows, Ubuntu Linux, or OS X, or in a Docker container. You’ll need to install your
favorite code editor. The descriptions below use Visual Studio Code which is an open
source, cross-platform editor. However, you can use whatever tools you are comfortable
with.
If you've never used C# before, this tutorial explains the structure of a C# program. You
can read that and then return here to learn more about LINQ.
C#
// Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
If these three lines ( using directives) aren't at the top of the file, your program might
not compile.
Now that you have all of the references that you'll need, consider what constitutes a
deck of cards. Commonly, a deck of playing cards has four suits, and each suit has
thirteen values. Normally, you might consider creating a Card class right off the bat and
populating a collection of Card objects by hand. With LINQ, you can be more concise
than the usual way of dealing with creating a deck of cards. Instead of creating a Card
class, you can create two sequences to represent suits and ranks, respectively. You'll
create a really simple pair of iterator methods that will generate the ranks and suits as
IEnumerable<T>s of strings:
C#
// Program.cs
// The Main() method
Place these underneath the Main method in your Program.cs file. These two methods
both utilize the yield return syntax to produce a sequence as they run. The compiler
builds an object that implements IEnumerable<T> and generates the sequence of
strings as they are requested.
Now, use these iterator methods to create the deck of cards. You'll place the LINQ query
in our Main method. Here's a look at it:
C#
// Program.cs
static void Main(string[] args)
{
var startingDeck = from s in Suits()
from r in Ranks()
select new { Suit = s, Rank = r };
The multiple from clauses produce a SelectMany, which creates a single sequence from
combining each element in the first sequence with each element in the second
sequence. The order is important for our purposes. The first element in the first source
sequence (Suits) is combined with every element in the second sequence (Ranks). This
produces all thirteen cards of first suit. That process is repeated with each element in the
first sequence (Suits). The end result is a deck of cards ordered by suits, followed by
values.
It's important to keep in mind that whether you choose to write your LINQ in the query
syntax used above or use method syntax instead, it's always possible to go from one
form of syntax to the other. The above query written in query syntax can be written in
method syntax as:
C#
The compiler translates LINQ statements written with query syntax into the equivalent
method call syntax. Therefore, regardless of your syntax choice, the two versions of the
query produce the same result. Choose which syntax works best for your situation: for
instance, if you're working in a team where some of the members have difficulty with
method syntax, try to prefer using query syntax.
Go ahead and run the sample you've built at this point. It will display all 52 cards in the
deck. You may find it very helpful to run this sample under a debugger to observe how
the Suits() and Ranks() methods execute. You can clearly see that each string in each
sequence is generated only as it is needed.
C#
// Program.cs
public static void Main(string[] args)
{
var startingDeck = from s in Suits()
from r in Ranks()
select new { Suit = s, Rank = r };
// 52 cards in a deck, so 52 / 2 = 26
var top = startingDeck.Take(26);
var bottom = startingDeck.Skip(26);
}
In order to add some functionality to how you interact with the IEnumerable<T> you'll
get back from LINQ queries, you'll need to write some special kinds of methods called
extension methods. Briefly, an extension method is a special purpose static method that
adds new functionality to an already-existing type without having to modify the original
type you want to add functionality to.
Give your extension methods a new home by adding a new static class file to your
program called Extensions.cs , and then start building out the first extension method:
C#
// Extensions.cs
using System;
using System.Collections.Generic;
using System.Linq;
namespace LinqFaroShuffle
{
public static class Extensions
{
public static IEnumerable<T> InterleaveSequenceWith<T>(this
IEnumerable<T> first, IEnumerable<T> second)
{
// Your implementation will go here soon enough
}
}
}
C#
You can see the addition of the this modifier on the first argument to the method. That
means you call the method as though it were a member method of the type of the first
argument. This method declaration also follows a standard idiom where the input and
output types are IEnumerable<T> . That practice enables LINQ methods to be chained
together to perform more complex queries.
Naturally, since you split the deck into halves, you'll need to join those halves together.
In code, this means you'll be enumerating both of the sequences you acquired through
Take and Skip at once, interleaving the elements, and creating one sequence: your
now-shuffled deck of cards. Writing a LINQ method that works with two sequences
requires that you understand how IEnumerable<T> works.
The IEnumerable<T> interface has one method: GetEnumerator. The object returned by
GetEnumerator has a method to move to the next element, and a property that retrieves
the current element in the sequence. You will use those two members to enumerate the
collection and return the elements. This Interleave method will be an iterator method, so
instead of building a collection and returning the collection, you'll use the yield return
syntax shown above.
C#
Now that you've written this method, go back to the Main method and shuffle the deck
once:
C#
// Program.cs
public static void Main(string[] args)
{
var startingDeck = from s in Suits()
from r in Ranks()
select new { Suit = s, Rank = r };
Comparisons
How many shuffles it takes to set the deck back to its original order? To find out, you'll
need to write a method that determines if two sequences are equal. After you have that
method, you'll need to place the code that shuffles the deck in a loop, and check to see
when the deck is back in order.
C#
public static bool SequenceEquals<T>
(this IEnumerable<T> first, IEnumerable<T> second)
{
var firstIter = first.GetEnumerator();
var secondIter = second.GetEnumerator();
return true;
}
This shows a second LINQ idiom: terminal methods. They take a sequence as input (or in
this case, two sequences), and return a single scalar value. When using terminal
methods, they are always the final method in a chain of methods for a LINQ query,
hence the name "terminal".
You can see this in action when you use it to determine when the deck is back in its
original order. Put the shuffle code inside a loop, and stop when the sequence is back in
its original order by applying the SequenceEquals() method. You can see it would always
be the final method in any query, because it returns a single value instead of a
sequence:
C#
// Program.cs
static void Main(string[] args)
{
// Query for building the deck
var times = 0;
// We can re-use the shuffle variable from earlier, or you can make a
new one
shuffle = startingDeck;
do
{
shuffle = shuffle.Take(26).InterleaveSequenceWith(shuffle.Skip(26));
} while (!startingDeck.SequenceEquals(shuffle));
Console.WriteLine(times);
}
Run the code you've got so far and take note of how the deck rearranges on each
shuffle. After 8 shuffles (iterations of the do-while loop), the deck returns to the original
configuration it was in when you first created it from the starting LINQ query.
Optimizations
The sample you've built so far executes an out shuffle, where the top and bottom cards
stay the same on each run. Let's make one change: we'll use an in shuffle instead, where
all 52 cards change position. For an in shuffle, you interleave the deck so that the first
card in the bottom half becomes the first card in the deck. That means the last card in
the top half becomes the bottom card. This is a simple change to a singular line of code.
Update the current shuffle query by switching the positions of Take and Skip. This will
change the order of the top and bottom halves of the deck:
C#
shuffle = shuffle.Skip(26).InterleaveSequenceWith(shuffle.Take(26));
Run the program again, and you'll see that it takes 52 iterations for the deck to reorder
itself. You'll also start to notice some serious performance degradations as the program
continues to run.
There are a number of reasons for this. You can tackle one of the major causes of this
performance drop: inefficient use of lazy evaluation.
Briefly, lazy evaluation states that the evaluation of a statement is not performed until its
value is needed. LINQ queries are statements that are evaluated lazily. The sequences
are generated only as the elements are requested. Usually, that's a major benefit of
LINQ. However, in a use such as this program, this causes exponential growth in
execution time.
Remember that we generated the original deck using a LINQ query. Each shuffle is
generated by performing three LINQ queries on the previous deck. All these are
performed lazily. That also means they are performed again each time the sequence is
requested. By the time you get to the 52nd iteration, you're regenerating the original
deck many, many times. Let's write a log to demonstrate this behavior. Then, you'll fix it.
In your Extensions.cs file, type in or copy the method below. This extension method
creates a new file called debug.log within your project directory and records what query
is currently being executed to the log file. This extension method can be appended to
any query to mark that the query executed.
C#
return sequence;
}
You will see a red squiggle under File , meaning it doesn't exist. It won't compile, since
the compiler doesn't know what File is. To solve this problem, make sure to add the
following line of code under the very first line in Extensions.cs :
C#
using System.IO;
This should solve the issue and the red error disappears.
C#
// Program.cs
public static void Main(string[] args)
{
var startingDeck = (from s in Suits().LogQuery("Suit Generation")
from r in Ranks().LogQuery("Rank Generation")
select new { Suit = s, Rank = r
}).LogQuery("Starting Deck");
do
{
// Out shuffle
/*
shuffle = shuffle.Take(26)
.LogQuery("Top Half")
.InterleaveSequenceWith(shuffle.Skip(26)
.LogQuery("Bottom Half"))
.LogQuery("Shuffle");
*/
// In shuffle
shuffle = shuffle.Skip(26).LogQuery("Bottom Half")
.InterleaveSequenceWith(shuffle.Take(26).LogQuery("Top
Half"))
.LogQuery("Shuffle");
times++;
Console.WriteLine(times);
} while (!startingDeck.SequenceEquals(shuffle));
Console.WriteLine(times);
}
Notice that you don't log every time you access a query. You log only when you create
the original query. The program still takes a long time to run, but now you can see why.
If you run out of patience running the in shuffle with logging turned on, switch back to
the out shuffle. You'll still see the lazy evaluation effects. In one run, it executes 2592
queries, including all the value and suit generation.
You can improve the performance of the code here to reduce the number of executions
you make. A simple fix you can make is to cache the results of the original LINQ query
that constructs the deck of cards. Currently, you're executing the queries again and
again every time the do-while loop goes through an iteration, re-constructing the deck
of cards and reshuffling it every time. To cache the deck of cards, you can leverage the
LINQ methods ToArray and ToList; when you append them to the queries, they'll
perform the same actions you've told them to, but now they'll store the results in an
array or a list, depending on which method you choose to call. Append the LINQ
method ToArray to both queries and run the program again:
C#
Console.WriteLine();
var times = 0;
var shuffle = startingDeck;
do
{
/*
shuffle = shuffle.Take(26)
.LogQuery("Top Half")
.InterleaveSequenceWith(shuffle.Skip(26).LogQuery("Bottom
Half"))
.LogQuery("Shuffle")
.ToArray();
*/
shuffle = shuffle.Skip(26)
.LogQuery("Bottom Half")
.InterleaveSequenceWith(shuffle.Take(26).LogQuery("Top Half"))
.LogQuery("Shuffle")
.ToArray();
times++;
Console.WriteLine(times);
} while (!startingDeck.SequenceEquals(shuffle));
Console.WriteLine(times);
}
Now the out shuffle is down to 30 queries. Run again with the in shuffle and you'll see
similar improvements: it now executes 162 queries.
Please note that this example is designed to highlight the use cases where lazy
evaluation can cause performance difficulties. While it's important to see where lazy
evaluation can impact code performance, it's equally important to understand that not
all queries should run eagerly. The performance hit you incur without using ToArray is
because each new arrangement of the deck of cards is built from the previous
arrangement. Using lazy evaluation means each new deck configuration is built from the
original deck, even executing the code that built the startingDeck . That causes a large
amount of extra work.
In practice, some algorithms run well using eager evaluation, and others run well using
lazy evaluation. For daily usage, lazy evaluation is usually a better choice when the data
source is a separate process, like a database engine. For databases, lazy evaluation
allows more complex queries to execute only one round trip to the database process
and back to the rest of your code. LINQ is flexible whether you choose to utilize lazy or
eager evaluation, so measure your processes and pick whichever kind of evaluation
gives you the best performance.
Conclusion
In this project, you covered:
Aside from LINQ, you learned a bit about a technique magicians use for card tricks.
Magicians use the Faro shuffle because they can control where every card moves in the
deck. Now that you know, don't spoil it for everyone else!
Introduction to LINQ
Basic LINQ Query Operations (C#)
Data Transformations With LINQ (C#)
Query Syntax and Method Syntax in LINQ (C#)
C# Features That Support LINQ
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Language Integrated Query (LINQ)
Article • 12/15/2023
Language-Integrated Query (LINQ) is the name for a set of technologies based on the
integration of query capabilities directly into the C# language. Traditionally, queries
against data are expressed as simple strings without type checking at compile time or
IntelliSense support. Furthermore, you have to learn a different query language for each
type of data source: SQL databases, XML documents, various Web services, and so on.
With LINQ, a query is a first-class language construct, just like classes, methods, and
events.
When you write queries, the most visible "language-integrated" part of LINQ is the
query expression. Query expressions are written in a declarative query syntax. By using
query syntax, you perform filtering, ordering, and grouping operations on data sources
with a minimum of code. You use the same query expression patterns to query and
transform data from any type of data source.
The following example shows a complete query operation. The complete operation
includes creating a data source, defining the query expression, and executing the query
in a foreach statement.
C#
// Output: 97 92 81
You might need to add a using directive, using System.Linq; , for the preceding example
to compile. The most recent versions of .NET make use of implicit usings to add this
directive as a global using. Older versions require you to add it in your source.
Query expression overview
Query expressions query and transform data from any LINQ-enabled data source.
For example, a single query can retrieve data from an SQL database and produce
an XML stream as output.
Query expressions use many familiar C# language constructs, which make them
easy to read.
The variables in a query expression are all strongly typed.
A query isn't executed until you iterate over the query variable, for example in a
foreach statement.
In-memory data
There are two ways you enable LINQ querying of in-memory data. If the data is of a type
that implements IEnumerable<T>, you query the data by using LINQ to Objects. If it
doesn't make sense to enable enumeration by implementing the IEnumerable<T>
interface, you define LINQ standard query operator methods, either in that type or as
extension methods for that type. Custom implementations of the standard query
operators should use deferred execution to return the results.
Remote data
The best option for enabling LINQ querying of a remote data source is to implement the
IQueryable<T> interface.
A less complex IQueryable provider might access a single method from a Web service.
This type of provider is very specific because it expects specific information in the
queries that it handles. It has a closed type system, perhaps exposing a single result
type. Most of the execution of the query occurs locally, for example by using the
Enumerable implementations of the standard query operators. A less complex provider
might examine only one method call expression in the expression tree that represents
the query, and let the remaining logic of the query be handled elsewhere.
An IQueryable provider of medium complexity might target a data source that has a
partially expressive query language. If it targets a Web service, it might access more than
one method of the Web service and select which method to call based on the
information that the query seeks. A provider of medium complexity would have a richer
type system than a simple provider, but it would still be a fixed type system. For
example, the provider might expose types that have one-to-many relationships that can
be traversed, but it wouldn't provide mapping technology for user-defined types.
A complex IQueryable provider, such as the Entity Framework Core provider, might
translate complete LINQ queries to an expressive query language, such as SQL. A
complex provider is more general because it can handle a wider variety of questions in
the query. It also has an open type system and therefore must contain extensive
infrastructure to map user-defined types. Developing a complex provider requires a
significant amount of effort.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Introduction to LINQ Queries in C#
Article • 04/25/2024
A query is an expression that retrieves data from a data source. Different data sources
have different native query languages, for example SQL for relational databases and
XQuery for XML. Developers must learn a new query language for each type of data
source or data format that they must support. LINQ simplifies this situation by offering a
consistent C# language model for kinds of data sources and formats. In a LINQ query,
you always work with C# objects. You use the same basic coding patterns to query and
transform data in XML documents, SQL databases, .NET collections, and any other
format when a LINQ provider is available.
The following example shows how the three parts of a query operation are expressed in
source code. The example uses an integer array as a data source for convenience;
however, the same concepts apply to other data sources also. This example is referred
to throughout the rest of this article.
C#
// 2. Query creation.
// numQuery is an IEnumerable<int>
var numQuery =
from num in numbers
where (num % 2) == 0
select num;
// 3. Query execution.
foreach (int num in numQuery)
{
Console.Write("{0,1} ", num);
}
The following illustration shows the complete query operation. In LINQ, the execution of
the query is distinct from the query itself. In other words, you don't retrieve any data by
creating a query variable.
C#
C#
For more information about how to create specific types of data sources, see the
documentation for the various LINQ providers. However, the basic rule is simple: a LINQ
data source is any object that supports the generic IEnumerable<T> interface, or an
interface that inherits from it, typically IQueryable<T>.
7 Note
Types such as ArrayList that support the non-generic IEnumerable interface can
also be used as a LINQ data source. For more information, see How to query an
ArrayList with LINQ (C#).
The Query
The query specifies what information to retrieve from the data source or sources.
Optionally, a query also specifies how that information should be sorted, grouped, and
shaped before being returned. A query is stored in a query variable and initialized with a
query expression. You use C# query syntax to write queries.
The query in the previous example returns all the even numbers from the integer array.
The query expression contains three clauses: from , where , and select . (If you're familiar
with SQL, you noticed that the ordering of the clauses is reversed from the order in
SQL.) The from clause specifies the data source, the where clause applies the filter, and
the select clause specifies the type of the returned elements. All the query clauses are
discussed in detail in this section. For now, the important point is that in LINQ, the query
variable itself takes no action and returns no data. It just stores the information that is
required to produce the results when the query is executed at some later point. For
more information about how queries are constructed, see Standard Query Operators
Overview (C#).
7 Note
Queries can also be expressed by using method syntax. For more information, see
Query Syntax and Method Syntax in LINQ.
Immediate
Immediate execution means that the data source is read and the operation is performed
once. All the standard query operators that return a scalar result execute immediately.
Examples of such queries are Count , Max , Average , and First . These methods execute
without an explicit foreach statement because the query itself must use foreach in
order to return a result. These queries return a single value, not an IEnumerable
collection. You can force any query to execute immediately using the Enumerable.ToList
or Enumerable.ToArray methods. Immediate execution provides reuse of query results,
not query declaration. The results are retrieved once, then stored for future use. The
following query returns a count of the even numbers in the source array:
C#
var evenNumQuery =
from num in numbers
where (num % 2) == 0
select num;
To force immediate execution of any query and cache its results, you can call the ToList
or ToArray methods.
C#
List<int> numQuery2 =
(from num in numbers
where (num % 2) == 0
select num).ToList();
// or like this:
// numQuery3 is still an int[]
var numQuery3 =
(from num in numbers
where (num % 2) == 0
select num).ToArray();
You can also force execution by putting the foreach loop immediately after the query
expression. However, by calling ToList or ToArray you also cache all the data in a single
collection object.
Deferred
Deferred execution means that the operation isn't performed at the point in the code
where the query is declared. The operation is performed only when the query variable is
enumerated, for example by using a foreach statement. The results of executing the
query depend on the contents of the data source when the query is executed rather
than when the query is defined. If the query variable is enumerated multiple times, the
results might differ every time. Almost all the standard query operators whose return
type is IEnumerable<T> or IOrderedEnumerable<TElement> execute in a deferred
manner. Deferred execution provides the facility of query reuse since the query fetches
the updated data from the data source each time query results are iterated. The
following code shows an example of deferred execution:
C#
The foreach statement is also where the query results are retrieved. For example, in the
previous query, the iteration variable num holds each value (one at a time) in the
returned sequence.
Because the query variable itself never holds the query results, you can execute it
repeatedly to retrieve updated data. For example, a separate application might update a
database continually. In your application, you could create one query that retrieves the
latest data, and you could execute it at intervals to retrieve updated results.
Query operators that use deferred execution can be additionally classified as streaming
or nonstreaming.
Streaming
Streaming operators don't have to read all the source data before they yield elements.
At the time of execution, a streaming operator performs its operation on each source
element as it is read and yields the element if appropriate. A streaming operator
continues to read source elements until a result element can be produced. This means
that more than one source element might be read to produce one result element.
Nonstreaming
Nonstreaming operators must read all the source data before they can yield a result
element. Operations such as sorting or grouping fall into this category. At the time of
execution, nonstreaming query operators read all the source data, put it into a data
structure, perform the operation, and yield the resulting elements.
Classification table
The following table classifies each standard query operator method according to its
method of execution.
7 Note
If an operator is marked in two columns, two input sequences are involved in the
operation, and each sequence is evaluated differently. In these cases, it is always
the first sequence in the parameter list that is evaluated in a deferred, streaming
manner.
ノ Expand table
Aggregate TSource X
All Boolean X
Any Boolean X
AsEnumerable IEnumerable<T> X
Cast IEnumerable<T> X
Concat IEnumerable<T> X
Contains Boolean X
Count Int32 X
DefaultIfEmpty IEnumerable<T> X
Distinct IEnumerable<T> X
ElementAt TSource X
ElementAtOrDefault TSource? X
Empty IEnumerable<T> X
Except IEnumerable<T> X X
First TSource X
FirstOrDefault TSource? X
GroupBy IEnumerable<T> X
GroupJoin IEnumerable<T> X X
Intersect IEnumerable<T> X X
Join IEnumerable<T> X X
Last TSource X
LastOrDefault TSource? X
LongCount Int64 X
OfType IEnumerable<T> X
OrderBy IOrderedEnumerable<TElement> X
OrderByDescending IOrderedEnumerable<TElement> X
Standard query Return type Immediate Deferred Deferred
operator execution streaming nonstreaming
execution execution
Range IEnumerable<T> X
Repeat IEnumerable<T> X
Reverse IEnumerable<T> X
Select IEnumerable<T> X
SelectMany IEnumerable<T> X
SequenceEqual Boolean X
Single TSource X
SingleOrDefault TSource? X
Skip IEnumerable<T> X
SkipWhile IEnumerable<T> X
Take IEnumerable<T> X
TakeWhile IEnumerable<T> X
ThenBy IOrderedEnumerable<TElement> X
ThenByDescending IOrderedEnumerable<TElement> X
ToDictionary Dictionary<TKey,TValue> X
ToList IList<T> X
ToLookup ILookup<TKey,TElement> X
Union IEnumerable<T> X
Where IEnumerable<T> X
LINQ to objects
"LINQ to Objects" refers to the use of LINQ queries with any IEnumerable or
IEnumerable<T> collection directly. You can use LINQ to query any enumerable
collections, such as List<T>, Array, or Dictionary<TKey,TValue>. The collection can be
user-defined or a type returned by a .NET API. In the LINQ approach, you write
declarative code that describes what you want to retrieve. LINQ to Objects provides a
great introduction to programming with LINQ.
LINQ queries offer three main advantages over traditional foreach loops:
They're more concise and readable, especially when filtering multiple conditions.
They provide powerful filtering, ordering, and grouping capabilities with a
minimum of application code.
They can be ported to other data sources with little or no modification.
The more complex the operation you want to perform on the data, the more benefit you
realize using LINQ instead of traditional iteration techniques.
store its results without executing a foreach loop, just call one of the following methods
on the query variable:
ToList
ToArray
ToDictionary
ToLookup
You should assign the returned collection object to a new variable when you store the
query results, as shown in the following example:
C#
IEnumerable<int> queryFactorsOfFour =
from num in numbers
where num % 4 == 0
select num;
// Read and write from the newly created list to demonstrate that it holds
data.
Console.WriteLine(factorsofFourList[2]);
factorsofFourList[2] = 0;
Console.WriteLine(factorsofFourList[2]);
See also
Walkthrough: Writing Queries in C#
foreach, in
Query Keywords (LINQ)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Query expression basics
Article • 03/06/2024
This article introduces the basic concepts related to query expressions in C#.
Generally, the source data is organized logically as a sequence of elements of the same
kind. For example, an SQL database table contains a sequence of rows. In an XML file,
there's a "sequence" of XML elements (although XML elements are organized
hierarchically in a tree structure). An in-memory collection contains a sequence of
objects.
From an application's viewpoint, the specific type and structure of the original source
data isn't important. The application always sees the source data as an IEnumerable<T>
or IQueryable<T> collection. For example, in LINQ to XML, the source data is made
visible as an IEnumerable <XElement>.
C#
IEnumerable<int> highScoresQuery =
from score in scores
where score > 80
orderby score descending
select score;
IEnumerable<string> highScoresQuery2 =
from score in scores
where score > 80
orderby score descending
select $"The score is {score}";
The first element that matches a condition, or the sum of particular values in a
specified set of elements. For example, the following query returns the number
of scores greater than 80 from the scores integer array:
C#
var highScoreCount = (
from score in scores
where score > 80
select score
).Count();
In the previous example, note the use of parentheses around the query
expression before the call to the Enumerable.Count method. You can also use a
new variable to store the concrete result.
C#
IEnumerable<int> highScoresQuery3 =
from score in scores
where score > 80
select score;
In the previous example, the query is executed in the call to Count , because Count must
iterate over the results in order to determine the number of elements returned by
highScoresQuery .
A query expression must begin with a from clause and must end with a select or group
clause. Between the first from clause and the last select or group clause, it can contain
one or more of these optional clauses: where, orderby, join, let and even another from
clauses. You can also use the into keyword to enable the result of a join or group
clause to serve as the source for more query clauses in the same query expression.
Query variable
In LINQ, a query variable is any variable that stores a query instead of the results of a
query. More specifically, a query variable is always an enumerable type that produces a
sequence of elements when iterated over in a foreach statement or a direct call to its
IEnumerator.MoveNext() method.
7 Note
Examples in this article uses the following data source and sample data.
C#
C#
The following code example shows a simple query expression with one data source, one
filtering clause, one ordering clause, and no transformation of the source elements. The
select clause ends the query.
C#
// Data source.
int[] scores = [90, 71, 82, 93, 75, 82];
// Query Expression.
IEnumerable<int> scoreQuery = //query variable
from score in scores //required
where score > 80 // optional
orderby score descending // optional
select score; //must end with select or group
// Output: 93 90 82 82
In the previous example, scoreQuery is a query variable, which is sometimes referred to
as just a query. The query variable stores no actual result data, which is produced in the
foreach loop. And when the foreach statement executes, the query results aren't
returned through the query variable scoreQuery . Rather, they're returned through the
iteration variable testScore . The scoreQuery variable can be iterated in a second
foreach loop. It produces the same results as long as neither it nor the data source has
been modified.
A query variable might store a query that is expressed in query syntax or method syntax,
or a combination of the two. In the following examples, both queryMajorCities and
queryMajorCities2 are query variables:
C#
City[] cities = [
new City("Tokyo", 37_833_000),
new City("Delhi", 30_290_000),
new City("Shanghai", 27_110_000),
new City("São Paulo", 22_043_000)
];
//Query syntax
IEnumerable<City> queryMajorCities =
from city in cities
where city.Population > 100000
select city;
// Output:
// City { Population = 120000 }
// City { Population = 112000 }
// City { Population = 150340 }
// Method-based syntax
IEnumerable<City> queryMajorCities2 = cities.Where(c => c.Population >
100000);
On the other hand, the following two examples show variables that aren't query
variables even though each is initialized with a query. They aren't query variables
because they store results:
C#
var highestScore = (
from score in scores
select score
).Max();
C#
var largeCitiesList = (
from country in countries
from city in country.Cities
where city.Population > 10000
select city
).ToList();
C#
var queryCities =
from city in cities
where city.Population > 100000
select city;
In the preceding example, the use of var is optional. queryCities is an
IEnumerable<City> whether implicitly or explicitly typed.
C#
IEnumerable<Country> countryAreaQuery =
from country in countries
where country.Area > 500000 //sq km
select country;
The range variable is in scope until the query is exited either with a semicolon or with a
continuation clause.
A query expression might contain multiple from clauses. Use more from clauses when
each element in the source sequence is itself a collection or contains a collection. For
example, assume that you have a collection of Country objects, each of which contains a
collection of City objects named Cities . To query the City objects in each Country ,
use two from clauses as shown here:
C#
IEnumerable<City> cityQuery =
from country in countries
from city in country.Cities
where city.Population > 10000
select city;
C#
var queryCountryGroups =
from country in countries
group country by country.Name[0];
select clause
Use the select clause to produce all other types of sequences. A simple select clause
just produces a sequence of the same type of objects as the objects that are contained
in the data source. In this example, the data source contains Country objects. The
orderby clause just sorts the elements into a new order and the select clause produces
C#
IEnumerable<Country> sortedQuery =
from country in countries
orderby country.Area
select country;
The select clause can be used to transform source data into sequences of new types.
This transformation is also named a projection. In the following example, the select
clause projects a sequence of anonymous types that contains only a subset of the fields
in the original element. The new objects are initialized by using an object initializer.
C#
var queryNameAndPop =
from country in countries
select new
{
Name = country.Name,
Pop = country.Population
};
So in this example, the var is required because the query produces an anonymous type.
For more information about all the ways that a select clause can be used to transform
source data, see select clause.
groups are created, more clauses filter out some groups, and then to sort the groups in
ascending order. To perform those extra operations, the continuation represented by
countryGroup is required.
C#
where clause
Use the where clause to filter out elements from the source data based on one or more
predicate expressions. The where clause in the following example has one predicate with
two conditions.
C#
IEnumerable<City> queryCityPop =
from city in cities
where city.Population is < 200000 and > 100000
select city;
orderby clause
Use the orderby clause to sort the results in either ascending or descending order. You
can also specify secondary sort orders. The following example performs a primary sort
on the country objects by using the Area property. It then performs a secondary sort by
using the Population property.
C#
IEnumerable<Country> querySortedCountries =
from country in countries
orderby country.Area, country.Population descending
select country;
The ascending keyword is optional; it's the default sort order if no order is specified. For
more information, see orderby clause.
join clause
Use the join clause to associate and/or combine elements from one data source with
elements from another data source based on an equality comparison between specified
keys in each element. In LINQ, join operations are performed on sequences of objects
whose elements are different types. After you join two sequences, you must use a
select or group statement to specify which element to store in the output sequence.
You can also use an anonymous type to combine properties from each set of associated
elements into a new type for the output sequence. The following example associates
prod objects whose Category property matches one of the categories in the categories
string array. Products whose Category doesn't match any string in categories are
filtered out. The select statement projects a new type whose properties are taken from
both cat and prod .
C#
var categoryQuery =
from cat in categories
join prod in products on cat equals prod.Category
select new
{
Category = cat,
Name = prod.Name
};
You can also perform a group join by storing the results of the join operation into a
temporary variable by using the into keyword. For more information, see join clause.
let clause
Use the let clause to store the result of an expression, such as a method call, in a new
range variable. In the following example, the range variable firstName stores the first
element of the array of strings returned by Split .
C#
C#
var queryGroupMax =
from student in students
group student by student.Year into studentGroup
select new
{
Level = studentGroup.Key,
HighestScore = (
from student2 in studentGroup
select student2.ExamScores.Average()
).Max()
};
See also
Query keywords (LINQ)
Standard query operators overview
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Write C# LINQ queries to query data
Article • 04/25/2024
Most queries in the introductory Language Integrated Query (LINQ) documentation are
written by using the LINQ declarative query syntax. However, the query syntax must be
translated into method calls for the .NET common language runtime (CLR) when the
code is compiled. These method calls invoke the standard query operators, which have
names such as Where , Select , GroupBy , Join , Max , and Average . You can call them
directly by using method syntax instead of query syntax.
Query syntax and method syntax are semantically identical, but query syntax is often
simpler and easier to read. Some queries must be expressed as method calls. For
example, you must use a method call to express a query that retrieves the number of
elements that match a specified condition. You also must use a method call for a query
that retrieves the element that has the maximum value in a source sequence. The
reference documentation for the standard query operators in the System.Linq
namespace generally uses method syntax. You should become familiar with how to use
method syntax in queries and in query expressions themselves.
C#
//Query syntax:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;
//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n =>
n);
The output from the two examples is identical. You can see that the type of the query
variable is the same in both forms: IEnumerable<T>.
To understand the method-based query, let's examine it more closely. On the right side
of the expression, notice that the where clause is now expressed as an instance method
on the numbers object, which has a type of IEnumerable<int> . If you're familiar with the
generic IEnumerable<T> interface, you know that it doesn't have a Where method.
However, if you invoke the IntelliSense completion list in the Visual Studio IDE, you see
not only a Where method, but many other methods such as Select , SelectMany , Join ,
and Orderby . These methods implement the standard query operators.
To use extension methods, you bring them into scope with using directives. From your
application's point of view, an extension method and a regular instance method are the
same.
For more information about extension methods, see Extension Methods. For more
information about standard query operators, see Standard Query Operators Overview
(C#). Some LINQ providers, such as Entity Framework and LINQ to XML, implement their
own standard query operators and extension methods for other types besides
IEnumerable<T>.
Lambda expressions
In the previous example, notice that the conditional expression ( num % 2 == 0 ) is passed
as an in-line argument to the Enumerable.Where method: Where(num => num % 2 == 0).
This inline expression is a lambda expression. It's a convenient way to write code that
would otherwise have to be written in more cumbersome form. The num on the left of
the operator is the input variable, which corresponds to num in the query expression.
The compiler can infer the type of num because it knows that numbers is a generic
IEnumerable<T> type. The body of the lambda is just the same as the expression in
query syntax or in any other C# expression or statement. It can include method calls and
other complex logic. The return value is just the expression result. Certain queries can
only be expressed in method syntax and some of those require lambda expressions.
Lambda expressions are a powerful and flexible tool in your LINQ toolbox.
Composability of queries
In the previous code example, the Enumerable.OrderBy method is invoked by using the
dot operator on the call to Where . Where produces a filtered sequence, and then
Orderby sorts the sequence produced by Where . Because queries return an IEnumerable ,
you compose them in method syntax by chaining the method calls together. The
compiler does this composition when you write queries using query syntax. Because a
query variable doesn't store the results of the query, you can modify it or use it as the
basis for a new query at any time, even after you execute it.
The following examples demonstrate some simple LINQ queries by using each approach
listed previously.
7 Note
These queries operate on simple in-memory collections; however, the basic syntax
is identical to that used in LINQ to Entities and LINQ to XML.
// Query #1.
IEnumerable<int> filteringQuery =
from num in numbers
where num is < 3 or > 7
select num;
// Query #2.
IEnumerable<int> orderingQuery =
from num in numbers
where num is < 3 or > 7
orderby num ascending
select num;
// Query #3.
string[] groupingQuery = ["carrots", "cabbage", "broccoli", "beans",
"barley"];
IEnumerable<IGrouping<char, string>> queryFoodGroups =
from item in groupingQuery
group item by item[0];
The type of the queries is IEnumerable<T>. All of these queries could be written using
var as shown in the following example:
In each previous example, the queries don't actually execute until you iterate over the
query variable in a foreach statement or other statement.
C#
// Query #4.
double average = numbers1.Average();
// Query #5.
IEnumerable<int> concatenationQuery = numbers1.Concat(numbers2);
C#
// Query #6.
IEnumerable<int> largeNumbersQuery = numbers2.Where(c => c > 15);
In the previous queries, only Query #4 executes immediately, because it returns a single
value, and not a generic IEnumerable<T> collection. The method itself uses foreach or
similar code in order to compute its value.
Each of the previous queries can be written by using implicit typing with `var``, as shown
in the following example:
C#
C#
// Query #7.
Because Query #7 returns a single value and not a collection, the query executes
immediately.
The previous query can be written by using implicit typing with var , as follows:
C#
C#
C#
C#
var queryNames =
from student in students
where ids.Contains(student.ID)
select new
{
student.LastName,
student.ID
};
/* Output:
Garcia: 114
O'Donnell: 112
Omelchenko: 111
*/
/* Output:
Adams: 120
Feng: 117
Garcia: 115
Tucker: 122
*/
You can use control flow statements, such as if... else or switch , to select among
predetermined alternative queries. In the following example, studentQuery uses a
different where clause if the run-time value of oddYear is true or false .
C#
FilterByYearType(true);
/* Output:
The following students are at an odd year level:
Fakhouri: 116
Feng: 117
Garcia: 115
Mortensen: 113
Tucker: 119
Tucker: 122
*/
FilterByYearType(false);
/* Output:
The following students are at an even year level:
Adams: 120
Garcia: 114
Garcia: 118
O'Donnell: 112
Omelchenko: 111
Zabokritski: 121
*/
You can code defensively to avoid a null reference exception as shown in the following
example:
C#
var query1 =
from c in categories
where c != null
join p in products on c.ID equals p?.CategoryID
select new
{
Category = c.Name,
Name = p.Name
};
In the previous example, the where clause filters out all null elements in the categories
sequence. This technique is independent of the null check in the join clause. The
conditional expression with null in this example works because Products.CategoryID is
of type int? , which is shorthand for Nullable<int> .
In a join clause, if only one of the comparison keys is a nullable value type, you can cast
the other to a nullable value type in the query expression. In the following example,
assume that EmployeeID is a column that contains values of type int? :
C#
var query =
from o in db.Orders
join e in db.Employees
on o.EmployeeID equals (int?)e.EmployeeID
select new { o.OrderID, e.FirstName };
In each of the examples, the equals query keyword is used. You can also use pattern
matching, which includes patterns for is null and is not null . These patterns aren't
recommended in LINQ queries because query providers might not interpret the new C#
syntax correctly. A query provider is a library that translates C# query expressions into a
native data format, such as Entity Framework Core. Query providers implement the
System.Linq.IQueryProvider interface to create data sources that implement the
System.Linq.IQueryable<T> interface.
The final example shows how to handle those cases when you must throw an exception
during execution of a query.
The following example shows how to move exception handling code outside a query
expression. This refactoring is only possible when the method doesn't depend on any
variables local to the query. It's easier to deal with exceptions outside of the query
expression.
C#
In some cases, the best response to an exception that is thrown from within a query
might be to stop the query execution immediately. The following example shows how to
handle exceptions that might be thrown from inside a query body. Assume that
SomeMethodThatMightThrow can potentially cause an exception that requires the query
execution to stop.
The try block encloses the foreach loop, and not the query itself. The foreach loop is
the point at which the query is executed. Run-time exceptions are thrown when the
query is executed. Therefore they must be handled in the foreach loop.
C#
// Data source.
string[] files = ["fileA.txt", "fileB.txt", "fileC.txt"];
try
{
foreach (var item in exceptionDemoQuery)
{
Console.WriteLine($"Processing {item}");
}
}
catch (InvalidOperationException e)
{
Console.WriteLine(e.Message);
}
/* Output:
Processing C:\newFolder\fileA.txt
Processing C:\newFolder\fileB.txt
Operation is not valid due to the current state of the object.
*/
Remember to catch whatever exception you expect to raise and/or do any necessary
cleanup in a finally block.
See also
Walkthrough: Writing Queries in C#
where clause
Querying based on runtime state
Nullable<T>
Nullable value types
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Type Relationships in LINQ Query
Operations (C#)
Article • 12/15/2023
To write queries effectively, you should understand how types of the variables in a
complete query operation all relate to each other. If you understand these relationships
you will more easily comprehend the LINQ samples and code examples in the
documentation. Furthermore, you will understand what occurs when variables are
implicitly typed by using var.
LINQ query operations are strongly typed in the data source, in the query itself, and in
the query execution. The type of the variables in the query must be compatible with the
type of the elements in the data source and with the type of the iteration variable in the
foreach statement. This strong typing guarantees that type errors are caught at compile
time when they can be corrected before users encounter them.
In order to demonstrate these type relationships, most of the examples that follow use
explicit typing for all variables. The last example shows how the same principles apply
even when you use implicit typing by using var .
1. The type argument of the data source determines the type of the range variable.
2. The type of the object that is selected determines the type of the query variable.
Here name is a string. Therefore, the query variable is an IEnumerable<string> .
3. The query variable is iterated over in the foreach statement. Because the query
variable is a sequence of strings, the iteration variable is also a string.
1. The type argument of the data source determines the type of the range variable.
2. The select statement returns the Name property instead of the complete Customer
object. Because Name is a string, the type argument of custNameQuery is string ,
not Customer .
3. Because custNameQuery is a sequence of strings, the foreach loop's iteration
variable must also be a string .
The following illustration shows a slightly more complex transformation. The select
statement returns an anonymous type that captures just two members of the original
Customer object.
1. The type argument of the data source is always the type of the range variable in
the query.
2. Because the select statement produces an anonymous type, the query variable
must be implicitly typed by using var .
3. Because the type of the query variable is implicit, the iteration variable in the
foreach loop must also be implicit.
1. When you create an instance of a generic collection class such as List<T>, you
replace the "T" with the type of objects that the list will hold. For example, a list of
strings is expressed as List<string> , and a list of Customer objects is expressed as
List<Customer> . A generic list is strongly typed and provides many benefits over
collections that store their elements as Object. If you try to add a Customer to a
List<string> , you will get an error at compile time. It is easy to use generic
C#
IEnumerable<Customer> customerQuery =
from cust in customers
where cust.City == "London"
select cust;
C#
var customerQuery2 =
from cust in customers
where cust.City == "London"
select cust;
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
C# Features That Support LINQ
Article • 04/25/2024
Query Expressions
Query expressions use a declarative syntax similar to SQL or XQuery to query over
System.Collections.Generic.IEnumerable<T> collections. At compile time, query syntax is
converted to method calls to a LINQ provider's implementation of the standard query
methods. Applications control the standard query operators that are in scope by
specifying the appropriate namespace with a using directive. The following query
expression takes an array of strings, groups them according to the first character in the
string, and orders the groups.
C#
C#
var number = 5;
var name = "Virginia";
var query = from str in stringArray
where str[0] == 'm'
select str;
Variables declared as var are strongly typed, just like variables whose type you specify
explicitly. The use of var makes it possible to create anonymous types, but only for local
variables. For more information, see Implicitly Typed Local Variables.
C#
Continuing with your Customer class, assume that there's a data source called
IncomingOrders , and that for each order with a large OrderSize , you would like to create
a new Customer based off of that order. A LINQ query can be executed on this data
source and use object initialization to fill a collection:
C#
The data source might have more properties defined than the Customer class such as
OrderSize , but with object initialization, the data returned from the query is molded into
the desired data type; you choose the data that is relevant to your class. As a result, you
now have an System.Collections.Generic.IEnumerable<T> filled with the new Customer s
you wanted. The preceding example can also be written in LINQ's method syntax:
C#
Beginning with C# 12, you can use a collection expression to initialize a collection.
Anonymous Types
The compiler constructs an anonymous type. The type name is only available to the
compiler. Anonymous types provide a convenient way to group a set of properties
temporarily in a query result without having to define a separate named type.
Anonymous types are initialized with a new expression and an object initializer, as
shown here:
C#
Extension Methods
An extension method is a static method that can be associated with a type, so that it can
be called as if it were an instance method on the type. This feature enables you to, in
effect, "add" new methods to existing types without actually modifying them. The
standard query operators are a set of extension methods that provide LINQ query
functionality for any type that implements IEnumerable<T>.
Lambda Expressions
A lambda expressions is an inline function that uses the => operator to separate input
parameters from the function body and can be converted at compile time to a delegate
or an expression tree. In LINQ programming, you encounter lambda expressions when
you make direct method calls to the standard query operators.
Expressions as data
Query objects are composable, meaning that you can return a query from a method.
Objects that represent queries don't store the resulting collection, but rather the steps
to produce the results when needed. The advantage of returning query objects from
methods is that they can be further composed or modified. Therefore any return value
or out parameter of a method that returns a query must also have that type. If a
method materializes a query into a concrete List<T> or Array type, it returns the query
results instead of the query itself. A query variable that is returned from a method can
still be composed or modified.
In the following example, the first method QueryMethod1 returns a query as a return
value, and the second method QueryMethod2 returns a query as an out parameter
( returnQ in the example). In both cases, it's a query that is returned, not query results.
C#
C#
You also can execute the query returned from QueryMethod1 directly, without using
myQuery1 .
C#
Rest the mouse pointer over the call to QueryMethod1 to see its return type.
C#
You can modify a query by using query composition. In this case, the previous query
object is used to create a new query object. This new object returns different results
than the original query object.
C#
myQuery1 =
from item in myQuery1
orderby item descending
select item;
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Tutorial: Write queries in C# using
language integrated query (LINQ)
Article • 04/25/2024
In this tutorial, you create a data source and write several LINQ queries. You can
experiment with the query expressions and see the differences in the results. This
walkthrough demonstrates the C# language features that are used to write LINQ query
expressions. You can follow along and build the app and experiment with the queries
yourself. This article assumes you've installed the latest .NET SDK. If not, go to the .NET
Downloads page and install the latest version on your machine.
First, create the application. From the console, type the following command:
.NET CLI
Or, if you prefer Visual Studio, create a new console application named
WalkthroughWritingLinqQueries.
C#
namespace WalkthroughWritingLinqQueries;
public record Student(string First, string Last, int ID, int[] Scores);
Next, create a sequence of Student records that serves as the source of this query. Open
Program.cs, and remove the following boilerplate code:
C#
Replace it with the following code that creates a sequence of Student records:
C#
using WalkthroughWritingLinqQueries;
Try adding a few more students with different test scores to the list of students to get
more familiar with the code so far.
C#
The query's range variable, student , serves as a reference to each Student in the source,
providing member access for each object.
C#
// Output:
// Omelchenko, Svetlana
// Garcia, Cesar
// Fakhouri, Fadi
// Feng, Hanying
// Garcia, Hugo
// Adams, Terry
// Zabokritski, Eugene
// Tucker, Michael
To further refine the query, you can combine multiple Boolean conditions in the where
clause. The following code adds a condition so that the query returns those students
whose first score was over 90 and whose last score was less than 80. The where clause
should resemble the following code.
C#
Try the preceding where clause, or experiment yourself with other filter conditions. For
more information, see where clause.
C#
Now change the orderby clause so that it orders the results in reverse order according
to the score on the first test, from the highest score to the lowest score.
C#
Change the WriteLine format string so that you can see the scores:
C#
C#
The type of the query changed. It now produces a sequence of groups that have a char
type as a key, and a sequence of Student objects. The code in the foreach execution
loop also must change:
C#
Explicitly coding IEnumerables of IGroupings can quickly become tedious. Write the
same query and foreach loop much more conveniently by using var . The var keyword
doesn't change the types of your objects; it just instructs the compiler to infer the types.
Change the type of studentQuery and the iteration variable group to var and rerun the
query. In the inner foreach loop, the iteration variable is still typed as Student , and the
query works as before. Change the student iteration variable to var and run the query
again. You see that you get exactly the same results.
C#
For more information about var , see Implicitly Typed Local Variables.
C#
var studentQuery4 =
from student in students
group student by student.Last[0] into studentGroup
orderby studentGroup.Key
select studentGroup;
// Output:
//A
// Adams, Terry
//F
// Fakhouri, Fadi
// Feng, Hanying
//G
// Garcia, Cesar
// Garcia, Debra
// Garcia, Hugo
//M
// Mortensen, Sven
//O
// Omelchenko, Svetlana
// O'Donnell, Claire
//T
// Tucker, Lance
// Tucker, Michael
//Z
// Zabokritski, Eugene
Run this query, and the groups are now sorted in alphabetical order.
You can use the let keyword to introduce an identifier for any expression result in the
query expression. This identifier can be a convenience, as in the following example. It
can also enhance performance by storing the results of an expression so that it doesn't
have to be calculated multiple times.
C#
// Output:
// Omelchenko, Svetlana
// O'Donnell, Claire
// Mortensen, Sven
// Garcia, Cesar
// Fakhouri, Fadi
// Feng, Hanying
// Garcia, Hugo
// Adams, Terry
// Zabokritski, Eugene
// Tucker, Michael
C#
var studentQuery =
from student in students
let totalScore = student.Scores[0] + student.Scores[1] +
student.Scores[2] + student.Scores[3]
select totalScore;
// Output:
// Class average score = 334.166666666667
C#
IEnumerable<string> studentQuery =
from student in students
where student.Last == "Garcia"
select student.First;
// Output:
// The Garcias in the class are:
// Cesar
// Debra
// Hugo
Code earlier in this walkthrough indicated that the average class score is approximately
334. To produce a sequence of Students whose total score is greater than the class
average, together with their Student ID , you can use an anonymous type in the select
statement:
C#
var aboveAverageQuery =
from student in students
let x = student.Scores[0] + student.Scores[1] +
student.Scores[2] + student.Scores[3]
where x > averageScore
select new { id = student.ID, score = x };
// Output:
// Student ID: 113, Score: 338
// Student ID: 114, Score: 353
// Student ID: 116, Score: 369
// Student ID: 117, Score: 352
// Student ID: 118, Score: 343
// Student ID: 120, Score: 341
// Student ID: 122, Score: 368
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
issues and pull requests. For Open a documentation issue
more information, see our
contributor guide. Provide product feedback
Standard Query Operators Overview
Article • 05/31/2024
The standard query operators are the keywords and methods that form the LINQ pattern.
The C# language defines LINQ query keywords that you use for the most common
query expression. The compiler translates expressions using these keywords to the
equivalent method calls. The two forms are synonymous. Other methods that are part of
the System.Linq namespace don't have equivalent query keywords. In those cases, you
must use the method syntax. This section covers all the query operator keywords. The
runtime and other NuGet packages add more methods designed to work with LINQ
queries each release. The most common methods, including those that have query
keyword equivalents are covered in this section. For the full list of query methods
supported by the .NET Runtime, see the System.Linq.Enumerable API documentation. In
addition to the methods covered here, this class contains methods for concatenating
data sources, computing a single value from a data source, such as a sum, average, or
other value.
) Important
For IEnumerable<T> , the returned enumerable object captures the arguments that were
passed to the method. When that object is enumerated, the logic of the query operator
is employed and the query results are returned.
For IQueryable<T> , the query is translated into an expression tree. The expression tree
can be translated to a native query when the data source can optimize the query.
Libraries such as Entity Framework translate LINQ queries into native SQL queries that
execute at the database.
The following code example demonstrates how the standard query operators can be
used to obtain information about a sequence.
C#
string sentence = "the quick brown fox jumps over the lazy dog";
// Split the string into individual words to create a collection.
string[] words = sentence.Split(' ');
Where possible, the queries in this section use a sequence of words or numbers as the
input source. For queries where more complicated relationships between objects are
used, the following sources that model a school are used:
C#
Each Student has a grade level, a primary department, and a series of scores. A Teacher
also has a City property that identifies the campus where the teacher holds classes. A
Department has a name, and a reference to a Teacher who serves as the department
head.
Query operators
In a LINQ query, the first step is to specify the data source. In a LINQ query, the from
clause comes first in order to introduce the data source ( customers ) and the range
variable ( cust ).
C#
//queryAllStudents is an IEnumerable<Student>
var queryAllStudents = from student in students
select student;
The range variable is like the iteration variable in a foreach loop except that no actual
iteration occurs in a query expression. When the query is executed, the range variable
serves as a reference to each successive element in customers . Because the compiler can
infer the type of cust , you don't have to specify it explicitly. You can introduce more
range variables in a let clause. For more information, see let clause.
7 Note
For non-generic data sources such as ArrayList, the range variable must be
explicitly typed. For more information, see How to query an ArrayList with LINQ
(C#) and from clause.
Once you obtain a data source, you can perform any number of operations on that data
source:
ノ Expand table
Method C# query
expression syntax
from int i in
numbers
(For more
information, see
from clause.)
GroupBy group … by
-or-
group … by … into
…
(For more
information, see
group clause.)
GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, join … in … on …
IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, equals … into …
Func<TOuter,IEnumerable<TInner>, TResult>)
(For more
information, see
join clause.)
Join<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, join … in … on …
IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, equals …
Func<TOuter,TInner,TResult>)
(For more
information, see
join clause.)
(For more
information, see
orderby clause.)
Method C# query
expression syntax
OrderByDescending<TSource,TKey>(IEnumerable<TSource>, orderby …
Func<TSource,TKey>) descending
(For more
information, see
orderby clause.)
Select select
(For more
information, see
select clause.)
(For more
information, see
from clause.)
ThenBy<TSource,TKey>(IOrderedEnumerable<TSource>, orderby …, …
Func<TSource,TKey>)
(For more
information, see
orderby clause.)
ThenByDescending<TSource,TKey>(IOrderedEnumerable<TSource>, orderby …, …
Func<TSource,TKey>) descending
(For more
information, see
orderby clause.)
Where where
(For more
information, see
where clause.)
Merge multiple input sequences into a single output sequence that has a new
type.
Create output sequences whose elements consist of only one or several properties
of each element in the source sequence.
Create output sequences whose elements consist of the results of operations
performed on the source data.
Create output sequences in a different format. For example, you can transform
data from SQL rows or text files into XML.
These transformations can be combined in various ways in the same query. Furthermore,
the output sequence of one query can be used as the input sequence for a new query.
The following example transforms objects in an in-memory data structure into XML
elements.
C#
XML
<Root>
<student>
<First>Svetlana</First>
<Last>Omelchenko</Last>
<Scores>97,90,73,54</Scores>
</student>
<student>
<First>Claire</First>
<Last>O'Donnell</Last>
<Scores>56,78,95,95</Scores>
</student>
...
<student>
<First>Max</First>
<Last>Lindgren</Last>
<Scores>86,88,96,63</Scores>
</student>
<student>
<First>Arina</First>
<Last>Ivanova</Last>
<Scores>93,63,70,80</Scores>
</student>
</Root>
You can use the results of one query as the data source for a subsequent query. This
example shows how to order the results of a join operation. This query creates a group
join, and then sorts the groups based on the category element, which is still in scope.
Inside the anonymous type initializer, a subquery orders all the matching elements from
the products sequence.
C#
The equivalent query using method syntax is shown in the following code:
C#
Although you can use an orderby clause with one or more of the source sequences
before the join, generally we don't recommend it. Some LINQ providers might not
preserve that ordering after the join. For more information, see join clause.
See also
Enumerable
Queryable
select clause
Extension Methods
Query Keywords (LINQ)
Anonymous Types
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Filtering Data in C# with LINQ
Article • 05/31/2024
Filtering refers to the operation of restricting the result set to contain only those
elements that satisfy a specified condition. It's also referred to as selecting elements that
match the specified condition.
) Important
The following illustration shows the results of filtering a sequence of characters. The
predicate for the filtering operation specifies that the character must be 'A'.
The standard query operator methods that perform selection are listed in the following
table:
ノ Expand table
C#
the
fox
*/
The equivalent query using method syntax is shown in the following code:
C#
IEnumerable<string> query =
words.Where(word => word.Length == 3);
the
fox
*/
See also
System.Linq
where clause
How to query an assembly's metadata with Reflection (LINQ) (C#)
How to query for files with a specified attribute or name (C#)
How to sort or filter text data by any word or field (LINQ) (C#)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Projection operations (C#)
Article • 05/31/2024
Projection refers to the operation of transforming an object into a new form that often
consists only of those properties subsequently used. By using projection, you can
construct a new type that is built from each object. You can project a property and
perform a mathematical function on it. You can also project the original object without
changing it.
) Important
The standard query operator methods that perform projection are listed in the following
section.
Methods
ノ Expand table
C#
a
a
a
d
*/
The equivalent query using method syntax is shown in the following code:
C#
a
a
a
d
*/
SelectMany
The following example uses multiple from clauses to project each word from each string
in a list of strings.
C#
an
apple
a
day
the
quick
brown
fox
*/
The equivalent query using method syntax is shown in the following code:
C#
an
apple
a
day
the
quick
brown
fox
*/
The SelectMany method can also form the combination of matching every item in the
first sequence with every item in the second sequence:
C#
The equivalent query using method syntax is shown in the following code:
C#
Zip
There are several overloads for the Zip projection operator. All of the Zip methods
work on sequences of two or more possibly heterogenous types. The first two overloads
return tuples, with the corresponding positional type from the given sequences.
C#
C#
) Important
The resulting sequence from a zip operation is never longer in length than the
shortest sequence. The numbers and letters collections differ in length, and the
resulting sequence omits the last element from the numbers collection, as it has
nothing to zip with.
The second overload accepts a third sequence. Let's create another collection, namely
emoji :
C#
C#
Much like the previous overload, the Zip method projects a tuple, but this time with
three elements.
The third overload accepts a Func<TFirst, TSecond, TResult> argument that acts as a
results selector. You can project a new resulting sequence from the sequences being
zipped.
C#
With the preceding Zip overload, the specified function is applied to the corresponding
elements numbers and letter , producing a sequence of the string results.
This illustration depicts how Select returns a collection that has the same number of
elements as the source collection.
Code example
The following example compares the behavior of Select and SelectMany . The code
creates a "bouquet" of flowers by taking the items from each list of flower names in the
source collection. In the following example, the "single value" that the transform
function Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) uses
is a collection of values. This example requires the extra foreach loop in order to
enumerate each string in each subsequence.
C#
class Bouquet
{
public required List<string> Flowers { get; init; }
}
See also
System.Linq
select clause
How to populate object collections from multiple sources (LINQ) (C#)
How to split a file into many files by using groups (LINQ) (C#)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Set operations (C#)
Article • 05/31/2024
Set operations in LINQ refer to query operations that produce a result set based on the
presence or absence of equivalent elements within the same or separate collections.
) Important
ノ Expand table
Union or Returns the set union, which means Not applicable. Enumerable.Union
UnionBy unique elements that appear in Enumerable.UnionBy
either of two collections. Queryable.Union
Queryable.UnionBy
C#
following code, words are discriminated based on their Length , and the first word of
each length is displayed:
C#
The following examples in this article use the common data sources for this area:
C#
Each Student has a grade level, a primary department, and a series of scores. A Teacher
also has a City property that identifies the campus where the teacher holds classes. A
Department has a name, and a reference to a Teacher who serves as the department
head.
C#
The ExceptBy method is an alternative approach to Except that takes two sequences of
possibly heterogenous types and a keySelector . The keySelector is the same type as
the first collection's type. Consider the following Teacher array and teacher IDs to
exclude. To find teachers in the first collection that aren't in the second collection, you
can project the teacher's ID onto the second collection:
C#
int[] teachersToExclude =
[
901, // English
965, // Mathematics
932, // Engineering
945, // Economics
987, // Physics
901 // Chemistry
];
The teachers array is filtered to only those teachers that aren't in the
teachersToExclude array.
The teachersToExclude array contains the ID value for all department heads.
The call to ExceptBy results in a new set of values that are written to the console.
The new set of values is of type Teacher , which is the type of the first collection. Each
teacher in the teachers array that doesn't have a corresponding ID value in the
C#
C#
The query produces the intersection of the Teacher and Student by comparing
names.
Only people that are found in both arrays are present in the resulting sequence.
The resulting Student instances are written to the console.
C#
The UnionBy method is an alternative approach to Union that takes two sequences of
the same type and a keySelector . The keySelector is used as the comparative
discriminator of the source type. The following query produces the list of all people that
are either students or teachers. Students who are also teachers are added to the union
set only once:
C#
The teachers and students arrays are woven together using their names as the
key selector.
The resulting names are written to the console.
See also
System.Linq
How to find the set difference between two lists (LINQ) (C#)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our Provide product feedback
contributor guide.
Sorting Data (C#)
Article • 05/31/2024
A sorting operation orders the elements of a sequence based on one or more attributes.
The first sort criterion performs a primary sort on the elements. By specifying a second
sort criterion, you can sort the elements within each primary sort group.
) Important
The standard query operator methods that sort data are listed in the following section.
Methods
ノ Expand table
The following examples in this article use the common data sources for this area:
C#
Each Student has a grade level, a primary department, and a series of scores. A Teacher
also has a City property that identifies the campus where the teacher holds classes. A
Department has a name, and a reference to a Teacher who serves as the department
head.
C#
The equivalent query written using method syntax is shown in the following code:
C#
C#
IEnumerable<string> query = from teacher in teachers
orderby teacher.Last descending
select teacher.Last;
The equivalent query written using method syntax is shown in the following code:
C#
C#
The equivalent query written using method syntax is shown in the following code:
C#
C#
The equivalent query written using method syntax is shown in the following code:
C#
See also
System.Linq
orderby clause
How to sort or filter text data by any word or field (LINQ) (C#)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Quantifier operations in LINQ (C#)
Article • 05/31/2024
Quantifier operations return a Boolean value that indicates whether some or all of the
elements in a sequence satisfy a condition.
) Important
The following illustration depicts two different quantifier operations on two different
source sequences. The first operation asks if any of the elements are the character 'A'.
The second operation asks if all the elements are the character 'A'. Both methods return
true in this example.
ノ Expand table
C#
Any
The following example uses the Any to find students that scored greater than 95 on any
exam.
C#
Contains
The following example uses the Contains to find students that scored exactly 95 on an
exam.
C#
See also
System.Linq
Dynamically specify predicate filters at run time
How to query for sentences that contain a specified set of words (LINQ) (C#)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Partitioning data (C#)
Article • 05/31/2024
Partitioning in LINQ refers to the operation of dividing an input sequence into two
sections, without rearranging the elements, and then returning one of the sections.
) Important
The following illustration shows the results of three different partitioning operations on
a sequence of characters. The first operation returns the first three elements in the
sequence. The second operation skips the first three elements and returns the remaining
elements. The third operation skips the first two elements in the sequence and returns
the next three elements.
The standard query operator methods that partition sequences are listed in the
following section.
Operators
ノ Expand table
Method Description C# query More information
names expression
syntax
You use the Take method to take only the first elements in a sequence:
C#
You use the Skip method to skip the first elements in a sequence, and use the
remaining elements:
C#
The TakeWhile and SkipWhile methods also take and skip elements in a sequence.
However, instead of a set number of elements, these methods skip or take elements
based on a condition. TakeWhile takes the elements of a sequence until an element
doesn't match the condition.
C#
SkipWhile skips the first elements, as long as the condition is true. The first element not
C#
The Chunk operator is used to split elements of a sequence based on a given size .
C#
int chunkNumber = 1;
foreach (int[] chunk in Enumerable.Range(0, 8).Chunk(3))
{
Console.WriteLine($"Chunk {chunkNumber++}:");
foreach (int item in chunk)
{
Console.WriteLine($" {item}");
}
Console.WriteLine();
}
// This code produces the following output:
// Chunk 1:
// 0
// 1
// 2
//
//Chunk 2:
// 3
// 4
// 5
//
//Chunk 3:
// 6
// 7
See also
System.Linq
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Converting Data Types (C#)
Article • 05/31/2024
) Important
Conversion operations in LINQ queries are useful in various applications. Following are
some examples:
Methods
The following table lists the standard query operator methods that perform data-type
conversions.
The conversion methods in this table whose names start with "As" change the static type
of the source collection but don't enumerate it. The methods whose names start with
"To" enumerate the source collection and put the items into the corresponding
collection type.
ノ Expand table
Method Description C# Query More Information
Name Expression
Syntax
from string
str in words
The following examples in this article use the common data sources for this area:
C#
Each Student has a grade level, a primary department, and a series of scores. A Teacher
also has a City property that identifies the campus where the teacher holds classes. A
Department has a name, and a reference to a Teacher who serves as the department
head.
C#
The equivalent query can be expressed using method syntax as shown in the following
example:
C#
See also
System.Linq
from clause
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Join Operations in LINQ
Article • 05/29/2024
A join of two data sources is the association of objects in one data source with objects
that share a common attribute in another data source.
) Important
Joining is an important operation in queries that target data sources whose relationships
to each other can't be followed directly. In object-oriented programming, joining could
mean a correlation between objects that isn't modeled, such as the backwards direction
of a one-way relationship. An example of a one-way relationship is a Student class that
has a property of type Department that represents the major, but the Department class
doesn't have a property that is a collection of Student objects. If you have a list of
Department objects and you want to find all the students in each department, you could
The join methods provided in the LINQ framework are Join and GroupJoin. These
methods perform equijoins, or joins that match two data sources based on equality of
their keys. (For comparison, Transact-SQL supports join operators other than equals , for
example the less than operator.) In relational database terms, Join implements an inner
join, a type of join in which only those objects that have a match in the other data set
are returned. The GroupJoin method has no direct equivalent in relational database
terms, but it implements a superset of inner joins and left outer joins. A left outer join is
a join that returns each element of the first (left) data source, even if it has no correlated
elements in the other data source.
The following illustration shows a conceptual view of two sets and the elements within
those sets that are included in either an inner join or a left outer join.
Methods
ノ Expand table
The following examples in this article use the common data sources for this area:
C#
Each Student has a grade level, a primary department, and a series of scores. A Teacher
also has a City property that identifies the campus where the teacher holds classes. A
Department has a name, and a reference to a Teacher who serves as the department
head.
The following example uses the join … in … on … equals … clause to join two
sequences based on specific value:
C#
The preceding query can be expressed using method syntax as shown in the following
code:
C#
The following example uses the join … in … on … equals … into … clause to join two
sequences based on specific value and groups the resulting matches for each element:
C#
The preceding query can be expressed using method syntax as shown in the following
example:
C#
A simple inner join that correlates elements from two data sources based on a
simple key.
An inner join that correlates elements from two data sources based on a composite
key. A composite key, which is a key that consists of more than one value, enables
you to correlate elements based on more than one property.
A multiple join in which successive join operations are appended to each other.
An inner join that is implemented by using a group join.
objects look. In the following example, the resulting objects are anonymous types that
consist of the department name and the name of the teacher that leads the department.
C#
You achieve the same results using the Join method syntax:
C#
The teachers who aren't department heads don't appear in the final results.
The following example uses a list of Teacher objects and a list of Student objects to
determine which teachers are also students. Both of these types have properties that
represent the first and family name of each person. The functions that create the join
keys from each list's elements return an anonymous type that consists of the properties.
The join operation compares these composite keys for equality and returns pairs of
objects from each list where both the first name and the family name match.
C#
// Join the two data sources based on a composite key consisting of first
and last name,
// to determine which employees are also students.
IEnumerable<string> query =
from teacher in teachers
join student in students on new
{
FirstName = teacher.First,
LastName = teacher.Last
} equals new
{
student.FirstName,
student.LastName
}
select teacher.First + " " + teacher.Last;
string result = "The following people are both teachers and students:\r\n";
foreach (string name in query)
{
result += $"{name}\r\n";
}
Console.Write(result);
You can use the Join method, as shown in the following example:
C#
Multiple join
Any number of join operations can be appended to each other to perform a multiple
join. Each join clause in C# correlates a specified data source with the results of the
previous join.
The first join clause matches students and departments based on a Student object's
DepartmentID matching a Department object's ID . It returns a sequence of anonymous
The second join clause correlates the anonymous types returned by the first join with
Teacher objects based on that teacher's ID matching the department head ID. It returns
a sequence of anonymous types that contain the student's name, the department name,
and the department leader's name. Because this operation is an inner join, only those
objects from the first data source that have a match in the second data source are
returned.
C#
The equivalent using multiple Join method uses the same approach with the anonymous
type:
C#
C#
var query1 =
from department in departments
join student in students on department.ID equals student.DepartmentID
into gj
from subStudent in gj
select new
{
DepartmentName = department.Name,
StudentName = $"{subStudent.FirstName} {subStudent.LastName}"
};
Console.WriteLine("Inner join using GroupJoin():");
foreach (var v in query1)
{
Console.WriteLine($"{v.DepartmentName} - {v.StudentName}");
}
C#
The result is equivalent to the result set obtained by using the join clause without the
into clause to perform an inner join. The following code demonstrates this equivalent
query:
C#
To avoid chaining, the single Join method can be used as presented here:
C#
7 Note
Each element of the first collection appears in the result set of a group join
regardless of whether correlated elements are found in the second collection. In the
case where no correlated elements are found, the sequence of correlated elements
for that element is empty. The result selector therefore has access to every element
of the first collection. This differs from the result selector in a non-group join, which
cannot access elements from the first collection that have no match in the second
collection.
2 Warning
The first example in this article shows you how to perform a group join. The second
example shows you how to use a group join to create XML elements.
Group join
The following example performs a group join of objects of type Department and Student
based on the Department.ID matching the Student.DepartmentID property. Unlike a
non-group join, which produces a pair of elements for each match, the group join
produces only one resulting object for each element of the first collection, which in this
example is a Department object. The corresponding elements from the second
collection, which in this example are Student objects, are grouped into a collection.
Finally, the result selector function creates an anonymous type for each match that
consists of Department.Name and a collection of Student objects.
C#
The equivalent query using method syntax is shown in the following code:
C#
C#
Console.WriteLine(departmentsAndStudents);
The equivalent query using method syntax is shown in the following code:
C#
Console.WriteLine(departmentsAndStudents);
The following example demonstrates how to use the DefaultIfEmpty method on the
results of a group join to perform a left outer join.
The first step in producing a left outer join of two collections is to perform an inner join
by using a group join. (See Perform inner joins for an explanation of this process.) In this
example, the list of Department objects is inner-joined to the list of Student objects
based on a Department object's ID that matches the student's DepartmentID .
The second step is to include each element of the first (left) collection in the result set
even if that element has no matches in the right collection. This is accomplished by
calling DefaultIfEmpty on each sequence of matching elements from the group join. In
this example, DefaultIfEmpty is called on each sequence of matching Student objects.
The method returns a collection that contains a single, default value if the sequence of
matching Student objects is empty for any Department object, ensuring that each
Department object is represented in the result collection.
7 Note
The default value for a reference type is null ; therefore, the example checks for a
null reference before accessing each element of each Student collection.
C#
var query =
from student in students
join department in departments on student.DepartmentID equals
department.ID into gj
from subgroup in gj.DefaultIfEmpty()
select new
{
student.FirstName,
student.LastName,
Department = subgroup?.Name ?? string.Empty
};
The equivalent query using method syntax is shown in the following code:
C#
See also
Join
GroupJoin
Anonymous types
Formulate Joins and Cross-Product Queries
join clause
group clause
How to join content from dissimilar files (LINQ) (C#)
How to populate object collections from multiple sources (LINQ) (C#)
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Grouping Data (C#)
Article • 05/31/2024
Grouping refers to the operation of putting data into groups so that the elements in
each group share a common attribute. The following illustration shows the results of
grouping a sequence of characters. The key for each group is the character.
) Important
The standard query operator methods that group data elements are listed in the
following table.
ノ Expand table
group … by …
into …
Method Description C# Query More Information
Name Expression
Syntax
The following code example uses the group by clause to group integers in a list
according to whether they're even or odd.
C#
List<int> numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208];
The equivalent query using method syntax is shown in the following code:
C#
List<int> numbers = [35, 44, 200, 84, 3987, 4, 199, 329, 446, 208];
The following examples in this article use the common data sources for this area:
C#
Each Student has a grade level, a primary department, and a series of scores. A Teacher
also has a City property that identifies the campus where the teacher holds classes. A
Department has a name, and a reference to a Teacher who serves as the department
head.
By a single property.
By the first letter of a string property.
By a computed numeric range.
By Boolean predicate or other expression.
By a compound key.
In addition, the last two queries project their results into a new anonymous type that
contains only the student's first and family name. For more information, see the group
clause.
C#
var groupByYearQuery =
from student in students
group student by student.Year into newGroup
orderby newGroup.Key
select newGroup;
The equivalent code using method syntax is shown in the following example:
C#
C#
var groupByFirstLetterQuery =
from student in students
let firstLetter = student.LastName[0]
group student by firstLetter;
The equivalent code using method syntax is shown in the following example:
C#
percentile based on the student's average score. The method returns an integer
between 0 and 10.
C#
var groupByPercentileQuery =
from student in students
let percentile = GetPercentile(student)
group new
{
student.FirstName,
student.LastName
} by percentile into percentGroup
orderby percentGroup.Key
select percentGroup;
Nested foreach required to iterate over groups and group items. The equivalent code
using method syntax is shown in the following example:
C#
C#
var groupByHighAverageQuery =
from student in students
group new
{
student.FirstName,
student.LastName
} by student.Scores.Average() > 75 into studentGroup
select studentGroup;
The equivalent query using method syntax is shown in the following code:
C#
C#
var groupByCompoundKey =
from student in students
group student by new
{
FirstLetterOfLastName = student.LastName[0],
IsScoreOver85 = student.Scores[0] > 85
} into studentGroup
orderby studentGroup.Key.FirstLetterOfLastName
select studentGroup;
The equivalent query using method syntax is shown in the following code:
C#
var groupByCompoundKey = students
.GroupBy(student => new
{
FirstLetterOfLastName = student.LastName[0],
IsScoreOver85 = student.Scores[0] > 85
})
.OrderBy(studentGroup => studentGroup.Key.FirstLetterOfLastName);
C#
var nestedGroupsQuery =
from student in students
group student by student.Year into newGroup1
from newGroup2 in
from student in newGroup1
group student by student.LastName
group newGroup2 by newGroup1.Key;
The equivalent query using method syntax is shown in the following code:
C#
var nestedGroupsQuery =
students
.GroupBy(student => student.Year)
.Select(newGroup1 => new
{
newGroup1.Key,
NestedGroup = newGroup1
.GroupBy(student => student.LastName)
});
against each new group created by the outer query. In this particular example the final
output isn't a group, but a flat sequence of anonymous types.
For more information about how to group, see group clause. For more information
about continuations, see into. The following example uses an in-memory data structure
as the data source, but the same principles apply for any kind of LINQ data source.
C#
var queryGroupMax =
from student in students
group student by student.Year into studentGroup
select new
{
Level = studentGroup.Key,
HighestScore = (
from student2 in studentGroup
select student2.Scores.Average()
).Max()
};
The query in the preceding snippet can also be written using method syntax. The
following code snippet has a semantically equivalent query written using method syntax.
C#
var queryGroupMax =
students
.GroupBy(student => student.Year)
.Select(studentGroup => new
{
Level = studentGroup.Key,
HighestScore = studentGroup.Max(student2 =>
student2.Scores.Average())
});
See also
System.Linq
GroupBy
IGrouping<TKey,TElement>
group clause
How to split a file into many files by using groups (LINQ) (C#)
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to: Use LINQ to query files and
directories
Article • 04/25/2024
Many file system operations are essentially queries and are therefore well suited to the
LINQ approach. These queries are nondestructive. They don't change the contents of the
original files or folders. Queries shouldn't cause any side-effects. In general, any code
(including queries that perform create / update / delete operations) that modifies source
data should be kept separate from the code that just queries the data.
There's some complexity involved in creating a data source that accurately represents
the contents of the file system and handles exceptions gracefully. The examples in this
section create a snapshot collection of FileInfo objects that represents all the files under
a specified root folder and all its subfolders. The actual state of each FileInfo might
change in the time between when you begin and end executing a query. For example,
you can create a list of FileInfo objects to use as a data source. If you try to access the
Length property in a query, the FileInfo object tries to access the file system to update
the value of Length . If the file no longer exists, you get a FileNotFoundException in your
query, even though you aren't querying the file system directly.
C#
The following query shows how to group the contents of a specified directory tree by
the file name extension.
C#
The output from this program can be long, depending on the details of the local file
system and what the startFolder is set to. To enable viewing of all results, this example
shows how to page through results. A nested foreach loop is required because each
group is enumerated separately.
C#
// Return the total number of bytes in all the files under the specified
folder.
long totalBytes = fileLengths.Sum();
The following example contains five separate queries that show how to query and group
files, depending on their file size in bytes. You can modify these examples to base the
query on some other property of the FileInfo object.
C#
To return one or more complete FileInfo objects, the query first must examine each one
in the data source, and then sort them by the value of their Length property. Then it can
return the single one or the sequence with the greatest lengths. Use First to return the
first element in a list. Use Take to return the first n number of elements. Specify a
descending sort order to put the smallest elements at the start of the list.
C#
The first query uses a key to determine a match. It finds files that have the same name
but whose contents might be different. The second query uses a compound key to
match against three properties of the FileInfo object. This query is much more likely to
find files that have the same name and similar or identical content.
C#
C#
By querying for a Boolean value that specifies whether the two file lists are
identical.
By querying for the intersection to retrieve the files that are in both folders.
By querying for the set difference to retrieve the files that are in one folder but not
the other.
The techniques shown here can be adapted to compare sequences of objects of any
type.
The FileComparer class shown here demonstrates how to use a custom comparer class
together with the Standard Query Operators. The class isn't intended for use in real-
world scenarios. It just uses the name and length in bytes of each file to determine
whether the contents of each folder are identical or not. In a real-world scenario, you
should modify this comparer to perform a more rigorous equality check.
C#
if (areIdentical == true)
{
Console.WriteLine("the two folders are the same");
}
else
{
Console.WriteLine("The two folders are not the same");
}
if (queryCommonFiles.Any())
{
Console.WriteLine($"The following files are in both folders (total
number = {queryCommonFiles.Count()}):");
foreach (var v in queryCommonFiles.Take(10))
{
Console.WriteLine(v.Name); //shows which items end up in result
list
}
}
else
{
Console.WriteLine("There are no common files in the two folders.");
}
Console.WriteLine();
Console.WriteLine($"The following files are in list1 but not list2
(total number = {queryList1Only.Count()}):");
foreach (var v in queryList1Only.Take(10))
{
Console.WriteLine(v.FullName);
}
Console.WriteLine();
Console.WriteLine($"The following files are in list2 but not list1
(total number = {queryList2Only.Count()}:");
foreach (var v in queryList2Only.Take(10))
{
Console.WriteLine(v.FullName);
}
}
In the following example, assume that the three columns represent students' "family
name," "first name", and "ID." The fields are in alphabetical order based on the students'
family names. The query produces a new sequence in which the ID column appears first,
followed by a second column that combines the student's first name and family name.
The lines are reordered according to the ID field. The results are saved into a new file
and the original data isn't modified. The following text shows the contents of the
spreadsheet1.csv file used in the following example:
txt
Adams,Terry,120
Fakhouri,Fadi,116
Feng,Hanying,117
Garcia,Cesar,114
Garcia,Debra,115
Garcia,Hugo,118
Mortensen,Sven,113
O'Donnell,Claire,112
Omelchenko,Svetlana,111
Tucker,Lance,119
Tucker,Michael,122
Zabokritski,Eugene,121
The following code reads the source file and rearranges each column in the CSV file to
rearrange the order of the columns:
C#
File.WriteAllLines("spreadsheet2.csv", query.ToArray());
/* Output to spreadsheet2.csv:
111, Svetlana Omelchenko
112, Claire O'Donnell
113, Sven Mortensen
114, Cesar Garcia
115, Debra Garcia
116, Fadi Fakhouri
117, Hanying Feng
118, Hugo Garcia
119, Lance Tucker
120, Terry Adams
121, Eugene Zabokritski
122, Michael Tucker
*/
txt
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra
The second file, names2.txt, contains a different set of names, some of which are in
common with the first set:
txt
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi
The following code queries both files, takes the union of both files, then writes a new file
for each group, defined by the first letter of the family name:
C#
Console.WriteLine(g.Key);
The following text shows the contents of scores.csv. The file represents spreadsheet data.
Column 1 is the student's ID, and columns 2 through 5 are test scores.
txt
The following text shows the contents of names.csv. The file represents a spreadsheet
that contains the student's family name, first name, and student ID.
txt
Omelchenko,Svetlana,111
O'Donnell,Claire,112
Mortensen,Sven,113
Garcia,Cesar,114
Garcia,Debra,115
Fakhouri,Fadi,116
Feng,Hanying,117
Garcia,Hugo,118
Tucker,Lance,119
Adams,Terry,120
Zabokritski,Eugene,121
Tucker,Michael,122
Join content from dissimilar files that contain related information. File names.csv
contains the student name plus an ID number. File scores.csv contains the ID and a set of
four test scores. The following query joins the scores to the student names by using ID
as a matching key. The code is shown in the following example:
C#
The following text shows the contents of scores.csv. Assume that the first column
represents a student ID, and subsequent columns represent scores from four exams.
txt
The following text shows how to use the Split method to convert each line of text into
an array. Each array element represents a column. Finally, the text in each column is
converted to its numeric representation.
C#
// Spreadsheet format:
// Student ID Exam#1 Exam#2 Exam#3 Exam#4
// 111, 97, 92, 81, 60
If your file is a tab-separated file, just update the argument in the Split method to \t .
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to: Use LINQ to query strings
Article • 04/25/2024
C#
The preceding query shows how you can treat a string as a sequence of characters.
C#
The preceding query shows how you can view strings as a sequence of words, after
splitting a string into a sequence of words.
How to sort or filter text data by any word or
field
The following example shows how to sort lines of structured text, such as comma-
separated values, by any field in the line. The field can be dynamically specified at run
time. Assume that the fields in scores.csv represent a student's ID number, followed by a
series of four test scores:
txt
The following query sorts the lines based on the score of the first exam, stored in the
second column:
C#
The preceding query shows how you can manipulate strings by splitting them into fields,
and querying the individual fields.
C#
// Define the search terms. This list could also be dynamically populated at
run time.
string[] wordsToMatch = [ "Historically", "data", "integrated" ];
// Find sentences that contain all the terms in the wordsToMatch array.
// Note that the number of terms to match is not specified at compile time.
char[] separators = ['.', '?', '!', ' ', ';', ':', ','];
var sentenceQuery = from sentence in sentences
let w =
sentence.Split(separators,StringSplitOptions.RemoveEmptyEntries)
where w.Distinct().Intersect(wordsToMatch).Count() ==
wordsToMatch.Count()
select sentence;
The query first splits the text into sentences, and then splits each sentence into an array
of strings that hold each word. For each of these arrays, the Distinct method removes all
duplicate words, and then the query performs an Intersect operation on the word array
and the wordsToMatch array. If the count of the intersection is the same as the count of
the wordsToMatch array, all words were found in the words and the original sentence is
returned.
The call to Split uses punctuation marks as separators in order to remove them from the
string. If you didn't do remove punctuation, for example you could have a string
"Historically," that wouldn't match "Historically" in the wordsToMatch array. You might
have to use extra separators, depending on the types of punctuation found in the
source text.
C#
You can also query the MatchCollection object returned by a RegEx search. Only the
value of each match is produced in the results. However, it's also possible to use LINQ to
perform all kinds of filtering, sorting, and grouping on that collection. Because
MatchCollection is a nongeneric IEnumerable collection, you have to explicitly state the
type of the range variable in the query.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
LINQ and collections
Article • 04/25/2024
Most collections model a sequence of elements. You can use LINQ to query any
collection type. Other LINQ methods find elements in a collection, compute values from
the elements in a collection, or modify the collection or its elements. These examples
help you learn about LINQ methods and how you can use them with your collections, or
other data sources.
txt
Bankov, Peter
Holm, Michael
Garcia, Hugo
Potra, Cristina
Noriega, Fabricio
Aw, Kam Foo
Beebe, Ann
Toyoshima, Tim
Guy, Wey Yuan
Garcia, Debra
The second collection of names is stored in the file names2.txt. Some names appear in
both sequences.
txt
Liu, Jinghao
Bankov, Peter
Holm, Michael
Garcia, Hugo
Beebe, Ann
Gilchrist, Beth
Myrcha, Jacek
Giakoumakis, Leo
McLin, Nkenge
El Yassir, Mehdi
The following code shows how you can use the Enumerable.Except method to find
elements in the first list that aren't in the second list:
C#
// Create the query. Note that method syntax must be used here.
var differenceQuery = names1.Except(names2);
Some types of query operations, such as Except, Distinct, Union, and Concat, can only be
expressed in method-based syntax.
C#
Don't try to join in-memory data or data in the file system with data that is still in a
database. Such cross-domain joins can yield undefined results because of different
ways in which join operations might be defined for database queries and other
types of sources. Additionally, there is a risk that such an operation could cause an
out-of-memory exception if the amount of data in the database is large enough. To
join data from a database to in-memory data, first call ToList or ToArray on the
database query, and then perform the join on the returned collection.
This example uses two files. The first, names.csv, contains student names and student
IDs.
txt
Omelchenko,Svetlana,111
O'Donnell,Claire,112
Mortensen,Sven,113
Garcia,Cesar,114
Garcia,Debra,115
Fakhouri,Fadi,116
Feng,Hanying,117
Garcia,Hugo,118
Tucker,Lance,119
Adams,Terry,120
Zabokritski,Eugene,121
Tucker,Michael,122
The second, scores.csv, contains student IDs in the first column, followed by exam scores.
txt
C#
In the select clause, each new Student object is initialized from the data in the two
sources.
If you don't have to store the results of a query, tuples or anonymous types can be more
convenient than named types. The following example executes the same task as the
previous example, but uses tuples instead of named types:
C#
C#
By specifying the type of the range variable, you're casting each item in the ArrayList to
a Student .
C#
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to extend LINQ
Article • 04/25/2024
All LINQ based methods follow one of two similar patterns. They take an enumerable
sequence. They return either a different sequence, or a single value. The consistency of
the shape enables you to extend LINQ by writing methods with a similar shape. In fact,
the .NET libraries have gained new methods in many .NET releases since LINQ was first
introduced. In this article, you see examples of extending LINQ by writing your own
methods that follow the same pattern.
When you extend the IEnumerable<T> interface, you can apply your custom methods to
any enumerable collection. For more information, see Extension Methods.
An aggregate method computes a single value from a set of values. LINQ provides
several aggregate methods, including Average, Min, and Max. You can create your own
aggregate method by adding an extension method to the IEnumerable<T> interface.
The following code example shows how to create an extension method called Median to
compute a median for a sequence of numbers of type double .
C#
var sortedList =
source.OrderBy(number => number).ToList();
int itemIndex = sortedList.Count / 2;
if (sortedList.Count % 2 == 0)
{
// Even number of items.
return (sortedList[itemIndex] + sortedList[itemIndex - 1]) / 2;
}
else
{
// Odd number of items.
return sortedList[itemIndex];
}
}
}
You call this extension method for any enumerable collection in the same way you call
other aggregate methods from the IEnumerable<T> interface.
The following code example shows how to use the Median method for an array of type
double .
C#
You can overload your aggregate method so that it accepts sequences of various types.
The standard approach is to create an overload for each type. Another approach is to
create an overload that takes a generic type and convert it to a specific type by using a
delegate. You can also combine both approaches.
You can create a specific overload for each type that you want to support. The following
code example shows an overload of the Median method for the int type.
C#
// int overload
public static double Median(this IEnumerable<int> source) =>
(from number in source select (double)number).Median();
You can now call the Median overloads for both integer and double types, as shown in
the following code:
C#
You can also create an overload that accepts a generic sequence of objects. This overload
takes a delegate as a parameter and uses it to convert a sequence of objects of a
generic type to a specific type.
The following code shows an overload of the Median method that takes the
Func<T,TResult> delegate as a parameter. This delegate takes an object of generic type
T and returns an object of type double .
C#
// generic overload
public static double Median<T>(
this IEnumerable<T> numbers, Func<T, double> selector) =>
(from num in numbers select selector(num)).Median();
You can now call the Median method for a sequence of objects of any type. If the type
doesn't have its own method overload, you have to pass a delegate parameter. In C#,
you can use a lambda expression for this purpose. Also, in Visual Basic only, if you use
the Aggregate or Group By clause instead of the method call, you can pass any value or
expression that is in the scope this clause.
The following example code shows how to call the Median method for an array of
integers and an array of strings. For strings, the median for the lengths of strings in the
array is calculated. The example shows how to pass the Func<T,TResult> delegate
parameter to the Median method for each case.
C#
/*
You can use the num => num lambda expression as a parameter for the
Median method
so that the compiler will implicitly convert its value to double.
If there is no implicit conversion, the compiler will display an error
message.
*/
var query3 = numbers3.Median(num => num);
// With the generic overload, you can also use numeric properties of
objects.
var query4 = numbers4.Median(str => str.Length);
You can extend the IEnumerable<T> interface with a custom query method that returns
a sequence of values. In this case, the method must return a collection of type
IEnumerable<T>. Such methods can be used to apply filters or data transforms to a
sequence of values.
first element.
C#
index++;
}
}
You can call this extension method for any enumerable collection just as you would call
other methods from the IEnumerable<T> interface, as shown in the following code:
C#
ノ Expand table
Key Value
A We
A think
A that
B Linq
C is
A really
B cool
B !
The solution is implemented as a thread-safe extension method that returns its results in
a streaming manner. It produces its groups as it moves through the source sequence.
Unlike the group or orderby operators, it can begin returning groups to the caller
before reading the entire sequence. The following example shows both the extension
method and the client code that uses it:
C#
while (true)
{
var key = keySelector(enumerator.Current);
if (current.CopyAllChunkElements() == noMoreSourceElements)
{
yield break;
}
}
}
}
C#
ChunkExtensions class
5. When the caller has enumerated all the chunk items, the Chunk.GetEnumerator
method has copied all chunk items. If the Chunk.GetEnumerator loop didn't
enumerate all elements in the chunk, do it now to avoid corrupting the iterator for
clients that might be calling it on a separate thread.
Chunk class
The Chunk class is a contiguous group of one or more source elements that have the
same key. A Chunk has a key and a list of ChunkItem objects, which are copies of the
elements in the source sequence:
C#
// Flag to indicate the source iterator has reached the end of the
source sequence.
internal bool isLastSourceElement;
// The end and beginning are the same until the list contains > 1
elements.
tail = head;
// Indicates that all chunk elements have been copied to the list of
ChunkItems.
private bool DoneCopyingChunk => tail == null;
// If we are (a) at the end of the source, or (b) at the end of the
current chunk
// then null out the enumerator and predicate for reuse with the
next chunk.
if (isLastSourceElement || !predicate(enumerator.Current))
{
enumerator = default!;
predicate = default!;
}
else
{
tail!.Next = new ChunkItem(enumerator.Current);
}
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
}
Each ChunkItem (represented by ChunkItem class) has a reference to the next ChunkItem
in the list. The list consists of its head - which stores the contents of the first source
element that belongs with this chunk, and its tail - which is an end of the list. The tail
is repositioned each time a new ChunkItem is added. The tail of the linked list is set to
null in the CopyNextChunkElement method if the key of the next element doesn't match
the current chunk's key, or there are no more elements in the source.
The CopyNextChunkElement method of the Chunk class adds one ChunkItem to the current
group of items. It tries to advance the iterator on the source sequence. If the MoveNext()
method returns false the iteration is at the end, and isLastSourceElement is set to
true .
The CopyAllChunkElements method is called after the end of the last chunk was reached.
It checks whether there are more elements in the source sequence. If there are, it returns
true if the enumerator for this chunk was exhausted. In this method, when the private
DoneCopyingChunk field is checked for true , if isLastSourceElement is false , it signals to
The inner foreach loop invokes the GetEnumerator method of the Chunk class. This
method stays one element ahead of the client requests. It adds the next element of the
chunk only after the client requests the previous last element in the list.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Query based on run-time state
Article • 04/25/2024
In most LINQ queries, the general shape of the query is set in code. You might filter
items using a where clause, sort the output collection using orderby , group items, or
perform some computation. Your code might provide parameters for the filter, or the
sort key, or other expressions that are part of the query. However, the overall shape of
the query can't change. In this article, you learn techniques to use
System.Linq.IQueryable<T> interface and types that implement it to modify the shape
of a query at run time.
You use these techniques to build queries at run time, where some user input or run-
time state changes the query methods you want to use as part of the query. You want to
edit the query by adding, removing, or modifying query clauses.
7 Note
C#
string[] companyNames = [
"Consolidated Messenger", "Alpine Ski House", "Southridge Video",
"City Power & Light", "Coho Winery", "Wide World Importers",
"Graphic Design Institute", "Adventure Works", "Humongous Insurance",
"Woodgrove Bank", "Margie's Travel", "Northwind Traders",
"Blue Yonder Airlines", "Trey Research", "The Phone Company",
"Wingtip Toys", "Lucerne Publishing", "Fourth Coffee"
];
// Use an in-memory array as the data source, but the IQueryable could have
come
// from anywhere -- an ORM backed by a database, a web request, or any other
LINQ provider.
IQueryable<string> companyNamesSource = companyNames.AsQueryable();
var fixedQry = companyNames.OrderBy(x => x);
Every time you run the preceding code, the same exact query is executed. Let's learn
how to modify the query extend it or modify it. Fundamentally, an IQueryable has two
components:
Expression—a language-agnostic and datasource-agnostic representation of the
current query's components, in the form of an expression tree.
Provider—an instance of a LINQ provider, which knows how to materialize the
current query into a value or set of values.
In the context of dynamic querying, the provider usually remains the same; the
expression tree of the query differs from query to query.
Expression trees are immutable; if you want a different expression tree—and thus a
different query—you need to translate the existing expression tree to a new one. The
following sections describe specific techniques for querying differently in response to
run-time state:
C#
var length = 1;
var qry = companyNamesSource
.Select(x => x.Substring(0, length))
.Distinct();
Console.WriteLine(string.Join(",", qry));
// prints: C, A, S, W, G, H, M, N, B, T, L, F
length = 2;
Console.WriteLine(string.Join(",", qry));
// prints: Co, Al, So, Ci, Wi, Gr, Ad, Hu, Wo, Ma, No, Bl, Tr, Th, Lu, Fo
The internal expression tree—and thus the query—isn't modified; the query returns
different values only because the value of length changed.
You can replace the original query with the result of an System.Linq.IQueryable<T>-
returning method, to get a new query. You can use run-time state, as in the following
example:
C#
C#
You might also want to compose the various subexpressions using another library such
as LinqKit 's PredicateBuilder :
C#
// using LinqKit;
// string? startsWith = /* ... */;
// string? endsWith = /* ... */;
Constructing an Expression<TDelegate>
When you construct an expression to pass into one of the LINQ methods, you're actually
constructing an instance of System.Linq.Expressions.Expression<TDelegate>, where
TDelegate is some delegate type such as Func<string, bool> , Action , or a custom
delegate type.
C#
1. Define ParameterExpression objects for each of the parameters (if any) in the
lambda expression, using the Parameter factory method.
C#
C#
C#
The following sections describe a scenario in which you might want to construct an
Expression<TDelegate> to pass into a LINQ method. It provides a complete example of
how to do so using the factory methods.
C#
For any of these entity types, you want to filter and return only those entities that have a
given text inside one of their string fields. For Person , you'd want to search the
FirstName and LastName properties:
C#
But for Car , you'd want to search only the Model property:
C#
While you could write one custom function for IQueryable<Person> and another for
IQueryable<Car> , the following function adds this filtering to any existing query,
C#
Because the TextFilter function takes and returns an IQueryable<T> (and not just an
IQueryable), you can add further compile-time-typed query elements after the text filter.
C#
You could also duplicate the LINQ method's functionality, by wrapping the entire tree in
a MethodCallExpression that represents a call to the LINQ method:
C#
return source.Provider.CreateQuery(filteredTree);
}
In this case, you don't have a compile-time T generic placeholder, so you use the
Lambda overload that doesn't require compile-time type information, and which
produces a LambdaExpression instead of an Expression<TDelegate>.
The Dynamic LINQ library
Constructing expression trees using factory methods is relatively complex; it's easier to
compose strings. The Dynamic LINQ library exposes a set of extension methods on
IQueryable corresponding to the standard LINQ methods at Queryable, and which
accept strings in a special syntax instead of expression trees. The library generates the
appropriate expression tree from the string, and can return the resultant translated
IQueryable.
C#
// using System.Linq.Dynamic.Core
That's the goal of this syntax: enable code that reads like a sequence of statements, but
executes in a much more complicated order based on external resource allocation and
when tasks are complete. It's analogous to how people give instructions for processes
that include asynchronous tasks. Throughout this article, you'll use an example of
instructions for making breakfast to see how the async and await keywords make it
easier to reason about code that includes a series of asynchronous instructions. You'd
write the instructions something like the following list to explain how to make a
breakfast:
If you have experience with cooking, you'd execute those instructions asynchronously.
You'd start warming the pan for eggs, then start the bacon. You'd put the bread in the
toaster, then start the eggs. At each step of the process, you'd start a task, then turn
your attention to tasks that are ready for your attention.
Cooking breakfast is a good example of asynchronous work that isn't parallel. One
person (or thread) can handle all these tasks. Continuing the breakfast analogy, one
person can make breakfast asynchronously by starting the next task before the first task
completes. The cooking progresses whether or not someone is watching it. As soon as
you start warming the pan for the eggs, you can begin frying the bacon. Once the bacon
starts, you can put the bread into the toaster.
For a parallel algorithm, you'd need multiple cooks (or threads). One would make the
eggs, one the bacon, and so on. Each one would be focused on just that one task. Each
cook (or thread) would be blocked synchronously waiting for the bacon to be ready to
flip, or the toast to pop.
C#
using System;
using System.Threading.Tasks;
namespace AsyncBreakfast
{
// These classes are intentionally empty for the purpose of this
example. They are simply marker classes for the purpose of demonstration,
contain no properties, and serve no other purpose.
internal class Bacon { }
internal class Coffee { }
internal class Egg { }
internal class Juice { }
internal class Toast { }
class Program
{
static void Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
Computers don't interpret those instructions the same way people do. The computer
will block on each statement until the work is complete before moving on to the next
statement. That creates an unsatisfying breakfast. The later tasks wouldn't be started
until the earlier tasks had been completed. It would take much longer to create the
breakfast, and some items would have gotten cold before being served.
If you want the computer to execute the above instructions asynchronously, you must
write asynchronous code.
These concerns are important for the programs you write today. When you write client
programs, you want the UI to be responsive to user input. Your application shouldn't
make a phone appear frozen while it's downloading data from the web. When you write
server programs, you don't want threads blocked. Those threads could be serving other
requests. Using synchronous code when asynchronous alternatives exist hurts your
ability to scale out less expensively. You pay for those blocked threads.
Let's start by updating this code so that the thread doesn't block while tasks are
running. The await keyword provides a non-blocking way to start a task, then continue
execution when that task completes. A simple asynchronous version of the make a
breakfast code would look like the following snippet:
C#
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
) Important
The total elapsed time is roughly the same as the initial synchronous version. The
code has yet to take advantage of some of the key features of asynchronous
programming.
Tip
7 Note
The Main method returns Task , despite not having a return expression—this is by
design. For more information, see Evaluation of a void-returning async function.
This code doesn't block while the eggs or the bacon are cooking. This code won't start
any other tasks though. You'd still put the toast in the toaster and stare at it until it pops.
But at least, you'd respond to anyone that wanted your attention. In a restaurant where
multiple orders are placed, the cook could start another breakfast while the first is
cooking.
Now, the thread working on the breakfast isn't blocked while awaiting any started task
that hasn't yet finished. For some applications, this change is all that's needed. A GUI
application still responds to the user with just this change. However, for this scenario,
you want more. You don't want each of the component tasks to be executed
sequentially. It's better to start each of the component tasks before awaiting the
previous task's completion.
The System.Threading.Tasks.Task and related types are classes you can use to reason
about tasks that are in progress. That enables you to write code that more closely
resembles the way you'd create breakfast. You'd start cooking the eggs, bacon, and
toast at the same time. As each requires action, you'd turn your attention to that task,
take care of the next action, then wait for something else that requires your attention.
You start a task and hold on to the Task object that represents the work. You'll await
each task before working with its result.
Let's make these changes to the breakfast code. The first step is to store the tasks for
operations when they start, rather than awaiting them:
C#
Juice oj = PourOJ();
Console.WriteLine("Oj is ready");
Console.WriteLine("Breakfast is ready!");
The preceding code won't get your breakfast ready any faster. The tasks are all await ed
as soon as they are started. Next, you can move the await statements for the bacon and
eggs to the end of the method, before serving breakfast:
C#
The asynchronously prepared breakfast took roughly 20 minutes, this time savings is
because some tasks ran concurrently.
The preceding code works better. You start all the asynchronous tasks at once. You await
each task only when you need the results. The preceding code may be similar to code in
a web application that makes requests to different microservices, then combines the
results into a single page. You'll make all the requests immediately, then await all those
tasks and compose the web page.
) Important
C#
return toast;
}
The preceding method has the async modifier in its signature. That signals to the
compiler that this method contains an await statement; it contains asynchronous
operations. This method represents the task that toasts the bread, then adds butter and
jam. This method returns a Task<TResult> that represents the composition of those
three operations. The main block of code now becomes:
C#
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
The previous change illustrated an important technique for working with asynchronous
code. You compose tasks by separating the operations into a new method that returns a
task. You can choose when to await that task. You can start other tasks concurrently.
Asynchronous exceptions
Up to this point, you've implicitly assumed that all these tasks complete successfully.
Asynchronous methods throw exceptions, just like their synchronous counterparts.
Asynchronous support for exceptions and error handling strives for the same goals as
asynchronous support in general: You should write code that reads like a series of
synchronous statements. Tasks throw exceptions when they can't complete successfully.
The client code can catch those exceptions when a started task is awaited . For example,
let's assume that the toaster catches fire while making the toast. You can simulate that
by modifying the ToastBreadAsync method to match the following code:
C#
7 Note
You'll get a warning when you compile the preceding code regarding unreachable
code. That's intentional, because once the toaster catches fire, operations won't
proceed normally.
Run the application after making these changes, and you'll output similar to the
following text:
Console
Pouring coffee
Coffee is ready
Warming the egg pan...
putting 3 slices of bacon in the pan
Cooking first side of bacon...
Putting a slice of bread in the toaster
Putting a slice of bread in the toaster
Start toasting...
Fire! Toast is ruined!
Flipping a slice of bacon
Flipping a slice of bacon
Flipping a slice of bacon
Cooking the second side of bacon...
Cracking 2 eggs
Cooking the eggs ...
Put bacon on plate
Put eggs on plate
Eggs are ready
Bacon is ready
Unhandled exception. System.InvalidOperationException: The toaster is on
fire
at AsyncBreakfast.Program.ToastBreadAsync(Int32 slices) in
Program.cs:line 65
at AsyncBreakfast.Program.MakeToastWithButterAndJamAsync(Int32 number) in
Program.cs:line 36
at AsyncBreakfast.Program.Main(String[] args) in Program.cs:line 24
at AsyncBreakfast.Program.<Main>(String[] args)
You'll notice quite a few tasks are completed between when the toaster catches fire and
the exception is observed. When a task that runs asynchronously throws an exception,
that Task is faulted. The Task object holds the exception thrown in the Task.Exception
property. Faulted tasks throw an exception when they're awaited.
When code running asynchronously throws an exception, that exception is stored in the
Task . The Task.Exception property is a System.AggregateException because more than
one exception may be thrown during asynchronous work. Any exception thrown is
added to the AggregateException.InnerExceptions collection. If that Exception property
is null, a new AggregateException is created and the thrown exception is the first item in
the collection.
The most common scenario for a faulted task is that the Exception property contains
exactly one exception. When code awaits a faulted task, the first exception in the
AggregateException.InnerExceptions collection is rethrown. That's why the output from
this example shows an InvalidOperationException instead of an AggregateException .
Extracting the first inner exception makes working with asynchronous methods as
similar as possible to working with their synchronous counterparts. You can examine the
Exception property in your code when your scenario may generate multiple exceptions.
Tip
Before going on, comment out these two lines in your ToastBreadAsync method. You
don't want to start another fire:
C#
C#
Another option is to use WhenAny, which returns a Task<Task> that completes when
any of its arguments complete. You can await the returned task, knowing that it has
already finished. The following code shows how you could use WhenAny to await the
first task to finish and then process its result. After processing the result from the
completed task, you remove that completed task from the list of tasks passed to
WhenAny .
C#
Near the end, you see the line await finishedTask; . The line await Task.WhenAny
doesn't await the finished task. It await s the Task returned by Task.WhenAny . The result
of Task.WhenAny is the task that has completed (or faulted). You should await that task
again, even though you know it's finished running. That's how you retrieve its result, or
ensure that the exception causing it to fault gets thrown.
After all those changes, the final version of the code looks like this:
C#
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace AsyncBreakfast
{
// These classes are intentionally empty for the purpose of this
example. They are simply marker classes for the purpose of demonstration,
contain no properties, and serve no other purpose.
internal class Bacon { }
internal class Coffee { }
internal class Egg { }
internal class Juice { }
internal class Toast { }
class Program
{
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");
Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
return toast;
}
This final code is asynchronous. It more accurately reflects how a person would cook a
breakfast. Compare the preceding code with the first code sample in this article. The
core actions are still clear from reading the code. You can read this code the same way
you'd read those instructions for making a breakfast at the beginning of this article. The
language features for async and await provide the translation every person makes to
follow those written instructions: start tasks as you can and don't block waiting for tasks
to complete.
Next steps
Explore real world scenarios for asynchronous programs
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Asynchronous programming scenarios
Article • 10/12/2023
If you have any I/O-bound needs (such as requesting data from a network, accessing a
database, or reading and writing to a file system), you'll want to utilize asynchronous
programming. You could also have CPU-bound code, such as performing an expensive
calculation, which is also a good scenario for writing async code.
For I/O-bound code, you await an operation that returns a Task or Task<T> inside
of an async method.
For CPU-bound code, you await an operation that is started on a background
thread with the Task.Run method.
The await keyword is where the magic happens. It yields control to the caller of the
method that performed await , and it ultimately allows a UI to be responsive or a service
to be elastic. While there are ways to approach async code other than async and await ,
this article focuses on the language-level constructs.
7 Note
C#
The code expresses the intent (downloading data asynchronously) without getting
bogged down in interacting with Task objects.
The best way to handle this is to start a background thread, which does the work using
Task.Run , and await its result using await . This allows the UI to feel smooth as the work
is being done.
C#
This code clearly expresses the intent of the button's click event, it doesn't require
managing a background thread manually, and it does so in a non-blocking way.
Here are two questions you should ask before you write any code:
1. Will your code be "waiting" for something, such as data from a database?
If the work you have is I/O-bound, use async and await without Task.Run . You should
not use the Task Parallel Library.
If the work you have is CPU-bound and you care about responsiveness, use async and
await , but spawn off the work on another thread with Task.Run . If the work is
appropriate for concurrency and parallelism, also consider using the Task Parallel Library.
Additionally, you should always measure the execution of your code. For example, you
may find yourself in a situation where your CPU-bound work is not costly enough
compared with the overhead of context switches when multithreading. Every choice has
its tradeoff, and you should pick the correct tradeoff for your situation.
More examples
The following examples demonstrate various ways you can write async code in C#. They
cover a few different scenarios you may come across.
7 Note
If you plan on doing HTML parsing in production code, don't use regular
expressions. Use a parsing library instead.
C#
[HttpGet, Route("DotNetCount")]
static public async Task<int> GetDotNetCount(string URL)
{
// Suspends GetDotNetCount() to allow the caller (the web server)
// to accept another request, rather than blocking on this one.
var html = await s_httpClient.GetStringAsync(URL);
return Regex.Matches(html, @"\.NET").Count;
}
Here's the same scenario written for a Universal Windows App, which performs the same
task when a Button is pressed:
C#
// Any other work on the UI thread can be done here, such as enabling a
Progress Bar.
// This is important to do here, before the "await" call, so that the
user
// sees the progress bar before execution of this method is yielded.
NetworkProgressBar.IsEnabled = true;
NetworkProgressBar.Visibility = Visibility.Visible;
NetworkProgressBar.IsEnabled = false;
NetworkProgressBar.Visibility = Visibility.Collapsed;
}
This example shows how you might grab User data for a set of userId s.
C#
C#
Although it's less code, use caution when mixing LINQ with asynchronous code. Because
LINQ uses deferred (lazy) execution, async calls won't happen immediately as they do in
a foreach loop unless you force the generated sequence to iterate with a call to
.ToList() or .ToArray() . The above example uses Enumerable.ToArray to perform the
query eagerly and store the results in an array. That forces the code id =>
GetUserAsync(id) to run and start the task.
async methods need to have an await keyword in their body or they will never
yield!
This is important to keep in mind. If await is not used in the body of an async
method, the C# compiler generates a warning, but the code compiles and runs as
if it were a normal method. This is incredibly inefficient, as the state machine
generated by the C# compiler for the async method is not accomplishing anything.
Add "Async" as the suffix of every async method name you write.
This is the convention used in .NET to more easily differentiate synchronous and
asynchronous methods. Certain methods that aren't explicitly called by your code
(such as event handlers or web controller methods) don't necessarily apply.
Because they are not explicitly called by your code, being explicit about their
naming isn't as important.
async void is the only way to allow asynchronous event handlers to work because
events do not have return types (thus cannot make use of Task and Task<T> ). Any
other use of async void does not follow the TAP model and can be challenging to
use, such as:
Exceptions thrown in an async void method can't be caught outside of that
method.
async void methods are difficult to test.
async void methods can cause bad side effects if the caller isn't expecting them
to be async.
Lambda expressions in LINQ use deferred execution, meaning code could end up
executing at a time when you're not expecting it to. The introduction of blocking
tasks into this can easily result in a deadlock if not written correctly. Additionally,
the nesting of asynchronous code like this can also make it more difficult to reason
about the execution of the code. Async and LINQ are powerful but should be used
together as carefully and clearly as possible.
Blocking the current thread as a means to wait for a Task to complete can result in
deadlocks and blocked context threads and can require more complex error-
handling. The following table provides guidance on how to deal with waiting for
tasks in a non-blocking way:
ノ Expand table
Use this... Instead of this... When wishing to do this...
Don't depend on the state of global objects or the execution of certain methods.
Instead, depend only on the return values of methods. Why?
Code will be easier to reason about.
Code will be easier to test.
Mixing async and synchronous code is far simpler.
Race conditions can typically be avoided altogether.
Depending on return values makes coordinating async code simple.
(Bonus) it works really well with dependency injection.
Complete example
The following code is the complete text of the Program.cs file for the example.
C#
using System.Text.RegularExpressions;
using System.Windows;
using Microsoft.AspNetCore.Mvc;
class Button
{
public Func<object, object, Task>? Clicked
{
get;
internal set;
}
}
class DamageResult
{
public int Damage
{
get { return 0; }
}
}
class User
{
public bool isEnabled
{
get;
set;
}
public int id
{
get;
set;
}
}
// <GetUsersForDataset>
private static async Task<User> GetUserAsync(int userId)
{
// Code omitted:
//
// Given a user Id {userId}, retrieves a User object corresponding
// to the entry in the database with {userId} as its Id.
// <GetUsersForDatasetByLINQ>
private static async Task<User[]> GetUsersAsyncByLINQ(IEnumerable<int>
userIds)
{
var getUserTasks = userIds.Select(id => GetUserAsync(id)).ToArray();
return await Task.WhenAll(getUserTasks);
}
// </GetUsersForDatasetByLINQ>
// <ExtractDataFromNetwork>
[HttpGet, Route("DotNetCount")]
static public async Task<int> GetDotNetCount(string URL)
{
// Suspends GetDotNetCount() to allow the caller (the web server)
// to accept another request, rather than blocking on this one.
var html = await s_httpClient.GetStringAsync(URL);
return Regex.Matches(html, @"\.NET").Count;
}
// </ExtractDataFromNetwork>
Console.WriteLine("Application ending.");
}
}
// Example output:
//
// Application started.
// Counting '.NET' phrase in websites...
// https://learn.microsoft.com: 0
// https://learn.microsoft.com/aspnet/core: 57
// https://learn.microsoft.com/azure: 1
// https://learn.microsoft.com/azure/devops: 2
// https://learn.microsoft.com/dotnet: 83
// https://learn.microsoft.com/dotnet/desktop/wpf/get-started/create-app-
visual-studio: 31
// https://learn.microsoft.com/education: 0
// https://learn.microsoft.com/shows/net-core-101/what-is-net: 42
// https://learn.microsoft.com/enterprise-mobility-security: 0
// https://learn.microsoft.com/gaming: 0
// https://learn.microsoft.com/graph: 0
// https://learn.microsoft.com/microsoft-365: 0
// https://learn.microsoft.com/office: 0
// https://learn.microsoft.com/powershell: 0
// https://learn.microsoft.com/sql: 0
// https://learn.microsoft.com/surface: 0
// https://dotnetfoundation.org: 16
// https://learn.microsoft.com/visualstudio: 0
// https://learn.microsoft.com/windows: 0
// https://learn.microsoft.com/maui: 6
// Total: 238
// Retrieving User objects with list of IDs...
// 1: isEnabled= False
// 2: isEnabled= False
// 3: isEnabled= False
// 4: isEnabled= False
// 5: isEnabled= False
// 6: isEnabled= False
// 7: isEnabled= False
// 8: isEnabled= False
// 9: isEnabled= False
// 0: isEnabled= False
// Application ending.
Other resources
The Task asynchronous programming model (C#).
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Task asynchronous programming model
Article • 02/13/2023
You can avoid performance bottlenecks and enhance the overall responsiveness of your
application by using asynchronous programming. However, traditional techniques for
writing asynchronous applications can be complicated, making them difficult to write,
debug, and maintain.
This topic provides an overview of when and how to use async programming and
includes links to support topics that contain details and examples.
The following table shows typical areas where asynchronous programming improves
responsiveness. The listed APIs from .NET and the Windows Runtime contain methods
that support async programming.
Application area .NET types with async Windows Runtime types with async
methods methods
BitmapDecoder
Asynchrony proves especially valuable for applications that access the UI thread because
all UI-related activity usually shares one thread. If any process is blocked in a
synchronous application, all are blocked. Your application stops responding, and you
might conclude that it has failed when instead it's just waiting.
When you use asynchronous methods, the application continues to respond to the UI.
You can resize or minimize a window, for example, or you can close the application if
you don't want to wait for it to finish.
The async-based approach adds the equivalent of an automatic transmission to the list
of options that you can choose from when designing asynchronous operations. That is,
you get all the benefits of traditional asynchronous programming but with much less
effort from the developer.
The following example shows an async method. Almost everything in the code should
look familiar to you.
You can find a complete Windows Presentation Foundation (WPF) example available for
download from Asynchronous programming with async and await in C#.
C#
Task<string> getStringTask =
client.GetStringAsync("https://learn.microsoft.com/dotnet");
DoIndependentWork();
string contents = await getStringTask;
return contents.Length;
}
void DoIndependentWork()
{
Console.WriteLine("Working...");
}
You can learn several practices from the preceding sample. Start with the method
signature. It includes the async modifier. The return type is Task<int> (See "Return
Types" section for more options). The method name ends in Async . In the body of the
method, GetStringAsync returns a Task<string> . That means that when you await the
task you'll get a string ( contents ). Before awaiting the task, you can do work that
doesn't rely on the string from GetStringAsync .
The return statement specifies an integer result. Any methods that are awaiting
GetUrlContentLengthAsync retrieve the length value.
C#
The following characteristics summarize what makes the previous example an async
method:
For more information, see the Return types and parameters section.
The method usually includes at least one await expression, which marks a point
where the method can't continue until the awaited asynchronous operation is
complete. In the meantime, the method is suspended, and control returns to the
method's caller. The next section of this topic illustrates what happens at the
suspension point.
In async methods, you use the provided keywords and types to indicate what you want
to do, and the compiler does the rest, including keeping track of what must happen
when control returns to an await point in a suspended method. Some routine processes,
such as loops and exception handling, can be difficult to handle in traditional
asynchronous code. In an async method, you write these elements much as you would
in a synchronous solution, and the problem is solved.
For more information about asynchrony in previous versions of .NET Framework, see TPL
and traditional .NET Framework asynchronous programming.
The numbers in the diagram correspond to the following steps, initiated when a calling
method calls the async method.
represents the ongoing process for the call to GetStringAsync , with a commitment
to produce an actual string value when the work is complete.
5. DoIndependentWork is a synchronous method that does its work and returns to its
caller.
6. GetUrlContentLengthAsync has run out of work that it can do without a result from
getStringTask . GetUrlContentLengthAsync next wants to calculate and return the
length of the downloaded string, but the method can't calculate that value until
the method has the string.
promise to produce an integer result that's the length of the downloaded string.
7 Note
Inside the calling method the processing pattern continues. The caller might do
other work that doesn't depend on the result from GetUrlContentLengthAsync
before awaiting that result, or the caller might await immediately. The calling
method is waiting for GetUrlContentLengthAsync , and GetUrlContentLengthAsync is
waiting for GetStringAsync .
7. GetStringAsync completes and produces a string result. The string result isn't
returned by the call to GetStringAsync in the way that you might expect.
(Remember that the method already returned a task in step 3.) Instead, the string
result is stored in the task that represents the completion of the method,
getStringTask . The await operator retrieves the result from getStringTask . The
8. When GetUrlContentLengthAsync has the string result, the method can calculate
the length of the string. Then the work of GetUrlContentLengthAsync is also
complete, and the waiting event handler can resume. In the full example at the end
of the topic, you can confirm that the event handler retrieves and prints the value
of the length result. If you are new to asynchronous programming, take a minute
to consider the difference between synchronous and asynchronous behavior. A
synchronous method returns when its work is complete (step 5), but an async
method returns a task value when its work is suspended (steps 3 and 6). When the
async method eventually completes its work, the task is marked as completed and
the result, if any, is stored in the task.
The Windows Runtime also contains many methods that you can use with async and
await in Windows apps. For more information, see Threading and async programming
for UWP development, and Asynchronous programming (Windows Store apps) and
Quickstart: Calling asynchronous APIs in C# or Visual Basic if you use earlier versions of
the Windows Runtime.
Threads
Async methods are intended to be non-blocking operations. An await expression in an
async method doesn't block the current thread while the awaited task is running.
Instead, the expression signs up the rest of the method as a continuation and returns
control to the caller of the async method.
The async and await keywords don't cause additional threads to be created. Async
methods don't require multithreading because an async method doesn't run on its own
thread. The method runs on the current synchronization context and uses time on the
thread only when the method is active. You can use Task.Run to move CPU-bound work
to a background thread, but a background thread doesn't help with a process that's just
waiting for results to become available.
The marked async method can use await to designate suspension points. The
await operator tells the compiler that the async method can't continue past that
The marked async method can itself be awaited by methods that call it.
An async method typically contains one or more occurrences of an await operator, but
the absence of await expressions doesn't cause a compiler error. If an async method
doesn't use an await operator to mark a suspension point, the method executes as a
synchronous method does, despite the async modifier. The compiler issues a warning
for such methods.
async and await are contextual keywords. For more information and examples, see the
following topics:
async
await
You specify Task<TResult> as the return type if the method contains a return statement
that specifies an operand of type TResult .
You use Task as the return type if the method has no return statement or has a return
statement that doesn't return an operand.
You can also specify any other return type, provided that the type includes a GetAwaiter
method. ValueTask<TResult> is an example of such a type. It is available in the
System.Threading.Tasks.Extension NuGet package.
The following example shows how you declare and call a method that returns a
Task<TResult> or a Task:
C#
return hours;
}
Each returned task represents ongoing work. A task encapsulates information about the
state of the asynchronous process and, eventually, either the final result from the
process or the exception that the process raises if it doesn't succeed.
An async method can also have a void return type. This return type is used primarily to
define event handlers, where a void return type is required. Async event handlers often
serve as the starting point for async programs.
An async method that has a void return type can't be awaited, and the caller of a void-
returning method can't catch any exceptions that the method throws.
An async method can't declare in, ref or out parameters, but the method can call
methods that have such parameters. Similarly, an async method can't return a value by
reference, although it can call methods with ref return values.
For more information and examples, see Async return types (C#).
Asynchronous APIs in Windows Runtime programming have one of the following return
types, which are similar to tasks:
Naming convention
By convention, methods that return commonly awaitable types (for example, Task ,
Task<T> , ValueTask , ValueTask<T> ) should have names that end with "Async". Methods
that start an asynchronous operation but do not return an awaitable type should not
have names that end with "Async", but may start with "Begin", "Start", or some other
verb to suggest this method does not return or throw the result of the operation.
You can ignore the convention where an event, base class, or interface contract suggests
a different name. For example, you shouldn't rename common event handlers, such as
OnButtonClick .
How to make multiple web requests in Demonstrates how to start several tasks at the same
parallel by using async and await (C#) time.
Async return types (C#) Illustrates the types that async methods can return,
and explains when each type is appropriate.
Cancel tasks with a cancellation token as a Shows how to add the following functionality to
signaling mechanism. your async solution:
Using async for file access (C#) Lists and demonstrates the benefits of using async
and await to access files.
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Async return types (C#)
Article • 04/08/2023
Task, for an async method that performs an operation but returns no value.
Task<TResult>, for an async method that returns a value.
void , for an event handler.
Any type that has an accessible GetAwaiter method. The object returned by the
GetAwaiter method must implement the
System.Runtime.CompilerServices.ICriticalNotifyCompletion interface.
IAsyncEnumerable<T>, for an async method that returns an async stream.
For more information about async methods, see Asynchronous programming with async
and await (C#).
Several other types also exist that are specific to Windows workloads:
C#
public static async Task DisplayCurrentInfoAsync()
{
await WaitAndApologizeAsync();
Console.WriteLine($"Today is {DateTime.Now:D}");
Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
Console.WriteLine("The current temperature is 76 degrees.");
}
statement.
You can separate the call to WaitAndApologizeAsync from the application of an await
operator, as the following code shows. However, remember that a Task doesn't have a
Result property, and that no value is produced when an await operator is applied to a
Task .
The following code separates calling the WaitAndApologizeAsync method from awaiting
the task that the method returns.
C#
string output =
$"Today is {DateTime.Now:D}\n" +
$"The current time is {DateTime.Now.TimeOfDay:t}\n" +
"The current temperature is 76 degrees.\n";
await waitAndApologizeTask;
Console.WriteLine(output);
Task<TResult> return type
The Task<TResult> return type is used for an async method that contains a return
statement in which the operand is TResult .
C#
Console.WriteLine(message);
}
int leisureHours =
today is DayOfWeek.Saturday || today is DayOfWeek.Sunday
? 16 : 5;
return leisureHours;
}
// Example output:
// Today is Wednesday, May 24, 2017
// Today's hours of leisure: 5
leisureHours ) that's stored in the task returned by the GetLeisureHours method. For
more information about await expressions, see await.
You can better understand how await retrieves the result from a Task<T> by separating
the call to GetLeisureHoursAsync from the application of await , as the following code
shows. A call to method GetLeisureHoursAsync that isn't immediately awaited returns a
Task<int> , as you would expect from the declaration of the method. The task is
assigned to the getLeisureHoursTask variable in the example. Because
getLeisureHoursTask is a Task<TResult>, it contains a Result property of type TResult .
In this case, TResult represents an integer type. When await is applied to
getLeisureHoursTask , the await expression evaluates to the contents of the Result
property of getLeisureHoursTask . The value is assigned to the ret variable.
) Important
The Result property is a blocking property. If you try to access it before its task is
finished, the thread that's currently active is blocked until the task completes and
the value is available. In most cases, you should access the value by using await
instead of accessing the property directly.
The previous example retrieved the value of the Result property to block the main
thread so that the Main method could print the message to the console before the
application ended.
C#
string message =
$"Today is {DateTime.Today:D}\n" +
"Today's hours of leisure: " +
$"{await getLeisureHoursTask}";
Console.WriteLine(message);
The caller of a void-returning async method can't catch exceptions thrown from the
method. Such unhandled exceptions are likely to cause your application to fail. If a
method that returns a Task or Task<TResult> throws an exception, the exception is
stored in the returned task. The exception is rethrown when the task is awaited. Make
sure that any async method that can produce an exception has a return type of Task or
Task<TResult> and that calls to the method are awaited.
The following example shows the behavior of an async event handler. In the example
code, an async event handler must let the main thread know when it finishes. Then the
main thread can wait for an async event handler to complete before exiting the
program.
C#
button.Clicked += OnButtonClicked1;
button.Clicked += OnButtonClicked2Async;
button.Clicked += OnButtonClicked3;
await secondHandlerFinished;
}
C#
class Program
{
static readonly Random s_rnd = new Random();
Writing a generalized async return type is an advanced scenario, and is targeted for use
in specialized environments. Consider using the Task , Task<T> , and ValueTask<T> types
instead, which cover most scenarios for asynchronous code.
In C# 10 and later, you can apply the AsyncMethodBuilder attribute to an async method
(instead of the async return type declaration) to override the builder for that type.
Typically you'd apply this attribute to use a different builder provided in the .NET
runtime.
Async streams with IAsyncEnumerable<T>
An async method may return an async stream, represented by IAsyncEnumerable<T>.
An async stream provides a way to enumerate items read from a stream when elements
are generated in chunks with repeated asynchronous calls. The following example shows
an async method that generates an async stream:
C#
The preceding example reads lines from a string asynchronously. Once each line is read,
the code enumerates each word in the string. Callers would enumerate each word using
the await foreach statement. The method awaits when it needs to asynchronously read
the next line from the source string.
See also
FromResult
Process asynchronous tasks as they complete
Asynchronous programming with async and await (C#)
async
await
Process asynchronous tasks as they
complete (C#)
Article • 05/23/2023
By using Task.WhenAny, you can start multiple tasks at the same time and process them
one by one as they're completed rather than process them in the order in which they're
started.
The following example uses a query to create a collection of tasks. Each task downloads
the contents of a specified website. In each iteration of a while loop, an awaited call to
WhenAny returns the task in the collection of tasks that finishes its download first. That
task is removed from the collection and processed. The loop repeats until the collection
contains no more tasks.
Prerequisites
You can follow this tutorial by using one of the following options:
Visual Studio 2022 with the .NET desktop development workload installed. The
.NET SDK is automatically installed when you select this workload.
The .NET SDK with a code editor of your choice, such as Visual Studio Code .
Open the Program.cs file in your code editor, and replace the existing code with this
code:
C#
using System.Diagnostics;
namespace ProcessTasksAsTheyFinish;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Add fields
In the Program class definition, add the following two fields:
C#
The HttpClient exposes the ability to send HTTP requests and receive HTTP responses.
The s_urlList holds all of the URLs that the application plans to process.
C#
static Task Main() => SumPageSizesAsync();
The updated Main method is now considered an Async main, which allows for an
asynchronous entry point into the executable. It is expressed as a call to
SumPageSizesAsync .
C#
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
int total = 0;
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
stopwatch.Stop();
The while loop removes one of the tasks in each iteration. After every task has
completed, the loop ends. The method starts by instantiating and starting a Stopwatch.
It then includes a query that, when executed, creates a collection of tasks. Each call to
ProcessUrlAsync in the following code returns a Task<TResult>, where TResult is an
integer:
C#
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
Due to deferred execution with the LINQ, you call Enumerable.ToList to start each task.
C#
The while loop performs the following steps for each task in the collection:
1. Awaits a call to WhenAny to identify the first task in the collection that has finished
its download.
C#
C#
downloadTasks.Remove(finishedTask);
already complete, but you await it to retrieve the length of the downloaded
website, as the following example shows. If the task is faulted, await will throw the
first child exception stored in the AggregateException , unlike reading the
Task<TResult>.Result property, which would throw the AggregateException .
C#
C#
static async Task<int> ProcessUrlAsync(string url, HttpClient client)
{
byte[] content = await client.GetByteArrayAsync(url);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
For any given URL, the method will use the client instance provided to get the
response as a byte[] . The length is returned after the URL and length is written to the
console.
Run the program several times to verify that the downloaded lengths don't always
appear in the same order.
U Caution
You can use WhenAny in a loop, as described in the example, to solve problems that
involve a small number of tasks. However, other approaches are more efficient if
you have a large number of tasks to process. For more information and examples,
see Processing tasks as they complete .
Complete example
The following code is the complete text of the Program.cs file for the example.
C#
using System.Diagnostics;
await SumPageSizesAsync();
IEnumerable<Task<int>> downloadTasksQuery =
from url in s_urlList
select ProcessUrlAsync(url, s_client);
int total = 0;
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
total += await finishedTask;
}
stopwatch.Stop();
return content.Length;
}
// Example output:
// https://learn.microsoft.com 132,517
// https://learn.microsoft.com/powershell 57,375
// https://learn.microsoft.com/gaming 33,549
// https://learn.microsoft.com/aspnet/core 88,714
// https://learn.microsoft.com/surface 39,840
// https://learn.microsoft.com/enterprise-mobility-security 30,903
// https://learn.microsoft.com/microsoft-365 67,867
// https://learn.microsoft.com/windows 26,816
// https://learn.microsoft.com/maui 57,958
// https://learn.microsoft.com/dotnet 78,706
// https://learn.microsoft.com/graph 48,277
// https://learn.microsoft.com/dynamics365 49,042
// https://learn.microsoft.com/office 67,867
// https://learn.microsoft.com/system-center 42,887
// https://learn.microsoft.com/education 38,636
// https://learn.microsoft.com/azure 421,663
// https://learn.microsoft.com/visualstudio 30,925
// https://learn.microsoft.com/sql 54,608
// https://learn.microsoft.com/azure/devops 86,034
See also
WhenAny
Asynchronous programming with async and await (C#)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Asynchronous file access (C#)
Article • 02/13/2023
You can use the async feature to access files. By using the async feature, you can call
into asynchronous methods without using callbacks or splitting your code across
multiple methods or lambda expressions. To make synchronous code asynchronous, you
just call an asynchronous method instead of a synchronous method and add a few
keywords to the code.
You might consider the following reasons for adding asynchrony to file access calls:
You can't use this option with StreamReader and StreamWriter if you open them directly
by specifying a file path. However, you can use this option if you provide them a Stream
that the FileStream class opened. Asynchronous calls are faster in UI apps even if a
thread pool thread is blocked, because the UI thread isn't blocked during the wait.
Write text
The following examples write text to a file. At each await statement, the method
immediately exits. When the file I/O is complete, the method resumes at the statement
that follows the await statement. The async modifier is in the definition of methods that
use the await statement.
Simple example
C#
The first statement returns a task and causes file processing to start. The second
statement with the await causes the method to immediately exit and return a different
task. When the file processing later completes, execution returns to the statement that
follows the await.
Read text
The following examples read text from a file.
Simple example
C#
Console.WriteLine(text);
}
C#
return sb.ToString();
}
Simple example
C#
writeTaskList.Add(File.WriteAllTextAsync(filePath, text));
}
await Task.WhenAll(writeTaskList);
}
The example closes all FileStream instances in a finally block after the tasks are
complete. If each FileStream was instead created in a using statement, the FileStream
might be disposed of before the task was complete.
Any performance boost is almost entirely from the parallel processing and not the
asynchronous processing. The advantages of asynchrony are that it doesn't tie up
multiple threads, and that it doesn't tie up the user interface thread.
C#
try
{
string folder = Directory.CreateDirectory("tempfolder").Name;
IList<Task> writeTaskList = new List<Task>();
var sourceStream =
new FileStream(
filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true);
writeTaskList.Add(writeTask);
}
await Task.WhenAll(writeTaskList);
}
finally
{
foreach (FileStream sourceStream in sourceStreams)
{
sourceStream.Close();
}
}
}
When using the WriteAsync and ReadAsync methods, you can specify a
CancellationToken, which you can use to cancel the operation mid-stream. For more
information, see Cancellation in managed threads.
See also
Asynchronous programming with async and await (C#)
Async return types (C#)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Cancel a list of tasks
Article • 06/08/2024
You can cancel an async console application if you don't want to wait for it to finish. By
following the example in this topic, you can add a cancellation to an application that
downloads the contents of a list of websites. You can cancel many tasks by associating
the CancellationTokenSource instance with each task. If you select the Enter key, you
cancel all tasks that aren't yet complete.
Prerequisites
This tutorial requires the following:
C#
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Add fields
In the Program class definition, add these three fields:
C#
C#
static async Task Main()
{
Console.WriteLine("Application started.");
Console.WriteLine("Press the ENTER key to cancel...\n");
Console.WriteLine("Application ending.");
}
The updated Main method is now considered an Async main, which allows for an
asynchronous entry point into the executable. It writes a few instructional messages to
the console, then declares a Task instance named cancelTask , which will read console
key strokes. If the Enter key is pressed, a call to CancellationTokenSource.Cancel() is
made. This will signal cancellation. Next, the sumPageSizesTask variable is assigned from
the SumPageSizesAsync method. Both tasks are then passed to Task.WhenAny(Task[]),
which will continue when any of the two tasks have completed.
The next block of code ensures that the application doesn't exit until the cancellation
has been processed. If the first task to complete is the cancelTask , the sumPageSizeTask
is awaited. If it was cancelled, when awaited it throws a
System.Threading.Tasks.TaskCanceledException. The block catches that exception, and
prints a message.
C#
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client,
s_cts.Token);
total += contentLength;
}
stopwatch.Stop();
The method starts by instantiating and starting a Stopwatch. It then loops through each
URL in the s_urlList and calls ProcessUrlAsync . With each iteration, the s_cts.Token is
passed into the ProcessUrlAsync method and the code returns a Task<TResult>, where
TResult is an integer:
C#
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token);
total += contentLength;
}
return content.Length;
}
For any given URL, the method will use the client instance provided to get the
response as a byte[] . The CancellationToken instance is passed into the
HttpClient.GetAsync(String, CancellationToken) and
HttpContent.ReadAsByteArrayAsync() methods. The token is used to register for
requested cancellation. The length is returned after the URL and length is written to the
console.
Application started.
Press the ENTER key to cancel...
https://learn.microsoft.com 37,357
https://learn.microsoft.com/aspnet/core 85,589
https://learn.microsoft.com/azure 398,939
https://learn.microsoft.com/azure/devops 73,663
https://learn.microsoft.com/dotnet 67,452
https://learn.microsoft.com/dynamics365 48,582
https://learn.microsoft.com/education 22,924
Application ending.
Complete example
The following code is the complete text of the Program.cs file for the example.
C#
using System.Diagnostics;
class Program
{
static readonly CancellationTokenSource s_cts = new
CancellationTokenSource();
Console.WriteLine("Application ending.");
}
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client,
s_cts.Token);
total += contentLength;
}
stopwatch.Stop();
return content.Length;
}
}
See also
CancellationToken
CancellationTokenSource
Asynchronous programming with async and await (C#)
Next steps
Cancel async tasks after a period of time (C#)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Cancel async tasks after a period of time
Article • 09/08/2023
You can cancel an asynchronous operation after a period of time by using the
CancellationTokenSource.CancelAfter method if you don't want to wait for the operation
to finish. This method schedules the cancellation of any associated tasks that aren't
complete within the period of time that's designated by the CancelAfter expression.
This example adds to the code that's developed in Cancel a list of tasks (C#) to
download a list of websites and to display the length of the contents of each one.
Prerequisites
This tutorial requires the following:
You're expected to have created an application in the Cancel a list of tasks (C#)
tutorial
.NET 5 or later SDK
Integrated development environment (IDE)
We recommend Visual Studio or Visual Studio Code
C#
try
{
s_cts.CancelAfter(3500);
await SumPageSizesAsync();
}
catch (OperationCanceledException)
{
Console.WriteLine("\nTasks cancelled: timed out.\n");
}
finally
{
s_cts.Dispose();
}
Console.WriteLine("Application ending.");
}
The updated Main method writes a few instructional messages to the console. Within
the try-catch, a call to CancellationTokenSource.CancelAfter(Int32) schedules a
cancellation. This will signal cancellation after a period of time.
Next, the SumPageSizesAsync method is awaited. If processing all of the URLs occurs
faster than the scheduled cancellation, the application ends. However, if the scheduled
cancellation is triggered before all of the URLs are processed, a
OperationCanceledException is thrown.
Application started.
https://learn.microsoft.com 37,357
https://learn.microsoft.com/aspnet/core 85,589
https://learn.microsoft.com/azure 398,939
https://learn.microsoft.com/azure/devops 73,663
Application ending.
Complete example
The following code is the complete text of the Program.cs file for the example.
C#
using System.Diagnostics;
class Program
{
static readonly CancellationTokenSource s_cts = new
CancellationTokenSource();
static readonly HttpClient s_client = new HttpClient
{
MaxResponseContentBufferSize = 1_000_000
};
try
{
s_cts.CancelAfter(3500);
await SumPageSizesAsync();
}
catch (OperationCanceledException)
{
Console.WriteLine("\nTasks cancelled: timed out.\n");
}
finally
{
s_cts.Dispose();
}
Console.WriteLine("Application ending.");
}
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client,
s_cts.Token);
total += contentLength;
}
stopwatch.Stop();
return content.Length;
}
}
See also
CancellationToken
CancellationTokenSource
Asynchronous programming with async and await (C#)
Cancel a list of tasks (C#)
Tutorial: Generate and consume async
streams using C# and .NET
Article • 03/25/2023
Async streams model a streaming source of data. Data streams often retrieve or
generate elements asynchronously. They provide a natural programming model for
asynchronous streaming data sources.
Prerequisites
You'll need to set up your machine to run .NET, including the C# compiler. The C#
compiler is available with Visual Studio 2022 or the .NET SDK .
You'll need to create a GitHub access token so that you can access the GitHub
GraphQL endpoint. Select the following permissions for your GitHub Access Token:
repo:status
public_repo
Save the access token in a safe place so you can use it to gain access to the GitHub API
endpoint.
2 Warning
Keep your personal access token secure. Any software with your personal access
token could make GitHub API calls using your access rights.
This tutorial assumes you're familiar with C# and .NET, including either Visual Studio or
the .NET CLI.
The starter application is a console application that uses the GitHub GraphQL
interface to retrieve recent issues written in the dotnet/docs repository. Start by
looking at the following code for the starter app Main method:
C#
try
{
var results = await RunPagedQueryAsync(client, PagedIssueQuery,
"docs",
cancellationSource.Token, progressReporter);
foreach(var issue in results)
Console.WriteLine(issue);
}
catch (OperationCanceledException)
{
Console.WriteLine("Work has been cancelled");
}
}
You can either set a GitHubKey environment variable to your personal access token, or
you can replace the last argument in the call to GetEnvVariable with your personal
access token. Don't put your access code in source code if you'll be sharing the source
with others. Never upload access codes to a shared source repository.
After creating the GitHub client, the code in Main creates a progress reporting object
and a cancellation token. Once those objects are created, Main calls RunPagedQueryAsync
to retrieve the most recent 250 created issues. After that task has finished, the results
are displayed.
When you run the starter application, you can make some important observations about
how this application runs. You'll see progress reported for each page returned from
GitHub. You can observe a noticeable pause before GitHub returns each new page of
issues. Finally, the issues are displayed only after all 10 pages have been retrieved from
GitHub.
C#
JObject results =
JObject.Parse(response.HttpResponse.Body.ToString()!);
The very first thing this method does is to create the POST object, using the
GraphQLRequest class:
C#
[JsonProperty("variables")]
public IDictionary<string, object> Variables { get; } = new
Dictionary<string, object>();
which helps to form the POST object body, and correctly convert it to JSON presented
as single string with the ToJsonText method, which removes all newline characters from
your request body marking them with the \ (backslash) escape character.
Let's concentrate on the paging algorithm and async structure of the preceding code.
(You can consult the GitHub GraphQL documentation for details on the GitHub
GraphQL API.) The RunPagedQueryAsync method enumerates the issues from most recent
to oldest. It requests 25 issues per page and examines the pageInfo structure of the
response to continue with the previous page. That follows GraphQL's standard paging
support for multi-page responses. The response includes a pageInfo object that
includes a hasPreviousPages value and a startCursor value used to request the
previous page. The issues are in the nodes array. The RunPagedQueryAsync method
appends these nodes to an array that contains all the results from all pages.
There are several elements in this code that can be improved. Most importantly,
RunPagedQueryAsync must allocate storage for all the issues returned. This sample stops
at 250 issues because retrieving all open issues would require much more memory to
store all the retrieved issues. The protocols for supporting progress reports and
cancellation make the algorithm harder to understand on its first reading. More types
and APIs are involved. You must trace the communications through the
CancellationTokenSource and its associated CancellationToken to understand where
cancellation is requested and where it's granted.
These new language features depend on three new interfaces added to .NET Standard
2.1 and implemented in .NET Core 3.0:
System.Collections.Generic.IAsyncEnumerable<T>
System.Collections.Generic.IAsyncEnumerator<T>
System.IAsyncDisposable
System.Collections.Generic.IEnumerable<T>
System.Collections.Generic.IEnumerator<T>
System.IDisposable
C#
The starter code processes each page as the page is retrieved, as shown in the following
code:
C#
finalResults.Merge(issues(results)["nodes"]!);
progress?.Report(issuesReturned);
cancel.ThrowIfCancellationRequested();
C#
You can also remove the declaration of finalResults earlier in this method and the
return statement that follows the loop you modified.
You've finished the changes to generate an async stream. The finished method should
resemble the following code:
C#
JObject results =
JObject.Parse(response.HttpResponse.Body.ToString()!);
Next, you change the code that consumes the collection to consume the async stream.
Find the following code in Main that processes the collection of issues:
C#
try
{
var results = await RunPagedQueryAsync(client, PagedIssueQuery, "docs",
cancellationSource.Token, progressReporter);
foreach(var issue in results)
Console.WriteLine(issue);
}
catch (OperationCanceledException)
{
Console.WriteLine("Work has been cancelled");
}
C#
int num = 0;
await foreach (var issue in RunPagedQueryAsync(client, PagedIssueQuery,
"docs"))
{
Console.WriteLine(issue);
Console.WriteLine($"Received {++num} issues in total");
}
The new interface IAsyncEnumerator<T> derives from IAsyncDisposable. That means the
preceding loop will asynchronously dispose the stream when the loop finishes. You can
imagine the loop looks like the following code:
C#
int num = 0;
var enumerator = RunPagedQueryAsync(client, PagedIssueQuery,
"docs").GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
var issue = enumerator.Current;
Console.WriteLine(issue);
Console.WriteLine($"Received {++num} issues in total");
}
} finally
{
if (enumerator != null)
await enumerator.DisposeAsync();
}
By default, stream elements are processed in the captured context. If you want to
disable capturing of the context, use the
TaskAsyncEnumerableExtensions.ConfigureAwait extension method. For more
information about synchronization contexts and capturing the current context, see the
article on consuming the Task-based asynchronous pattern.
Async streams support cancellation using the same protocol as other async methods.
You would modify the signature for the async iterator method as follows to support
cancellation:
C#
JObject results =
JObject.Parse(response.HttpResponse.Body.ToString()!);
C#
You can get the code for the finished tutorial from the dotnet/docs repository in the
asynchronous-programming/snippets folder.
You can see improvements in memory use by examining the code. You no longer need
to allocate a collection to store all the results before they're enumerated. The caller can
determine how to consume the results and if a storage collection is needed.
Run both the starter and finished applications and you can observe the differences
between the implementations for yourself. You can delete the GitHub access token you
created when you started this tutorial after you've finished. If an attacker gained access
to that token, they could access GitHub APIs using your credentials.
In this tutorial, you used async streams to read a individual items from a network API
that returns pages of data. Async streams can also read from "never ending streams"
like a stock ticker, or sensor device. The call to MoveNextAsync returns the next item as
soon as it's available.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Nullable reference types
Article • 01/17/2024
In a nullable-oblivious context, all reference types were nullable. Nullable reference types
refers to a group of features enabled in a nullable aware context that minimize the
likelihood that your code causes the runtime to throw System.NullReferenceException.
Nullable reference types includes three features that help you avoid these exceptions,
including the ability to explicitly mark a reference type as nullable:
Improved static flow analysis that determines if a variable might be null before
dereferencing it.
Attributes that annotate APIs so that the flow analysis determines null-state.
Variable annotations that developers use to explicitly declare the intended null-
state for a variable.
The compiler tracks the null-state of every expression in your code at compile time. The
null-state has one of three values:
The oblivious null-state and oblivious nullability match the behavior before nullable
reference types were introduced. Those values are useful during migration, or when your
app uses a library that hasn't enabled nullable reference types.
Null-state analysis and variable annotations are disabled by default for existing projects
—meaning that all reference types continue to be nullable. Starting in .NET 6, they're
enabled by default for new projects. For information about enabling these features by
declaring a nullable annotation context, see Nullable contexts.
The rest of this article describes how those three feature areas work to produce
warnings when your code might be dereferencing a null value. Dereferencing a
variable means to access one of its members using the . (dot) operator, as shown in the
following example:
C#
When you dereference a variable whose value is null , the runtime throws a
System.NullReferenceException.
C#
using System;
Finally, you learn known pitfalls for null-state analysis in struct types and arrays.
You can also explore these concepts in our Learn module on Nullable safety in C#.
null-state analysis
When nullable reference types are enabled, Null-state analysis tracks the null-state of
references. An expression is either not-null or maybe-null. The compiler determines that
a variable is not-null in two ways:
When nullable reference types aren't enabled, all expressions have the null-state of
oblivious. The rest of the section describes the behavior when nullable reference types
are enabled.
Any variable that the compiler hasn't determined as not-null is considered maybe-null.
The analysis provides warnings in situations where you might accidentally dereference a
null value. The compiler produces warnings based on the null-state.
C#
// warning!
Console.WriteLine(originalMessage.Length);
In the preceding example, the compiler determines that message is maybe-null when the
first message is printed. There's no warning for the second message. The final line of
code produces a warning because originalMessage might be null. The following
example shows a more practical use for traversing a tree of nodes to the root,
processing each node during the traversal:
C#
The previous code doesn't generate any warnings for dereferencing the variable
current . Static analysis determines that current is never dereferenced when it's maybe-
null. The variable current is checked against null before current.Parent is accessed,
and before passing current to the ProcessNode action. The previous examples show
how the compiler determines null-state for local variables when initialized, assigned, or
compared to null .
The null-state analysis doesn't trace into called methods. As a result, fields initialized in a
common helper method called by all constructors generates a warning with the
following template:
You can address these warnings in one of two ways: Constructor chaining, or nullable
attributes on the helper method. The following code shows an example of each. The
Person class uses a common constructor called by all other constructors. The Student
C#
using System.Diagnostics.CodeAnalysis;
public Student()
{
SetMajor();
}
[MemberNotNull(nameof(Major))]
private void SetMajor(string? major = default)
{
Major = major ?? "Undeclared";
}
}
7 Note
Nullable state analysis and the warnings the compiler generates help you avoid program
errors by dereferencing null . The article on resolving nullable warnings provides
techniques for correcting the warnings most likely seen in your code.
C#
Based on inspection, any developer would consider this code safe, and shouldn't
generate warnings. However the compiler doesn't know that IsNull provides a null
check and issues a warning for the message.ToUpper() statement, considering message
to be a maybe-null variable. Use the NotNullWhen attribute to fix this warning:
C#
This attribute informs the compiler, that, if IsNull returns false , the parameter s isn't
null. The compiler changes the null-state of message to not-null inside the if
(!IsNull(message)) {...} block. No warnings are issued.
Attributes provide detailed information about the null-state of arguments, return values,
and members of the object instance used to invoke a member. The details on each
attribute can be found in the language reference article on nullable reference attributes.
As of .NET 5, all .NET runtime APIs are annotated. You improve the static analysis by
annotating your APIs to provide semantic information about the null-state of arguments
and return values.
You use annotations that can declare whether a variable is a nullable reference type or a
non-nullable reference type. These annotations make important statements about the
null-state for variables:
The variable might only be dereferenced when the compiler can guarantee that
the value isn't null .
These variables might be initialized with the default null value and might be
assigned the value null in other code.
The compiler doesn't issue warnings when code assigns a maybe-null
expression to a variable that might be null.
Any non-nullable reference variable has a default null-state of not-null. Any nullable
reference variable has the initial null-state of maybe-null.
A nullable reference type is noted using the same syntax as nullable value types: a ? is
appended to the type of the variable. For example, the following variable declaration
represents a nullable string variable, name :
C#
string? name;
When nullable reference types are enabled, any variable where the ? isn't appended to
the type name is a non-nullable reference type. That includes all reference type
variables in existing code once you enable this feature. However, any implicitly typed
local variables (declared using var ) are nullable reference types. As the preceding
sections showed, static analysis determines the null-state of local variables to determine
if they're maybe-null before dereferencing it.
Sometimes you must override a warning when you know a variable isn't null, but the
compiler determines its null-state is maybe-null. You use the null-forgiving operator !
following a variable name to force the null-state to be not-null. For example, if you know
the name variable isn't null but the compiler issues a warning, you can write the
following code to override the compiler's analysis:
C#
name!.Length;
Nullable reference types and nullable value types provide a similar semantic concept: A
variable can represent a value or object, or that variable might be null . However,
nullable reference types and nullable value types are implemented differently: nullable
value types are implemented using System.Nullable<T>, and nullable reference types
are implemented by attributes read by the compiler. For example, string? and string
are both represented by the same type: System.String. However, int? and int are
represented by System.Nullable<System.Int32> and System.Int32, respectively.
Nullable reference types are a compile time feature. That means it's possible for callers
to ignore warnings, intentionally use null as an argument to a method expecting a non
nullable reference. Library authors should include run-time checks against null argument
values. The ArgumentNullException.ThrowIfNull is the preferred option for checking a
parameter against null at run time.
) Important
Enabling nullable annotations can change how Entity Framework Core determines if
a data member is required. You can learn more details in the article on Entity
Framework Core Fundamentals: Working with Nullable Reference Types.
Generics
Generics require detailed rules to handle T? for any type parameter T . The rules are
necessarily detailed because of history and the different implementation for a nullable
value type and a nullable reference type. Nullable value types are implemented using
the System.Nullable<T> struct. Nullable reference types are implemented as type
annotations that provide semantic rules to the compiler.
The class constraint means that T must be a non-nullable reference type (for
example string ). The compiler produces a warning if you use a nullable reference
type, such as string? for T .
The class? constraint means that T must be a reference type, either non-nullable
( string ) or a nullable reference type (for example string? ). When the type
parameter is a nullable reference type, such as string? , an expression of T?
references that same nullable reference type, such as string? .
The notnull constraint means that T must be a non-nullable reference type, or a
non-nullable value type. If you use a nullable reference type or a nullable value
type for the type parameter, the compiler produces a warning. Furthermore, when
T is a value type, the return value is that value type, not the corresponding
These constraints help provide more information to the compiler on how T is used. That
helps when developers choose the type for T and provides better null-state analysis
when an instance of the generic type is used.
Nullable contexts
For small projects, you can enable nullable reference types, fix warnings, and continue.
However, for larger projects and multi-project solutions, that might generate a large
number of warnings. You can use pragmas to enable nullable reference types file-by-file
as you begin using nullable reference types. The new features that protect against
throwing a System.NullReferenceException can be disruptive when turned on in an
existing codebase:
The nullable annotation context determines the compiler's behavior. There are four
values for the nullable annotation context:
warnings: The compiler performs all null analysis and emits warnings when code
might dereference null .
All new nullable warnings are enabled.
Use of the ? suffix to declare a nullable reference type produces a warning.
All reference type variables are allowed to be null. However, members have the
null-state of not-null at the opening brace of all methods unless declared with
the ? suffix.
You can use the null forgiving operator, ! .
annotations: The compiler doesn't emit warnings when code might dereference
null , or when you assign a maybe-null expression to a non-nullable variable.
All new nullable warnings are disabled.
You can use the ? suffix to declare a nullable reference type.
Reference type variables without the ? suffix are non-nullable reference types.
You can use the null forgiving operator, ! , but it has no effect.
The nullable annotation context and nullable warning context can be set for a project
using the <Nullable> element in your .csproj file. This element configures how the
compiler interprets the nullability of types and what warnings are emitted. The following
table shows the allowable values and summarizes the contexts they specify.
ノ Expand table
Choose disable for legacy projects that you don't want to update based on
diagnostics or new features.
Choose warnings to determine where your code might throw
System.NullReferenceExceptions. You can address those warnings before
modifying code to enable non-nullable reference types.
Choose annotations to express your design intent before enabling warnings.
Choose enable for new projects and active projects where you want to protect
against null reference exceptions.
Example:
XML
<Nullable>enable</Nullable>
You can also use directives to set these same contexts anywhere in your source code.
These directives are most useful when you're migrating a large codebase.
#nullable enable : Sets the nullable annotation context and nullable warning
context to enable.
#nullable disable : Sets the nullable annotation context and nullable warning
context to disable.
#nullable restore : Restores the nullable annotation context and nullable warning
settings.
#nullable disable annotations : Set the nullable annotation context to disable.
#nullable enable annotations : Set the nullable annotation context to enable.
project settings.
For any line of code, you can set any of the following combinations:
ノ Expand table
Those nine combinations provide you with fine-grained control over the diagnostics the
compiler emits for your code. You can enable more features in any area you're updating,
without seeing more warnings you aren't ready to address yet.
) Important
The global nullable context does not apply for generated code files. Under either
strategy, the nullable context is disabled for any source file marked as generated.
This means any APIs in generated files are not annotated. There are four ways a file
is marked as generated:
By default, nullable annotation and warning contexts are disabled. That means that your
existing code compiles without changes and without generating any new warnings.
Beginning with .NET 6, new projects include the <Nullable>enable</Nullable> element
in all project templates.
These options provide two distinct strategies to update an existing codebase to use
nullable reference types.
Known pitfalls
Arrays and structs that contain reference types are known pitfalls in nullable references
and the static analysis that determines null safety. In both situations, a non-nullable
reference might be initialized to null , without generating warnings.
Structs
A struct that contains non-nullable reference types allows assigning default for it
without any warnings. Consider the following example:
C#
using System;
#nullable enable
Another more common case is when you deal with generic structs. Consider the
following example:
C#
#nullable enable
In the preceding example, the property Prop is null at run time. It's assigned to non-
nullable string without any warnings.
Arrays
Arrays are also a known pitfall in nullable reference types. Consider the following
example that doesn't produce any warnings:
C#
using System;
#nullable enable
In the preceding example, the declaration of the array shows it holds non-nullable
strings, while its elements are all initialized to null . Then, the variable s is assigned a
null value (the first element of the array). Finally, the variable s is dereferenced causing
a runtime exception.
See also
Nullable reference types proposal
Draft nullable reference types specification
Unconstrained type parameter annotations
Intro to nullable references tutorial
Nullable (C# Compiler option)
CS8602: Possible dereference of null warning
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Update a codebase with nullable
reference types to improve null
diagnostic warnings
Article • 09/21/2022
Nullable reference types enable you to declare if variables of a reference type should or
shouldn't be assigned a null value. The compiler's static analysis and warnings when
your code might dereference null are the most important benefit of this feature. Once
enabled, the compiler generates warnings that help you avoid throwing a
System.NullReferenceException when your code runs.
If your codebase is relatively small, you can turn on the feature in your project, address
warnings, and enjoy the benefits of the improved diagnostics. Larger codebases may
require a more structured approach to address warnings over time, enabling the feature
for some as you address warnings in different types or files. This article describes
different strategies to update a codebase and the tradeoffs associated with these
strategies. Before starting your migration, read the conceptual overview of nullable
reference types. It covers the compiler's static analysis, null-state values of maybe-null
and not-null and the nullable annotations. Once you're familiar with those concepts and
terms, you're ready to migrate your code.
7 Note
You can designate a Nullable setting for your project using a <Nullable> tag. Refer
to Compiler options for more information.
The first choice is setting the default for the project. Your choices are:
1. Nullable disable as the default: disable is the default if you don't add a Nullable
element to your project file. Use this default when you're not actively adding new
files to the codebase. The main activity is to update the library to use nullable
reference types. Using this default means you add a nullable preprocessor directive
to each file as you update its code.
2. Nullable enable as the default: Set this default when you're actively developing
new features. You want all new code to benefit from nullable reference types and
nullable static analysis. Using this default means you must add a #nullable
disable to the top of each file. You'll remove these preprocessor directives as you
Enabling nullable as the default creates more up-front work to add the preprocessor
directives to every file. The advantage is that every new code file added to the project
will be nullable enabled. Any new work will be nullable aware; only existing code must
be updated. Disabling nullable as the default works better if the library is stable, and the
main focus of the development is to adopt nullable reference types. You turn on nullable
reference types as you annotate APIs. When you've finished, you enable nullable
reference types for the entire project. When you create a new file, you must add the
preprocessor directives and make it nullable aware. If any developers on your team
forget, that new code is now in the backlog of work to make all code nullable aware.
Which of these strategies you pick depends on how much active development is taking
place in your project. The more mature and stable your project, the better the second
strategy. The more features being developed, the better the first strategy.
) Important
The global nullable context does not apply for generated code files. Under either
strategy, the nullable context is disabled for any source file marked as generated.
This means any APIs in generated files are not annotated. There are four ways a file
is marked as generated:
oblivious: All reference types are nullable oblivious when the annotation context is
disabled.
nonnullable: An unannotated reference type, C is nonnullable when the annotation
context is enabled.
nullable: An annotated reference type, C? , is nullable, but a warning may be issued
when the annotation context is disabled. Variables declared with var are nullable
when the annotation context is enabled.
Each variable has a default nullable state that depends on its nullability:
Before you enable nullable reference types, all declarations in your codebase are
nullable oblivious. That's important because it means all reference types have a default
null-state of not-null.
Address warnings
If your project uses Entity Framework Core, you should read their guidance on Working
with nullable reference types.
When you start your migration, you should start by enabling warnings only. All
declarations remain nullable oblivious, but you'll see warnings when you dereference a
value after its null-state changes to maybe-null. As you address these warnings, you'll be
checking against null in more locations, and your codebase becomes more resilient. To
learn specific techniques for different situations, see the article on Techniques to resolve
nullable warnings.
You can address warnings and enable annotations in each file or class before continuing
with other code. However, it's often more efficient to address the warnings generated
while the context is warnings before enabling the type annotations. That way, all types
are oblivious until you've addressed the first set of warnings.
Next steps
Once you've addressed all warnings after enabling annotations, you can set the default
context for your project to enabled. If you added any pragmas in your code for the
nullable annotation or warning context, you can remove them. Over time, you may see
new warnings. You may write code that introduces warnings. A library dependency may
be updated for nullable reference types. Those updates will change the types in that
library from nullable oblivious to either nonnullable or nullable.
You can also explore these concepts in our Learn module on Nullable safety in C#.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Methods in C#
Article • 05/21/2024
A method is a code block that contains a series of statements. A program causes the
statements to be executed by calling the method and specifying any required method
arguments. In C#, every executed instruction is performed in the context of a method.
7 Note
This topic discusses named methods. For information about anonymous functions,
see Lambda expressions.
Method signatures
Methods are declared in a class , record , or struct by specifying:
) Important
A return type of a method is not part of the signature of the method for the
purposes of method overloading. However, it is part of the signature of the method
when determining the compatibility between a delegate and the method that it
points to.
The following example defines a class named Motorcycle that contains five methods:
C#
namespace MotorCycleExample
{
abstract class Motorcycle
{
// Anyone can call this.
public void StartEngine() {/* Method statements here */ }
The Motorcycle class includes an overloaded method, Drive . Two methods have the
same name, but are differentiated by their parameter types.
Method invocation
Methods can be either instance or static. You must instantiate an object to invoke an
instance method on that instance; an instance method operates on that instance and its
data. You invoke a static method by referencing the name of the type to which the
method belongs; static methods don't operate on instance data. Attempting to call a
static method through an object instance generates a compiler error.
Calling a method is like accessing a field. After the object name (if you're calling an
instance method) or the type name (if you're calling a static method), add a period,
the name of the method, and parentheses. Arguments are listed within the parentheses
and are separated by commas.
The method definition specifies the names and types of any parameters that are
required. When a caller invokes the method, it provides concrete values, called
arguments, for each parameter. The arguments must be compatible with the parameter
type, but the argument name, if one is used in the calling code, doesn't have to be the
same as the parameter named defined in the method. In the following example, the
Square method includes a single parameter of type int named i. The first method call
passes the Square method a variable of type int named num; the second, a numeric
constant; and the third, an expression.
C#
public static class SquareExample
{
public static void Main()
{
// Call with an int variable.
int num = 4;
int productA = Square(num);
The most common form of method invocation used positional arguments; it supplies
arguments in the same order as method parameters. The methods of the Motorcycle
class can therefore be called as in the following example. The call to the Drive method,
for example, includes two arguments that correspond to the two parameters in the
method's syntax. The first becomes the value of the miles parameter. The second
becomes the value of the speed parameter.
C#
moto.StartEngine();
moto.AddGas(15);
_ = moto.Drive(5, 20);
double speed = moto.GetTopSpeed();
Console.WriteLine("My top speed is {0}", speed);
}
}
You can also use named arguments instead of positional arguments when invoking a
method. When using named arguments, you specify the parameter name followed by a
colon (":") and the argument. Arguments to the method can appear in any order, as long
as all required arguments are present. The following example uses named arguments to
invoke the TestMotorcycle.Drive method. In this example, the named arguments are
passed in the opposite order from the method's parameter list.
C#
namespace NamedMotorCycle;
You can invoke a method using both positional arguments and named arguments.
However, positional arguments can only follow named arguments when the named
arguments are in the correct positions. The following example invokes the
TestMotorcycle.Drive method from the previous example using one positional
C#
objects are equal. The Equals method, however, isn't defined in the Person class; it's
inherited from Object.
C#
Types can override inherited members by using the override keyword and providing an
implementation for the overridden method. The method signature must be the same as
the overridden method. The following example is like the previous one, except that it
overrides the Equals(Object) method. (It also overrides the GetHashCode() method, since
the two methods are intended to provide consistent results.)
C#
namespace methods;
Passing parameters
Types in C# are either value types or reference types. For a list of built-in value types, see
Types. By default, both value types and reference types are passed by value to a
method.
The following example passes a value type to a method by value, and the called method
attempts to change the value type's value. It defines a variable of type int , which is a
value type, initializes its value to 20, and passes it to a method named ModifyValue that
changes the variable's value to 30. When the method returns, however, the variable's
value remains unchanged.
C#
The following example defines a class (which is a reference type) named SampleRefType .
It instantiates a SampleRefType object, assigns 44 to its value field, and passes the
object to the ModifyObject method. This example does essentially the same thing as the
previous example—it passes an argument by value to a method. But because a
reference type is used, the result is different. The modification that is made in
ModifyObject to the obj.value field also changes the value field of the argument, rt ,
in the Main method to 33, as the output from the example shows.
C#
C#
A common pattern that uses by ref parameters involves swapping the values of
variables. You pass two variables to a method by reference, and the method swaps their
contents. The following example swaps integer values.
C#
Passing a reference-type parameter allows you to change the value of the reference
itself, rather than the value of its individual elements or fields.
Parameter collections
Sometimes, the requirement that you specify the exact number of arguments to your
method is restrictive. By using the params keyword to indicate that a parameter is a
parameter collection, you allow your method to be called with a variable number of
arguments. The parameter tagged with the params keyword must be a collection type,
and it must be the last parameter in the method's parameter list.
A caller can then invoke the method in either of four ways for the params parameter:
By passing a collection of the appropriate type that contains the desired number of
elements. The example uses a collection expression so the compiler creates an
appropriate collection type.
By passing a comma-separated list of individual arguments of the appropriate type
to the method. The compiler creates the appropriate collection type.
By passing null .
By not providing an argument to the parameter collection.
The following example defines a method named GetVowels that returns all the vowels
from a parameter collection. The Main method illustrates all four ways of invoking the
method. Callers aren't required to supply any arguments for parameters that include the
params modifier. In that case, the parameter is an empty collection.
C#
Before C# 13, the params modifier can be used only with a single dimensional array.
You assign the parameter's default value with one of the following kinds of expressions:
An expression of the form new ValType() , where ValType is a value type. This
expression invokes the value type's implicit parameterless constructor, which isn't
an actual member of the type.
7 Note
In C# 10 and later, when an expression of the form new ValType() invokes the
explicitly defined parameterless constructor of a value type, the compiler
generates an error as the default parameter value must be a compile-time
constant. Use the default(ValType) expression or the default literal to
provide the default parameter value. For more information about
parameterless constructors, see the Struct initialization and default values
section of the Structure types article.
If a method includes both required and optional parameters, optional parameters are
defined at the end of the parameter list, after all required parameters.
The following example defines a method, ExampleMethod , that has one required and two
optional parameters.
C#
The caller must supply an argument for all optional parameters up to the last optional
parameter for which an argument is supplied. In the ExampleMethod method, for
example, if the caller supplies an argument for the description parameter, it must also
supply one for the optionalInt parameter. opt.ExampleMethod(2, 2, "Addition of 2 and
2"); is a valid method call; opt.ExampleMethod(2, , "Addition of 2 and 0"); generates
The following example calls the ExampleMethod method three times. The first two
method calls use positional arguments. The first omits both optional arguments, while
the second omits the last argument. The third method call supplies a positional
argument for the required parameter but uses a named argument to supply a value to
the description parameter while omitting the optionalInt argument.
C#
The use of optional parameters affects overload resolution, or the way the C# compiler
determines which overload to invoke for a method call, as follows:
Return values
Methods can return a value to the caller. If the return type (the type listed before the
method name) isn't void , the method can return the value by using the return
keyword. A statement with the return keyword followed by a variable, constant, or
expression that matches the return type returns that value to the method caller.
Methods with a nonvoid return type are required to use the return keyword to return a
value. The return keyword also stops the execution of the method.
If the return type is void , a return statement without a value is still useful to stop the
execution of the method. Without the return keyword, the method stops executing
when it reaches the end of the code block.
For example, these two methods use the return keyword to return integers:
C#
class SimpleMath
{
public int AddTwoNumbers(int number1, int number2) =>
number1 + number2;
The examples above are expression bodied members. Expression bodied members
return the value returned by the expression.
You can also choose to define your methods with a statement body and a return
statement:
C#
class SimpleMathExtnsion
{
public int DivideTwoNumbers(int number1, int number2)
{
return number1 / number2;
}
}
To use a value returned from a method, the calling method can use the method call
itself anywhere a value of the same type would be sufficient. You can also assign the
return value to a variable. For example, the following three code examples accomplish
the same goal:
C#
C#
C#
result = obj2.DivideTwoNumbers(6,2);
// The result is 3.
Console.WriteLine(result);
Sometimes, you want your method to return more than a single value. You use tuple
types and tuple literals to return multiple values. The tuple type defines the data types of
the tuple's elements. Tuple literals provide the actual values of the returned tuple. In the
following example, (string, string, string, int) defines the tuple type returned by
the GetPersonalInfo method. The expression (per.FirstName, per.MiddleName,
per.LastName, per.Age) is the tuple literal; the method returns the first, middle, and
C#
The caller can then consume the returned tuple using the following code:
C#
Names can also be assigned to the tuple elements in the tuple type definition. The
following example shows an alternate version of the GetPersonalInfo method that uses
named elements:
C#
C#
If a method takes an array as a parameter and modifies the value of individual elements,
it isn't necessary for the method to return the array. C# passes all reference types by
value, and the value of an array reference is the pointer to the array. In the following
example, changes to the contents of the values array that are made in the
DoubleValues method are observable by any code that has a reference to the array.
C#
Extension methods
Ordinarily, there are two ways to add a method to an existing type:
Modify the source code for that type. Modifying the source creates a breaking
change if you also add any private data fields to support the method.
Define the new method in a derived class. A method can't be added in this way
using inheritance for other types, such as structures and enumerations. Nor can it
be used to "add" a method to a sealed class.
Extension methods let you "add" a method to an existing type without modifying the
type itself or implementing the new method in an inherited type. The extension method
also doesn't have to reside in the same assembly as the type it extends. You call an
extension method as if it were a defined member of a type.
Async Methods
By using the async feature, you can invoke asynchronous methods without using explicit
callbacks or manually splitting your code across multiple methods or lambda
expressions.
If you mark a method with the async modifier, you can use the await operator in the
method. When control reaches an await expression in the async method, control
returns to the caller if the awaited task isn't completed, and progress in the method with
the await keyword is suspended until the awaited task completes. When the task is
complete, execution can resume in the method.
7 Note
An async method returns to the caller when either it encounters the first awaited
object that's not yet complete or it gets to the end of the async method, whichever
occurs first.
In the following example, DelayAsync is an async method that has a return statement
that returns an integer. Because it's an async method, its method declaration must have
a return type of Task<int> . Because the return type is Task<int> , the evaluation of the
await expression in DoSomethingAsync produces an integer, as the following int result
= await delayTask statement demonstrates.
C#
class Program
{
static Task Main() => DoSomethingAsync();
Console.WriteLine($"Result: {result}");
}
An async method can't declare any in, ref, or out parameters, but it can call methods
that have such parameters.
For more information about async methods, see Asynchronous programming with async
and await and Async return types.
Expression-bodied members
It's common to have method definitions that return immediately with the result of an
expression, or that have a single statement as the body of the method. There's a syntax
shortcut for defining such methods using => :
C#
public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
public void Print() => Console.WriteLine(First + " " + Last);
// Works with operators, properties, and indexers too.
public static Complex operator +(Complex a, Complex b) => a.Add(b);
public string Name => First + " " + Last;
public Customer this[long id] => store.LookupCustomer(id);
If the method returns void or is an async method, the body of the method must be a
statement expression (same as with lambdas). For properties and indexers, they must be
read-only, and you don't use the get accessor keyword.
Iterators
An iterator performs a custom iteration over a collection, such as a list or an array. An
iterator uses the yield return statement to return each element one at a time. When a
yield return statement is reached, the current location is remembered so that the
See also
Access Modifiers
Static Classes and Static Class Members
Inheritance
Abstract and Sealed Classes and Class Members
params
out
ref
in
Passing Parameters
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Properties
Article • 09/29/2022
Properties are first class citizens in C#. The language defines syntax that enables
developers to write code that accurately expresses their design intent.
Properties behave like fields when they're accessed. However, unlike fields, properties
are implemented with accessors that define the statements executed when a property is
accessed or assigned.
Property syntax
The syntax for properties is a natural extension to fields. A field defines a storage
location:
C#
A property definition contains declarations for a get and set accessor that retrieves
and assigns the value of that property:
C#
The syntax shown above is the auto property syntax. The compiler generates the storage
location for the field that backs up the property. The compiler also implements the body
of the get and set accessors.
Sometimes, you need to initialize a property to a value other than the default for its
type. C# enables that by setting a value after the closing brace for the property. You may
prefer the initial value for the FirstName property to be the empty string rather than
null . You would specify that as shown below:
C#
Specific initialization is most useful for read-only properties, as you'll see later in this
article.
C#
C#
The property definition shown above is a read-write property. Notice the keyword value
in the set accessor. The set accessor always has a single parameter named value . The
get accessor must return a value that is convertible to the type of the property ( string
in this example).
That's the basics of the syntax. There are many different variations that support various
different design idioms. Let's explore, and learn the syntax options for each.
Validation
The examples above showed one of the simplest cases of property definition: a read-
write property with no validation. By writing the code you want in the get and set
accessors, you can create many different scenarios.
You can write code in the set accessor to ensure that the values represented by a
property are always valid. For example, suppose one rule for the Person class is that the
name can't be blank or white space. You would write that as follows:
C#
The preceding example can be simplified by using a throw expression as part of the
property setter validation:
C#
The example above enforces the rule that the first name must not be blank or white
space. If a developer writes
C#
hero.FirstName = "";
You can extend this same syntax to anything needed in your scenario. You can check the
relationships between different properties, or validate against any external conditions.
Any valid C# statements are valid in a property accessor.
Access control
Up to this point, all the property definitions you have seen are read/write properties
with public accessors. That's not the only valid accessibility for properties. You can create
read-only properties, or give different accessibility to the set and get accessors. Suppose
that your Person class should only enable changing the value of the FirstName property
from other methods in that class. You could give the set accessor private accessibility
instead of public :
C#
You can add any restrictive access modifier to either the set or get accessors. Any access
modifier you place on the individual accessor must be more limited than the access
modifier on the property definition. The above is legal because the FirstName property
is public , but the set accessor is private . You couldn't declare a private property with
a public accessor. Property declarations can also be declared protected , internal ,
protected internal , or, even private .
It's also legal to place the more restrictive modifier on the get accessor. For example,
you could have a public property, but restrict the get accessor to private . That
scenario is rarely done in practice.
Read-only
You can also restrict modifications to a property so that it can only be set in a
constructor. You can modify the Person class so as follows:
C#
Init-only
The preceding example requires callers to use the constructor that includes the
FirstName parameter. Callers can't use object initializers to assign a value to the
property. To support initializers, you can make the set accessor an init accessor, as
shown in the following code:
C#
The preceding example allows a caller to create a Person using the default constructor,
even when that code doesn't set the FirstName property. Beginning in C# 11, you can
require callers to set that property:
C#
[SetsRequiredMembers]
public Person(string firstName) => FirstName = firstName;
The preceding code makes two additions to the Person class. First, the FirstName
property declaration includes the required modifier. That means any code that creates a
new Person must set this property. Second, the constructor that takes a firstName
parameter has the System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute
attribute. This attribute informs the compiler that this constructor sets all required
members.
) Important
Don't confuse required with non-nullable. It's valid to set a required property to
null or default . If the type is non-nullable, such as string in these examples, the
Callers must either use the constructor with SetsRequiredMembers or set the FirstName
property using an object initializer, as shown in the following code:
C#
Computed properties
A property doesn't need to simply return the value of a member field. You can create
properties that return a computed value. Let's expand the Person object to return the
full name, computed by concatenating the first and last names:
C#
The example above uses the string interpolation feature to create the formatted string
for the full name.
You can also use an expression-bodied member, which provides a more succinct way to
create the computed FullName property:
C#
Expression-bodied members use the lambda expression syntax to define methods that
contain a single expression. Here, that expression returns the full name for the person
object.
C#
The above code contains a bug though. If code updates the value of either the
FirstName or LastName property, the previously evaluated fullName field is invalid. You
modify the set accessors of the FirstName and LastName property so that the fullName
field is calculated again:
C#
This final version evaluates the FullName property only when needed. If the previously
calculated version is valid, it's used. If another state change invalidates the previously
calculated version, it will be recalculated. Developers that use this class don't need to
know the details of the implementation. None of these internal changes affect the use
of the Person object. That's the key reason for using Properties to expose data members
of an object.
C#
[field:NonSerialized]
public int Id { get; set; }
public string FullName => $"{FirstName} {LastName}";
}
This technique works for any attribute you attach to the backing field on the auto-
implemented property.
Implementing INotifyPropertyChanged
A final scenario where you need to write code in a property accessor is to support the
INotifyPropertyChanged interface used to notify data binding clients that a value has
changed. When the value of a property changes, the object raises the
INotifyPropertyChanged.PropertyChanged event to indicate the change. The data
binding libraries, in turn, update display elements based on that change. The code
below shows how you would implement INotifyPropertyChanged for the FirstName
property of this person class.
C#
The ?. operator is called the null conditional operator. It checks for a null reference
before evaluating the right side of the operator. The end result is that if there are no
subscribers to the PropertyChanged event, the code to raise the event doesn't execute. It
would throw a NullReferenceException without this check in that case. For more
information, see events. This example also uses the new nameof operator to convert
from the property name symbol to its text representation. Using nameof can reduce
errors where you've mistyped the name of the property.
Summing up
Properties are a form of smart fields in a class or object. From outside the object, they
appear like fields in the object. However, properties can be implemented using the full
palette of C# functionality. You can provide validation, different accessibility, lazy
evaluation, or any requirements your scenarios need.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Indexers
Article • 12/04/2021
Indexers are similar to properties. In many ways indexers build on the same language
features as properties. Indexers enable indexed properties: properties referenced using
one or more arguments. Those arguments provide an index into some collection of
values.
Indexer Syntax
You access an indexer through a variable name and square brackets. You place the
indexer arguments inside the brackets:
C#
You declare indexers using the this keyword as the property name, and declaring the
arguments within square brackets. This declaration would match the usage shown in the
previous paragraph:
C#
From this initial example, you can see the relationship between the syntax for properties
and for indexers. This analogy carries through most of the syntax rules for indexers.
Indexers can have any valid access modifiers (public, protected internal, protected,
internal, private or private protected). They may be sealed, virtual, or abstract. As with
properties, you can specify different access modifiers for the get and set accessors in an
indexer. You may also specify read-only indexers (by omitting the set accessor), or write-
only indexers (by omitting the get accessor).
You can apply almost everything you learn from working with properties to indexers.
The only exception to that rule is auto implemented properties. The compiler cannot
always generate the correct storage for an indexer.
The presence of arguments to reference an item in a set of items distinguishes indexers
from properties. You may define multiple indexers on a type, as long as the argument
lists for each indexer is unique. Let's explore different scenarios where you might use
one or more indexers in a class definition.
Scenarios
You would define indexers in your type when its API models some collection where you
define the arguments to that collection. Your indexers may or may not map directly to
the collection types that are part of the .NET core framework. Your type may have other
responsibilities in addition to modeling a collection. Indexers enable you to provide the
API that matches your type's abstraction without exposing the inner details of how the
values for that abstraction are stored or computed.
Let's walk through some of the common scenarios for using indexers. You can access the
sample folder for indexers . For download instructions, see Samples and Tutorials.
The advantage of creating your own indexer is that you can define the storage for that
collection to suit your needs. Imagine a scenario where your type models historical data
that is too large to load into memory at once. You need to load and unload sections of
the collection based on usage. The example following models this behavior. It reports
on how many data points exist. It creates pages to hold sections of the data on demand.
It removes pages from memory to make room for pages needed by more recent
requests.
C#
page[index] = value;
}
}
Dictionaries
Another common scenario is when you need to model a dictionary or a map. This
scenario is when your type stores values based on key, typically text keys. This example
creates a dictionary that maps command line arguments to lambda expressions that
manage those options. The following example shows two classes: an ArgsActions class
that maps a command line option to an Action delegate, and an ArgsProcessor that
uses the ArgsActions to execute each Action when it encounters that option.
C#
}
public class ArgsActions
{
readonly private Dictionary<string, Action> argsActions = new
Dictionary<string, Action>();
In this example, the ArgsAction collection maps closely to the underlying collection. The
get determines if a given option has been configured. If so, it returns the Action
associated with that option. If not, it returns an Action that does nothing. The public
accessor does not include a set accessor. Rather, the design is using a public method
for setting options.
Multi-Dimensional Maps
You can create indexers that use multiple arguments. In addition, those arguments are
not constrained to be the same type. Let's look at two examples.
The first example shows a class that generates values for a Mandelbrot set. For more
information on the mathematics behind the set, read this article . The indexer uses two
doubles to define a point in the X, Y plane. The get accessor computes the number of
iterations until a point is determined to be not in the set. If the maximum iterations is
reached, the point is in the set, and the class's maxIterations value is returned. (The
computer generated images popularized for the Mandelbrot set define colors for the
number of iterations necessary to determine that a point is outside the set.)
C#
The Mandelbrot Set defines values at every (x,y) coordinate for real number values. That
defines a dictionary that could contain an infinite number of values. Therefore, there is
no storage behind the set. Instead, this class computes the value for each point when
code calls the get accessor. There's no underlying storage used.
Let's examine one last use of indexers, where the indexer takes multiple arguments of
different types. Consider a program that manages historical temperature data. This
indexer uses a city and a date to set or get the high and low temperatures for that
location:
C#
using DateMeasurements =
System.Collections.Generic.Dictionary<System.DateTime,
IndexersSamples.Common.Measurements>;
using CityDataMeasurements =
System.Collections.Generic.Dictionary<string,
System.Collections.Generic.Dictionary<System.DateTime,
IndexersSamples.Common.Measurements>>;
This example creates an indexer that maps weather data on two different arguments: a
city (represented by a string ) and a date (represented by a DateTime ). The internal
storage uses two Dictionary classes to represent the two-dimensional dictionary. The
public API no longer represents the underlying storage. Rather, the language features of
indexers enables you to create a public interface that represents your abstraction, even
though the underlying storage must use different core collection types.
There are two parts of this code that may be unfamiliar to some developers. These two
using directives:
C#
using DateMeasurements =
System.Collections.Generic.Dictionary<System.DateTime,
IndexersSamples.Common.Measurements>;
using CityDataMeasurements = System.Collections.Generic.Dictionary<string,
System.Collections.Generic.Dictionary<System.DateTime,
IndexersSamples.Common.Measurements>>;
create an alias for a constructed generic type. Those statements enable the code later to
use the more descriptive DateMeasurements and CityDataMeasurements names instead of
the generic construction of Dictionary<DateTime, Measurements> and
Dictionary<string, Dictionary<DateTime, Measurements> > . This construct does require
using the fully qualified type names on the right side of the = sign.
The second technique is to strip off the time portions of any DateTime object used to
index into the collections. .NET doesn't include a date-only type. Developers use the
DateTime type, but use the Date property to ensure that any DateTime object from that
Summing Up
You should create indexers anytime you have a property-like element in your class
where that property represents not a single value, but rather a collection of values
where each individual item is identified by a set of arguments. Those arguments can
uniquely identify which item in the collection should be referenced. Indexers extend the
concept of properties, where a member is treated like a data item from outside the class,
but like a method on the inside. Indexers allow arguments to find a single item in a
property that represents a set of items.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Iterators
Article • 11/10/2021
Almost every program you write will have some need to iterate over a collection. You'll
write code that examines every item in a collection.
You'll also create iterator methods, which are methods that produce an iterator for the
elements of that class. An iterator is an object that traverses a container, particularly lists.
Iterators can be used for:
The C# language provides features for both generating and consuming sequences.
These sequences can be produced and consumed synchronously or asynchronously.
This article provides an overview of those features.
C#
That's all. To iterate over all the contents of a collection, the foreach statement is all you
need. The foreach statement isn't magic, though. It relies on two generic interfaces
defined in the .NET core library to generate the code necessary to iterate a collection:
IEnumerable<T> and IEnumerator<T> . This mechanism is explained in more detail below.
When a sequence is generated asynchronously, you can use the await foreach
statement to asynchronously consume the sequence:
C#
You could write this method to produce the sequence of integers from 0 through 9:
C#
The code above shows distinct yield return statements to highlight the fact that you
can use multiple discrete yield return statements in an iterator method. You can (and
often do) use other language constructs to simplify the code of an iterator method. The
method definition below produces the exact same sequence of numbers:
C#
You don't have to decide one or the other. You can have as many yield return
statements as necessary to meet the needs of your method:
C#
index = 100;
while (index < 110)
yield return index++;
}
All of these preceding examples would have an asynchronous counterpart. In each case,
you'd replace the return type of IEnumerable<T> with an IAsyncEnumerable<T> . For
example, the previous example would have the following asynchronous version:
C#
await Task.Delay(500);
await Task.Delay(500);
index = 100;
while (index < 110)
yield return index++;
}
That's the syntax for both synchronous and asynchronous iterators. Let's consider a real
world example. Imagine you're on an IoT project and the device sensors generate a very
large stream of data. To get a feel for the data, you might write a method that samples
every Nth data element. This small iterator method does the trick:
C#
If reading from the IoT device produces an asynchronous sequence, you'd modify the
method as the following method shows:
C#
There's one important restriction on iterator methods: you can't have both a return
statement and a yield return statement in the same method. The following code won't
compile:
C#
This restriction normally isn't a problem. You have a choice of either using yield return
throughout the method, or separating the original method into multiple methods, some
using return , and some using yield return .
You can modify the last method slightly to use yield return everywhere:
C#
var items = new int[] {100, 101, 102, 103, 104, 105, 106, 107, 108, 109
};
foreach (var item in items)
yield return item;
}
Sometimes, the right answer is to split an iterator method into two different methods.
One that uses return , and a second that uses yield return . Consider a situation where
you might want to return an empty collection, or the first five odd numbers, based on a
boolean argument. You could write that as these two methods:
C#
Look at the methods above. The first uses the standard return statement to return
either an empty collection, or the iterator created by the second method. The second
method uses the yield return statement to create the requested sequence.
The compiler translates the foreach loop shown in the first example into something
similar to this construct:
C#
The exact code generated by the compiler is more complicated, and handles situations
where the object returned by GetEnumerator() implements the IDisposable interface.
The full expansion generates code more like this:
C#
{
var enumerator = collection.GetEnumerator();
try
{
while (enumerator.MoveNext())
{
var item = enumerator.Current;
Console.WriteLine(item.ToString());
}
}
finally
{
// dispose of enumerator.
}
}
The compiler translates the first asynchronous sample into something similar to this
construct:
C#
{
var enumerator = collection.GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync())
{
var item = enumerator.Current;
Console.WriteLine(item.ToString());
}
}
finally
{
// dispose of async enumerator.
}
}
C#
finally
{
(enumerator as IDisposable)?.Dispose();
}
C#
finally
{
if (enumerator is IAsyncDisposable asyncDisposable)
await asyncDisposable.DisposeAsync();
}
However, if the type of enumerator is a sealed type and there's no implicit conversion
from the type of enumerator to IDisposable or IAsyncDisposable , the finally clause
expands to an empty block:
C#
finally
{
}
C#
finally
{
((IDisposable)enumerator).Dispose();
}
Thankfully, you don't need to remember all these details. The foreach statement
handles all those nuances for you. The compiler will generate the correct code for any of
these constructs.
Introduction to delegates and events in
C#
Article • 03/31/2022
Delegates provide a late binding mechanism in .NET. Late Binding means that you create
an algorithm where the caller also supplies at least one method that implements part of
the algorithm.
For example, consider sorting a list of stars in an astronomy application. You may
choose to sort those stars by their distance from the earth, or the magnitude of the star,
or their perceived brightness.
In all those cases, the Sort() method does essentially the same thing: arranges the items
in the list based on some comparison. The code that compares two stars is different for
each of the sort orderings.
These kinds of solutions have been used in software for half a century. The C# language
delegate concept provides first class language support, and type safety around the
concept.
As you'll see later in this series, the C# code you write for algorithms like this is type
safe. The compiler ensures that the types match for arguments and return types.
Function pointers support similar scenarios, where you need more control over the
calling convention. The code associated with a delegate is invoked using a virtual
method added to a delegate type. Using function pointers, you can specify different
conventions.
The team wanted a common language construct that could be used for any late binding
algorithms. Delegates enable developers to learn one concept, and use that same
concept across many different software problems.
Second, the team wanted to support both single and multicast method calls. (Multicast
delegates are delegates that chain together multiple method calls. You'll see examples
later in this series.)
The team wanted delegates to support the same type safety that developers expect
from all C# constructs.
Finally, the team recognized an event pattern is one specific pattern where delegates, or
any late binding algorithm, is useful. The team wanted to ensure the code for delegates
could provide the basis for the .NET event pattern.
The result of all that work was the delegate and event support in C# and .NET.
The remaining articles in this series will cover language features, library support, and
common idioms used when you work with delegates and events. You'll learn about:
Next
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
System.Delegate and the delegate
keyword
Article • 09/15/2021
Previous
This article covers the classes in .NET that support delegates, and how those map to the
delegate keyword.
You define a delegate type using syntax that is similar to defining a method signature.
You just add the delegate keyword to the definition.
Let's continue to use the List.Sort() method as our example. The first step is to create a
type for the comparison delegate:
C#
The compiler generates a class, derived from System.Delegate that matches the
signature used (in this case, a method that returns an integer, and has two arguments).
The type of that delegate is Comparison . The Comparison delegate type is a generic type.
For details on generics see here.
Notice that the syntax may appear as though it is declaring a variable, but it is actually
declaring a type. You can define delegate types inside classes, directly inside
namespaces, or even in the global namespace.
7 Note
Declaring delegate types (or other types) directly in the global namespace is not
recommended.
The compiler also generates add and remove handlers for this new type so that clients
of this class can add and remove methods from an instance's invocation list. The
compiler will enforce that the signature of the method being added or removed
matches the signature used when declaring the method.
C#
The type of the variable is Comparison<T> , the delegate type defined earlier. The name of
the variable is comparator .
That code snippet above declared a member variable inside a class. You can also declare
delegate variables that are local variables, or arguments to methods.
Invoke delegates
You invoke the methods that are in the invocation list of a delegate by calling that
delegate. Inside the Sort() method, the code will call the comparison method to
determine which order to place objects:
C#
In the line above, the code invokes the method attached to the delegate. You treat the
variable as a method name, and invoke it using normal method call syntax.
That line of code makes an unsafe assumption: There's no guarantee that a target has
been added to the delegate. If no targets have been attached, the line above would
cause a NullReferenceException to be thrown. The idioms used to address this problem
are more complicated than a simple null-check, and are covered later in this series.
Developers that want to use the List.Sort() method need to define a method whose
signature matches the delegate type definition, and assign it to the delegate used by
the sort method. This assignment adds the method to the invocation list of that
delegate object.
Suppose you wanted to sort a list of strings by their length. Your comparison function
might be the following:
C#
The method is declared as a private method. That's fine. You may not want this method
to be part of your public interface. It can still be used as the comparison method when
attached to a delegate. The calling code will have this method attached to the target list
of the delegate object, and can access it through that delegate.
You create that relationship by passing that method to the List.Sort() method:
C#
phrases.Sort(CompareLength);
Notice that the method name is used, without parentheses. Using the method as an
argument tells the compiler to convert the method reference into a reference that can
be used as a delegate invocation target, and attach that method as an invocation target.
You could also have been explicit by declaring a variable of type Comparison<string>
and doing an assignment:
C#
C#
Using lambda expressions for delegate targets is covered more in a later section.
The Sort() example typically attaches a single target method to the delegate. However,
delegate objects do support invocation lists that have multiple target methods attached
to a delegate object.
This design has its roots in the first release of C# and .NET. One goal for the design team
was to ensure that the language enforced type safety when using delegates. That meant
ensuring that delegates were invoked with the right type and number of arguments.
And, that any return type was correctly indicated at compile time. Delegates were part of
the 1.0 .NET release, which was before generics.
The best way to enforce this type safety was for the compiler to create the concrete
delegate classes that represented the method signature being used.
Even though you cannot create derived classes directly, you will use the methods
defined on these classes. Let's go through the most common methods that you will use
when you work with delegates.
The first, most important fact to remember is that every delegate you work with is
derived from MulticastDelegate . A multicast delegate means that more than one
method target can be invoked when invoking through a delegate. The original design
considered making a distinction between delegates where only one target method
could be attached and invoked, and delegates where multiple target methods could be
attached and invoked. That distinction proved to be less useful in practice than originally
thought. The two different classes were already created, and have been in the
framework since its initial public release.
The methods that you will use the most with delegates are Invoke() and BeginInvoke()
/ EndInvoke() . Invoke() will invoke all the methods that have been attached to a
particular delegate instance. As you saw above, you typically invoke delegates using the
method call syntax on the delegate variable. As you'll see later in this series, there are
patterns that work directly with these methods.
Now that you've seen the language syntax and the classes that support delegates, let's
examine how strongly typed delegates are used, created, and invoked.
Next
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Strongly Typed Delegates
Article • 09/15/2021
Previous
In the previous article, you saw that you create specific delegate types using the
delegate keyword.
The abstract Delegate class provides the infrastructure for loose coupling and
invocation. Concrete Delegate types become much more useful by embracing and
enforcing type safety for the methods that are added to the invocation list for a
delegate object. When you use the delegate keyword and define a concrete delegate
type, the compiler generates those methods.
In practice, this would lead to creating new delegate types whenever you need a
different method signature. This work could get tedious after a time. Every new feature
requires new delegate types.
Thankfully, this isn't necessary. The .NET Core framework contains several types that you
can reuse whenever you need delegate types. These are generic definitions so you can
declare customizations when you need new method declarations.
The first of these types is the Action type, and several variations:
C#
The in modifier on the generic type argument is covered in the article on covariance.
There are variations of the Action delegate that contain up to 16 arguments such as
Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>. It's important that
these definitions use different generic arguments for each of the delegate arguments:
That gives you maximum flexibility. The method arguments need not be, but may be,
the same type.
Use one of the Action types for any delegate type that has a void return type.
The framework also includes several generic delegate types that you can use for
delegate types that return values:
C#
The out modifier on the result generic type argument is covered in the article on
covariance.
There are variations of the Func delegate with up to 16 input arguments such as
Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>. The type of the
result is always the last type parameter in all the Func declarations, by convention.
Use one of the Func types for any delegate type that returns a value.
There's also a specialized Predicate<T> type for a delegate that returns a test on a
single value:
C#
You may notice that for any Predicate type, a structurally equivalent Func type exists
For example:
C#
You might think these two types are equivalent. They are not. These two variables
cannot be used interchangeably. A variable of one type cannot be assigned the other
type. The C# type system uses the names of the defined types, not the structure.
All these delegate type definitions in the .NET Core Library should mean that you do not
need to define a new delegate type for any new feature you create that requires
delegates. These generic definitions should provide all the delegate types you need
under most situations. You can simply instantiate one of these types with the required
type parameters. In the case of algorithms that can be made generic, these delegates
can be used as generic types.
This should save time, and minimize the number of new types that you need to create in
order to work with delegates.
In the next article, you'll see several common patterns for working with delegates in
practice.
Next
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Common patterns for delegates
Article • 09/15/2021
Previous
One excellent example for this kind of design is LINQ. The LINQ Query Expression
Pattern relies on delegates for all of its features. Consider this simple example:
C#
This filters the sequence of numbers to only those less than the value 10. The Where
method uses a delegate that determines which elements of a sequence pass the filter.
When you create a LINQ query, you supply the implementation of the delegate for this
specific purpose.
C#
This example is repeated with all the methods that are part of LINQ. They all rely on
delegates for the code that manages the specific query. This API design pattern is a
powerful one to learn and understand.
This simple example illustrates how delegates require very little coupling between
components. You don't need to create a class that derives from a particular base class.
You don't need to implement a specific interface. The only requirement is to provide the
implementation of one method that is fundamental to the task at hand.
There is one aspect of the feature that will change often: where messages are written. In
some environments, they may be written to the error console. In others, a file. Other
possibilities include database storage, OS event logs, or other document storage.
There are also combinations of output that might be used in different scenarios. You
may want to write messages to the console and to a file.
A design based on delegates will provide a great deal of flexibility, and make it easy to
support storage mechanisms that may be added in the future.
Under this design, the primary log component can be a non-virtual, even sealed class.
You can plug in any set of delegates to write the messages to different storage media.
The built-in support for multicast delegates makes it easy to support scenarios where
messages must be written to multiple locations (a file, and a console).
A First Implementation
Let's start small: the initial implementation will accept new messages, and write them
using any attached delegate. You can start with one delegate that writes messages to
the console.
C#
The static class above is the simplest thing that can work. We need to write the single
implementation for the method that writes messages to the console:
C#
Finally, you need to hook up the delegate by attaching it to the WriteMessage delegate
declared in the logger:
C#
Logger.WriteMessage += LoggingMethods.LogToConsole;
Practices
Our sample so far is fairly simple, but it still demonstrates some of the important
guidelines for designs involving delegates.
Using the delegate types defined in the core framework makes it easier for users to
work with the delegates. You don't need to define new types, and developers using your
library do not need to learn new, specialized delegate types.
The interfaces used are as minimal and as flexible as possible: To create a new output
logger, you must create one method. That method may be a static method, or an
instance method. It may have any access.
Format Output
Let's make this first version a bit more robust, and then start creating other logging
mechanisms.
Next, let's add a few arguments to the LogMessage() method so that your log class
creates more structured messages:
C#
C#
Next, let's make use of that Severity argument to filter the messages that are sent to
the log's output.
C#
Practices
You've added new features to the logging infrastructure. Because the logger component
is very loosely coupled to any output mechanism, these new features can be added with
no impact on any of the code implementing the logger delegate.
As you keep building this, you'll see more examples of how this loose coupling enables
greater flexibility in updating parts of the site without any changes to other locations. In
fact, in a larger application, the logger output classes might be in a different assembly,
and not even need to be rebuilt.
C#
C#
These two are not mutually exclusive. You could attach both log methods and generate
messages to the console and a file:
C#
Later, even in the same application, you can remove one of the delegates without any
other issues to the system:
C#
Logger.WriteMessage -= LoggingMethods.LogToConsole;
Practices
Now, you've added a second output handler for the logging subsystem. This one needs
a bit more infrastructure to correctly support the file system. The delegate is an instance
method. It's also a private method. There's no need for greater accessibility because the
delegate infrastructure can connect the delegates.
Second, the delegate-based design enables multiple output methods without any extra
code. You don't need to build any additional infrastructure to support multiple output
methods. They simply become another method on the invocation list.
Pay special attention to the code in the file logging output method. It is coded to ensure
that it does not throw any exceptions. While this isn't always strictly necessary, it's often
a good practice. If either of the delegate methods throws an exception, the remaining
delegates that are on the invocation won't be invoked.
As a last note, the file logger must manage its resources by opening and closing the file
on each log message. You could choose to keep the file open and implement
IDisposable to close the file when you are completed. Either method has its advantages
and disadvantages. Both do create a bit more coupling between the classes.
None of the code in the Logger class would need to be updated in order to support
either scenario.
C#
The null conditional operator ( ?. ) short-circuits when the left operand ( WriteMessage in
this case) is null, which means no attempt is made to log a message.
You won't find the Invoke() method listed in the documentation for System.Delegate or
System.MulticastDelegate . The compiler generates a type safe Invoke method for any
delegate type declared. In this example, that means Invoke takes a single string
argument, and has a void return type.
Summary of Practices
You've seen the beginnings of a log component that could be expanded with other
writers, and other features. By using delegates in the design, these different components
are loosely coupled. This provides several advantages. It's easy to create new output
mechanisms and attach them to the system. These other mechanisms only need one
method: the method that writes the log message. It's a design that's resilient when new
features are added. The contract required for any writer is to implement one method.
That method could be a static or instance method. It could be public, private, or any
other legal access.
The Logger class can make any number of enhancements or changes without
introducing breaking changes. Like any class, you cannot modify the public API without
the risk of breaking changes. But, because the coupling between the logger and any
output engines is only through the delegate, no other types (like interfaces or base
classes) are involved. The coupling is as small as possible.
Next
Introduction to events
Article • 09/15/2021
Previous
Events are, like delegates, a late binding mechanism. In fact, events are built on the
language support for delegates.
Events are a way for an object to broadcast (to all interested components in the system)
that something has happened. Any other component can subscribe to the event, and be
notified when an event is raised.
You've probably used events in some of your programming. Many graphical systems
have an event model to report user interaction. These events would report mouse
movement, button presses and similar interactions. That's one of the most common, but
certainly not the only scenario where events are used.
You can define events that should be raised for your classes. One important
consideration when working with events is that there may not be any object registered
for a particular event. You must write your code so that it does not raise events when no
listeners are configured.
Subscribing to an event also creates a coupling between two objects (the event source,
and the event sink). You need to ensure that the event sink unsubscribes from the event
source when no longer interested in events.
Enable very minimal coupling between an event source and an event sink. These
two components may not be written by the same organization, and may even be
updated on totally different schedules.
Event sources should support multiple event subscribers. It should also support
having no event subscribers attached.
You can see that the goals for events are very similar to the goals for delegates. That's
why the event language support is built on the delegate language support.
Language support for events
The syntax for defining events, and subscribing or unsubscribing from events is an
extension of the syntax for delegates.
C#
When you want to raise the event, you call the event handlers using the delegate
invocation syntax:
C#
As discussed in the section on delegates, the ?. operator makes it easy to ensure that
you do not attempt to raise the event when there are no subscribers to that event.
C#
fileLister.Progress += onProgress;
The handler method typically has the prefix 'On' followed by the event name, as shown
above.
C#
fileLister.Progress -= onProgress;
It's important that you declare a local variable for the expression that represents the
event handler. That ensures the unsubscribe removes the handler. If, instead, you used
the body of the lambda expression, you are attempting to remove a handler that has
never been attached, which does nothing.
In the next article, you'll learn more about typical event patterns, and different variations
on this example.
Next
Standard .NET event patterns
Article • 09/08/2022
Previous
.NET events generally follow a few known patterns. Standardizing on these patterns
means that developers can leverage knowledge of those standard patterns, which can
be applied to any .NET event program.
Let's go through these standard patterns so you will have all the knowledge you need to
create standard event sources, and subscribe and process standard events in your code.
C#
The return type is void. Events are based on delegates and are multicast delegates. That
supports multiple subscribers for any event source. The single return value from a
method doesn't scale to multiple event subscribers. Which return value does the event
source see after raising an event? Later in this article you'll see how to create event
protocols that support event subscribers that report information to the event source.
The argument list contains two arguments: the sender, and the event arguments. The
compile-time type of sender is System.Object , even though you likely know a more
derived type that would always be correct. By convention, use object .
The second argument has typically been a type that is derived from System.EventArgs .
(You'll see in the next section that this convention is no longer enforced.) If your event
type does not need any additional arguments, you will still provide both arguments.
There is a special value, EventArgs.Empty that you should use to denote that your event
does not contain any additional information.
Let's build a class that lists files in a directory, or any of its subdirectories that follow a
pattern. This component raises an event for each file found that matches the pattern.
Using an event model provides some design advantages. You can create multiple event
listeners that perform different actions when a sought file is found. Combining the
different listeners can create more robust algorithms.
Here is the initial event argument declaration for finding a sought file:
C#
Even though this type looks like a small, data-only type, you should follow the
convention and make it a reference ( class ) type. That means the argument object will
be passed by reference, and any updates to the data will be viewed by all subscribers.
The first version is an immutable object. You should prefer to make the properties in
your event argument type immutable. That way, one subscriber cannot change the
values before another subscriber sees them. (There are exceptions to this, as you'll see
below.)
Next, we need to create the event declaration in the FileSearcher class. Leveraging the
EventHandler<T> type means that you don't need to create yet another type definition.
Let's fill out the FileSearcher class to search for files that match a pattern, and raise the
correct event when a match is discovered.
C#
C#
This looks like it's declaring a public field, which would appear to be a bad object-
oriented practice. You want to protect data access through properties, or methods.
While this may look like a bad practice, the code generated by the compiler does create
wrappers so that the event objects can only be accessed in safe ways. The only
operations available on a field-like event are add handler:
C#
fileLister.FileFound += onFileFound;
C#
fileLister.FileFound -= onFileFound;
Note that there's a local variable for the handler. If you used the body of the lambda,
the remove would not work correctly. It would be a different instance of the delegate,
and silently do nothing.
Code outside the class cannot raise the event, nor can it perform any other operations.
The event handlers do not return a value, so you need to communicate that in another
way. The standard event pattern uses the EventArgs object to include fields that event
subscribers can use to communicate cancel.
Two different patterns could be used, based on the semantics of the Cancel contract. In
both cases, you'll add a boolean field to the EventArguments for the found file event.
One pattern would allow any one subscriber to cancel the operation. For this pattern,
the new field is initialized to false . Any subscriber can change it to true . After all
subscribers have seen the event raised, the FileSearcher component examines the
boolean value and takes action.
The second pattern would only cancel the operation if all subscribers wanted the
operation canceled. In this pattern, the new field is initialized to indicate the operation
should cancel, and any subscriber could change it to indicate the operation should
continue. After all subscribers have seen the event raised, the FileSearcher component
examines the boolean and takes action. There is one extra step in this pattern: the
component needs to know if any subscribers have seen the event. If there are no
subscribers, the field would indicate a cancel incorrectly.
Let's implement the first version for this sample. You need to add a boolean field named
CancelRequested to the FileFoundArgs type:
C#
This new field is automatically initialized to false , the default value for a Boolean field,
so you don't cancel accidentally. The only other change to the component is to check
the flag after raising the event to see if any of the subscribers have requested a
cancellation:
C#
One advantage of this pattern is that it isn't a breaking change. None of the subscribers
requested cancellation before, and they still are not. None of the subscriber code needs
updating unless they want to support the new cancel protocol. It's very loosely coupled.
Let's update the subscriber so that it requests a cancellation once it finds the first
executable:
C#
This could get to be a lengthy operation in a directory with many sub-directories. Let's
add an event that gets raised when each new directory search begins. This enables
subscribers to track progress, and update the user as to progress. All the samples you've
created so far are public. Let's make this one an internal event. That means you can also
make the types used for the arguments internal as well.
You'll start by creating the new EventArgs derived class for reporting the new directory
and progress.
C#
Again, you can follow the recommendations to make an immutable reference type for
the event arguments.
Next, define the event. This time, you'll use a different syntax. In addition to using the
field syntax, you can explicitly create the property, with add and remove handlers. In this
sample, you won't need extra code in those handlers, but this shows how you would
create them.
C#
In many ways, the code you write here mirrors the code the compiler generates for the
field event definitions you've seen earlier. You create the event using syntax very similar
to that used for properties. Notice that the handlers have different names: add and
remove . These are called to subscribe to the event, or unsubscribe from the event.
Notice that you also must declare a private backing field to store the event variable. It is
initialized to null.
Next, let's add the overload of the Search method that traverses subdirectories and
raises both events. The easiest way to accomplish this is to use a default argument to
specify that you want to search all directories:
C#
public void Search(string directory, string searchPattern, bool
searchSubDirs = false)
{
if (searchSubDirs)
{
var allDirectories = Directory.GetDirectories(directory, "*.*",
SearchOption.AllDirectories);
var completedDirs = 0;
var totalDirs = allDirectories.Length + 1;
foreach (var dir in allDirectories)
{
RaiseSearchDirectoryChanged(dir, totalDirs, completedDirs++);
// Search 'dir' and its subdirectories for files that match the
search pattern:
SearchDirectory(dir, searchPattern);
}
// Include the Current Directory:
RaiseSearchDirectoryChanged(directory, totalDirs, completedDirs++);
SearchDirectory(directory, searchPattern);
}
else
{
SearchDirectory(directory, searchPattern);
}
}
Let's add a handler to write a line that shows the progress in the console window.
C#
You've seen patterns that are followed throughout the .NET ecosystem. By learning
these patterns and conventions, you'll be writing idiomatic C# and .NET quickly.
See also
Introduction to events
Event design
Handle and raise events
Next, you'll see some changes in these patterns in the most recent release of .NET.
Next
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
The Updated .NET Core Event Pattern
Article • 02/13/2023
Previous
The previous article discussed the most common event patterns. .NET Core has a more
relaxed pattern. In this version, the EventHandler<TEventArgs> definition no longer has
the constraint that TEventArgs must be a class derived from System.EventArgs .
This increases flexibility for you, and is backwards compatible. Let's start with the
flexibility. The class System.EventArgs introduces one method: MemberwiseClone() , which
creates a shallow copy of the object. That method must use reflection in order to
implement its functionality for any class derived from EventArgs . That functionality is
easier to create in a specific derived class. That effectively means that deriving from
System.EventArgs is a constraint that limits your designs, but does not provide any
additional benefit. In fact, you can change the definitions of FileFoundArgs and
SearchDirectoryArgs so that they do not derive from EventArgs . The program will work
You could also change the SearchDirectoryArgs to a struct, if you make one more
change:
C#
The additional change is to call the parameterless constructor before entering the
constructor that initializes all the fields. Without that addition, the rules of C# would
report that the properties are being accessed before they have been assigned.
You should not change the FileFoundArgs from a class (reference type) to a struct (value
type). That's because the protocol for handling cancel requires that the event arguments
are passed by reference. If you made the same change, the file search class could never
observe any changes made by any of the event subscribers. A new copy of the structure
would be used for each subscriber, and that copy would be a different copy than the
one seen by the file search object.
Next, let's consider how this change can be backwards compatible. The removal of the
constraint does not affect any existing code. Any existing event argument types do still
derive from System.EventArgs . Backwards compatibility is one major reason why they
will continue to derive from System.EventArgs . Any existing event subscribers will be
subscribers to an event that followed the classic pattern.
Following similar logic, any event argument type created now would not have any
subscribers in any existing codebases. New event types that do not derive from
System.EventArgs will not break those codebases.
You need to reconcile this opposing guidance. Somehow, you must create a safe async
void method. The basics of the pattern you need to implement are below:
C#
That's why you should wrap the await statement for the async Task in your own try
block. If it does cause a faulted task, you can log the error. If it is an error from which
your application cannot recover, you can exit the program quickly and gracefully
Those are the major updates to the .NET event pattern. You will see many examples of
the earlier versions in the libraries you work with. However, you should understand what
the latest patterns are as well.
The next article in this series helps you distinguish between using delegates and events
in your designs. They are similar concepts, and that article will help you make the best
decision for your programs.
Next
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Distinguishing Delegates and Events
Article • 11/05/2021
Previous
Developers that are new to the .NET Core platform often struggle when deciding
between a design based on delegates and a design based on events . The choice of
delegates or events is often difficult, because the two language features are similar.
Events are even built using the language support for delegates.
They both offer a late binding scenario: they enable scenarios where a component
communicates by calling a method that is only known at run time. They both support
single and multiple subscriber methods. You may find this referred to as singlecast and
multicast support. They both support similar syntax for adding and removing handlers.
Finally, raising an event and calling a delegate use exactly the same method call syntax.
They even both support the same Invoke() method syntax for use with the ?. operator.
With all those similarities, it is easy to have trouble determining when to use which.
Consider the examples built during this section. The code you built using List.Sort()
must be given a comparer function in order to properly sort the elements. LINQ queries
must be supplied with delegates in order to determine what elements to return. Both
used a design built with delegates.
Consider the Progress event. It reports progress on a task. The task continues to
proceed whether or not there are any listeners. The FileSearcher is another example. It
would still search and find all the files that were sought, even with no event subscribers
attached. UX controls still work correctly, even when there are no subscribers listening to
the events. They both use designs based on events.
Notice that these two heuristics may often both be present: If your delegate method
returns a value, it will likely impact the algorithm in some way.
Evaluate Carefully
The above considerations are not hard and fast rules. Instead, they represent guidance
that can help you decide which choice is best for your particular usage. Because they are
similar, you can even prototype both, and consider which would be more natural to
work with. They both handle late binding scenarios well. Use the one that communicates
your design the best.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
be found on GitHub, where you Select a link to provide feedback:
can also create and review
issues and pull requests. For Open a documentation issue
more information, see our
contributor guide. Provide product feedback
Versioning in C#
Article • 02/08/2023
In this tutorial you'll learn what versioning means in .NET. You'll also learn the factors to
consider when versioning your library as well as upgrading to a new version of a library.
Authoring Libraries
As a developer who has created .NET libraries for public use, you've most likely been in
situations where you have to roll out new updates. How you go about this process
matters a lot as you need to ensure that there's a seamless transition of existing code to
the new version of your library. Here are several things to consider when creating a new
release:
Semantic Versioning
Semantic versioning (SemVer for short) is a naming convention applied to versions of
your library to signify specific milestone events. Ideally, the version information you give
your library should help developers determine the compatibility with their projects that
make use of older versions of that same library.
manner
PATCH is incremented when you make backwards-compatible bug fixes
There are also ways to specify other scenarios, for example, pre-release versions, when
applying version information to your .NET library.
Backwards Compatibility
As you release new versions of your library, backwards compatibility with previous
versions will most likely be one of your major concerns. A new version of your library is
source compatible with a previous version if code that depends on the previous version
can, when recompiled, work with the new version. A new version of your library is binary
compatible if an application that depended on the old version can, without
recompilation, work with the new version.
Here are some things to consider when trying to maintain backwards compatibility with
older versions of your library:
Virtual methods: When you make a virtual method non-virtual in your new version
it means that projects that override that method will have to be updated. This is a
huge breaking change and is strongly discouraged.
Method signatures: When updating a method behavior requires you to change its
signature as well, you should instead create an overload so that code calling into
that method will still work. You can always manipulate the old method signature to
call into the new method signature so that implementation remains consistent.
Obsolete attribute: You can use this attribute in your code to specify classes or
class members that are deprecated and likely to be removed in future versions.
This ensures developers utilizing your library are better prepared for breaking
changes.
Optional Method Arguments: When you make previously optional method
arguments compulsory or change their default value then all code that does not
supply those arguments will need to be updated.
7 Note
Making compulsory arguments optional should have very little effect especially if it
doesn't change the method's behavior.
The easier you make it for your users to upgrade to the new version of your library, the
more likely that they will upgrade sooner.
Consuming Libraries
As a developer that consumes .NET libraries built by other developers you're most likely
aware that a new version of a library might not be fully compatible with your project and
you might often find yourself having to update your code to work with those changes.
Lucky for you, C# and the .NET ecosystem comes with features and techniques that
allow us to easily update our app to work with new versions of libraries that might
introduce breaking changes.
XML
<dependentAssembly>
<assemblyIdentity name="ReferencedLibrary"
publicKeyToken="32ab4ba45e0a69a1" culture="en-us" />
<bindingRedirect oldVersion="1.0.0" newVersion="1.0.1" />
</dependentAssembly>
7 Note
This approach will only work if the new version of ReferencedLibrary is binary
compatible with your app. See the Backwards Compatibility section above for
changes to look out for when determining compatibility.
new
You use the new modifier to hide inherited members of a base class. This is one way
derived classes can respond to updates in base classes.
C#
b.MyMethod();
d.MyMethod();
}
Output
Console
A base method
A derived method
From the example above you can see how DerivedClass hides the MyMethod method
present in BaseClass . This means that when a base class in the new version of a library
adds a member that already exists in your derived class, you can simply use the new
modifier on your derived class member to hide the base class member.
When no new modifier is specified, a derived class will by default hide conflicting
members in a base class, although a compiler warning will be generated the code will
still compile. This means that simply adding new members to an existing class makes
that new version of your library both source and binary compatible with code that
depends on it.
override
The override modifier means a derived implementation extends the implementation of
a base class member rather than hides it. The base class member needs to have the
virtual modifier applied to it.
C#
Output
Console
The override modifier is evaluated at compile time and the compiler will throw an error
if it doesn't find a virtual member to override.
Your knowledge of the discussed techniques and your understanding of the situations in
which to use them, will go a long way towards easing the transition between versions of
a library.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to (C#)
Article • 02/13/2023
In the How to section of the C# Guide, you can find quick answers to common
questions. In some cases, articles may be listed in multiple sections. We wanted to make
them easy to find for multiple search paths.
General C# concepts
There are several tips and tricks that are common C# developer practices:
Exception handling
.NET programs report that methods did not successfully complete their work by
throwing exceptions. In these articles you'll learn to work with exceptions.
LINQ practices
LINQ enables you to write code to query any data source that supports the LINQ query
expression pattern. These articles help you understand the pattern and work with
different data sources.
Query a collection.
Use var in query expressions.
Return subsets of element properties from a query.
Write queries with complex filtering.
Sort elements of a data source.
Sort elements on multiple keys.
Control the type of a projection.
Count occurrences of a value in a source sequence.
Calculate intermediate values.
Debug empty query results.
Add custom methods to LINQ queries.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to separate strings using
String.Split in C#
Article • 03/08/2024
The String.Split method creates an array of substrings by splitting the input string based
on one or more delimiters. This method is often the easiest way to separate a string on
word boundaries. It's also used to split strings on other specific characters or strings.
7 Note
The C# examples in this article run in the Try.NET inline code runner and
playground. Select the Run button to run an example in an interactive window.
Once you execute the code, you can modify it and run the modified code by
selecting Run again. The modified code either runs in the interactive window or, if
compilation fails, the interactive window displays all C# compiler error messages.
The following code splits a common phrase into an array of strings for each word.
C#
string phrase = "The quick brown fox jumps over the lazy dog.";
string[] words = phrase.Split(' ');
Every instance of a separator character produces a value in the returned array. Since
arrays in C# are zero-indexed, each string in the array is indexed from 0 to the value
returned by the Array.Length property minus 1:
C#
string phrase = "The quick brown fox jumps over the lazy dog.";
string[] words = phrase.Split(' ');
C#
string phrase = "The quick brown fox jumps over the lazy dog.";
string[] words = phrase.Split(' ');
This behavior makes it easier for formats like comma-separated values (CSV) files
representing tabular data. Consecutive commas represent a blank column.
String.Split can use multiple separator characters. The following example uses spaces,
commas, periods, colons, and tabs as separating characters, which are passed to Split in
an array. The loop at the bottom of the code displays each of the words in the returned
array.
C#
Consecutive instances of any separator produce the empty string in the output array:
C#
String.Split can take an array of strings (character sequences that act as separators for
parsing the target string, instead of single characters).
C#
See also
Extract elements from a string
Strings
.NET regular expressions
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to concatenate multiple strings (C#
Guide)
Article • 03/15/2024
Concatenation is the process of appending one string to the end of another string. You
concatenate strings by using the + operator. For string literals and string constants,
concatenation occurs at compile time; no run-time concatenation occurs. For string
variables, concatenation occurs only at run time.
7 Note
The C# examples in this article run in the Try.NET inline code runner and
playground. Select the Run button to run an example in an interactive window.
Once you execute the code, you can modify it and run the modified code by
selecting Run again. The modified code either runs in the interactive window or, if
compilation fails, the interactive window displays all C# compiler error messages.
String literals
The following example splits a long string literal into smaller strings to improve
readability in the source code. The code concatenates the smaller strings to create the
long string literal. The parts are concatenated into a single string at compile time.
There's no run-time performance cost regardless of the number of strings involved.
C#
System.Console.WriteLine(text);
+ and += operators
To concatenate string variables, you can use the + or += operators, string interpolation
or the String.Format, String.Concat, String.Join or StringBuilder.Append methods. The +
operator is easy to use and makes for intuitive code. Even if you use several + operators
in one statement, the string content is copied only once. The following code shows
examples of using the + and += operators to concatenate strings:
C#
String interpolation
In some expressions, it's easier to concatenate strings using string interpolation, as the
following code shows:
C#
7 Note
In string concatenation operations, the C# compiler treats a null string the same as
an empty string.
Beginning with C# 10, you can use string interpolation to initialize a constant string
when all the expressions used for placeholders are also constant strings.
String.Format
Another method to concatenate strings is String.Format. This method works well when
you're building a string from a small number of component strings.
StringBuilder
In other cases, you may be combining strings in a loop where you don't know how many
source strings you're combining, and the actual number of source strings may be large.
The StringBuilder class was designed for these scenarios. The following code uses the
Append method of the StringBuilder class to concatenate strings.
C#
You can read more about the reasons to choose string concatenation or the
StringBuilder class.
String.Concat or String.Join
Another option to join strings from a collection is to use String.Concat method. Use
String.Join method if a delimiter should separate source strings. The following code
combines an array of words using both methods:
C#
C#
This option can cause more allocations than other methods for concatenating
collections, as it creates an intermediate string for each iteration. If optimizing
performance is critical, consider the StringBuilder class or the String.Concat or String.Join
method to concatenate a collection, instead of Enumerable.Aggregate .
See also
String
StringBuilder
Strings
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
How to search strings
Article • 09/15/2021
You can use two main strategies to search for text in strings. Methods of the String class
search for specific text. Regular expressions search for patterns in text.
7 Note
The C# examples in this article run in the Try.NET inline code runner and
playground. Select the Run button to run an example in an interactive window.
Once you execute the code, you can modify it and run the modified code by
selecting Run again. The modified code either runs in the interactive window or, if
compilation fails, the interactive window displays all C# compiler error messages.
The string type, which is an alias for the System.String class, provides a number of useful
methods for searching the contents of a string. Among them are Contains, StartsWith,
EndsWith, IndexOf, LastIndexOf. The System.Text.RegularExpressions.Regex class
provides a rich vocabulary to search for patterns in text. In this article, you learn these
techniques and how to choose the best method for your needs.
C#
// For user input and strings that will be displayed to the end user,
// use the StringComparison parameter on methods that have it to specify how
to match strings.
bool ignoreCaseSearchResult = factMessage.StartsWith("extension",
System.StringComparison.CurrentCultureIgnoreCase);
Console.WriteLine($"Starts with \"extension\"? {ignoreCaseSearchResult}
(ignoring case)");
The preceding example demonstrates an important point for using these methods.
Searches are case-sensitive by default. You use the
StringComparison.CurrentCultureIgnoreCase enumeration value to specify a case-
insensitive search.
C#
The following code example searches for the word "the" or "their" in a sentence,
ignoring case. The static method Regex.IsMatch performs the search. You give it the
string to search and a search pattern. In this case, a third argument specifies case-
insensitive search. For more information, see
System.Text.RegularExpressions.RegexOptions.
The search pattern describes the text you search for. The following table describes each
element of the search pattern. (The table below uses the single \ , which must be
escaped as \\ in a C# string).
ノ Expand table
Pattern Meaning
C#
string[] sentences =
{
"Put the water over there.",
"They're quite thirsty.",
"Their water bottles broke."
};
if (System.Text.RegularExpressions.Regex.IsMatch(s, sPattern,
System.Text.RegularExpressions.RegexOptions.IgnoreCase))
{
Console.WriteLine($" (match for '{sPattern}' found)");
}
else
{
Console.WriteLine();
}
}
Tip
The string methods are usually better choices when you are searching for an exact
string. Regular expressions are better when you are searching for some pattern in a
source string.
Does a string follow a pattern?
The following code uses regular expressions to validate the format of each string in an
array. The validation requires that each string have the form of a telephone number in
which three groups of digits are separated by dashes, the first two groups contain three
digits, and the third group contains four digits. The search pattern uses the regular
expression ^\\d{3}-\\d{3}-\\d{4}$ . For more information, see Regular Expression
Language - Quick Reference.
ノ Expand table
Pattern Meaning
C#
string[] numbers =
{
"123-555-0190",
"444-234-22450",
"690-555-0178",
"146-893-232",
"146-555-0122",
"4007-555-0111",
"407-555-0111",
"407-2-5555",
"407-555-8974",
"407-2ab-5555",
"690-555-8148",
"146-893-232-"
};
if (System.Text.RegularExpressions.Regex.IsMatch(s, sPattern))
{
Console.WriteLine(" - valid");
}
else
{
Console.WriteLine(" - invalid");
}
}
This single search pattern matches many valid strings. Regular expressions are better to
search for or validate against a pattern, rather than a single text string.
See also
Strings
System.Text.RegularExpressions.Regex
.NET regular expressions
Regular expression language - quick reference
Best practices for using strings in .NET
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to modify string contents in C#
Article • 09/15/2021
7 Note
The C# examples in this article run in the Try.NET inline code runner and
playground. Select the Run button to run an example in an interactive window.
Once you execute the code, you can modify it and run the modified code by
selecting Run again. The modified code either runs in the interactive window or, if
compilation fails, the interactive window displays all C# compiler error messages.
There are several techniques demonstrated in this article. You can replace existing text.
You can search for patterns and replace matching text with other text. You can treat a
string as a sequence of characters. You can also use convenience methods that remove
white space. Choose the techniques that most closely match your scenario.
Replace text
The following code creates a new string by replacing existing text with a substitute.
C#
The preceding code demonstrates this immutable property of strings. You can see in the
preceding example that the original string, source , is not modified. The String.Replace
method creates a new string containing the modifications.
The Replace method can replace either strings or single characters. In both cases, every
occurrence of the sought text is replaced. The following example replaces all ' '
characters with '_':
C#
The source string is unchanged, and a new string is returned with the replacement.
C#
Remove text
You can remove text from a string using the String.Remove method. This method
removes a number of characters starting at a specific index. The following example
shows how to use String.IndexOf followed by Remove to remove text from a string:
C#
Regular expressions are most useful for searching and replacing text that follows a
pattern, rather than known text. For more information, see How to search strings. The
search pattern, "the\s" searches for the word "the" followed by a white-space character.
That part of the pattern ensures that it doesn't match "there" in the source string. For
more information on regular expression language elements, see Regular Expression
Language - Quick Reference.
C#
string source = "The mountains are still there behind the clouds today.";
string LocalReplaceMatchCase(System.Text.RegularExpressions.Match
matchExpression)
{
// Test whether the match is capitalized
if (Char.IsUpper(matchExpression.Value[0]))
{
// Capitalize the replacement string
System.Text.StringBuilder replacementBuilder = new
System.Text.StringBuilder(replaceWith);
replacementBuilder[0] = Char.ToUpper(replacementBuilder[0]);
return replacementBuilder.ToString();
}
else
{
return replaceWith;
}
}
The StringBuilder.ToString method returns an immutable string with the contents in the
StringBuilder object.
The following example shows how to replace a set of characters in a string. First, it uses
the String.ToCharArray() method to create an array of characters. It uses the IndexOf
method to find the starting index of the word "fox." The next three characters are
replaced with a different word. Finally, a new string is constructed from the updated
character array.
C#
string phrase = "The quick brown fox jumps over the fence";
Console.WriteLine(phrase);
C#
Console.WriteLine(result);
You could modify a string in a fixed block with unsafe code, but it is strongly
discouraged to modify the string content after a string is created. Doing so will break
things in unpredictable ways. For example, if someone interns a string that has the same
content as yours, they'll get your copy and won't expect that you are modifying their
string.
See also
.NET regular expressions
Regular expression language - quick reference
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
How to compare strings in C#
Article • 03/19/2024
You compare strings to answer one of two questions: "Are these two strings equal?" or
"In what order should these strings be placed when sorting them?"
Those two questions are complicated by factors that affect string comparisons:
CurrentCulture: Compare strings using culture-sensitive sort rules and the current
culture.
CurrentCultureIgnoreCase: Compare strings using culture-sensitive sort rules, the
current culture, and ignoring the case of the strings being compared.
InvariantCulture: Compare strings using culture-sensitive sort rules and the
invariant culture.
InvariantCultureIgnoreCase: Compare strings using culture-sensitive sort rules, the
invariant culture, and ignoring the case of the strings being compared.
Ordinal: Compare strings using ordinal (binary) sort rules.
OrdinalIgnoreCase: Compare strings using ordinal (binary) sort rules and ignoring
the case of the strings being compared.
7 Note
The C# examples in this article run in the Try.NET inline code runner and
playground. Select the Run button to run an example in an interactive window.
Once you execute the code, you can modify it and run the modified code by
selecting Run again. The modified code either runs in the interactive window or, if
compilation fails, the interactive window displays all C# compiler error messages.
When you compare strings, you define an order among them. Comparisons are used to
sort a sequence of strings. Once the sequence is in a known order, it's easier to search,
both for software and for humans. Other comparisons might check if strings are the
same. These sameness checks are similar to equality, but some differences, such as case
differences, might be ignored.
Default ordinal comparisons
By default, the most common operations:
String.Equals
String.Equality and String.Inequality, that is, equality operators == and !=,
respectively perform a case-sensitive, ordinal comparison. String.Equals has an
overload where a StringComparison argument can be provided to alter its sorting
rules. The following example demonstrates that:
C#
The default ordinal comparison doesn't take linguistic rules into account when
comparing strings. It compares the binary value of each Char object in two strings. As a
result, the default ordinal comparison is also case-sensitive.
The test for equality with String.Equals and the == and != operators differs from string
comparison using the String.CompareTo and Compare(String, String) methods. They all
perform a case-sensitive comparison. However, while the tests for equality perform an
ordinal comparison, the CompareTo and Compare methods perform a culture-aware
linguistic comparison using the current culture. Make the intent of your code clear by
calling an overload that explicitly specifies the type of comparison to perform.
C#
These methods use the casing conventions of the invariant culture when performing a
case-insensitive ordinal comparison.
Linguistic comparisons
Many string comparison methods (such as String.StartsWith) use linguistic rules for the
current culture by default to order their inputs. This linguistic comparison is sometimes
referred to as "word sort order." When you perform a linguistic comparison, some
nonalphanumeric Unicode characters might have special weights assigned. For example,
the hyphen "-" might have a small weight assigned to it so that "co-op" and "coop"
appear next to each other in sort order. Some nonprinting control characters might be
ignored. In addition, some Unicode characters might be equivalent to a sequence of
Char instances. The following example uses the phrase "They dance in the street." in
German with the "ss" (U+0073 U+0073) in one string and 'ß' (U+00DF) in another.
Linguistically (in Windows), "ss" is equal to the German Esszet: 'ß' character in both the
"en-US" and "de-DE" cultures.
C#
showComparison(word, words);
showComparison(word, other);
showComparison(words, other);
void showComparison(string one, string two)
{
int compareLinguistic = String.Compare(one, two,
StringComparison.InvariantCulture);
int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal);
if (compareLinguistic < 0)
Console.WriteLine($"<{one}> is less than <{two}> using invariant
culture");
else if (compareLinguistic > 0)
Console.WriteLine($"<{one}> is greater than <{two}> using invariant
culture");
else
Console.WriteLine($"<{one}> and <{two}> are equivalent in order
using invariant culture");
if (compareOrdinal < 0)
Console.WriteLine($"<{one}> is less than <{two}> using ordinal
comparison");
else if (compareOrdinal > 0)
Console.WriteLine($"<{one}> is greater than <{two}> using ordinal
comparison");
else
Console.WriteLine($"<{one}> and <{two}> are equivalent in order
using ordinal comparison");
}
On Windows, prior to .NET 5, the sort order of "cop", "coop", and "co-op" changes when
you change from a linguistic comparison to an ordinal comparison. The two German
sentences also compare differently using the different comparison types. Prior to .NET 5,
the .NET globalization APIs used National Language Support (NLS) libraries. In .NET 5
and later versions, the .NET globalization APIs use International Components for
Unicode (ICU) libraries, which unifies .NET's globalization behavior across all
supported operating systems.
Comparisons using specific cultures
The following example stores CultureInfo objects for the en-US and de-DE cultures. The
comparisons are performed using a CultureInfo object to ensure a culture-specific
comparison. The culture used affects linguistic comparisons. The following example
shows the results of comparing the two German sentences using the "en-US" culture
and the "de-DE" culture:
C#
Culture-sensitive comparisons are typically used to compare and sort strings input by
users with other strings input by users. The characters and sorting conventions of these
strings might vary depending on the locale of the user's computer. Even strings that
contain identical characters might sort differently depending on the culture of the
current thread.
The following example shows how to sort an array of strings using the current culture:
C#
Console.WriteLine("Non-sorted order:");
foreach (string s in lines)
{
Console.WriteLine($" {s}");
}
Console.WriteLine("\n\rSorted order:");
Once the array is sorted, you can search for entries using a binary search. A binary
search starts in the middle of the collection to determine which half of the collection
would contain the sought string. Each subsequent comparison subdivides the remaining
part of the collection in half. The array is sorted using the
StringComparer.CurrentCulture. The local function ShowWhere displays information about
where the string was found. If the string wasn't found, the returned value indicates
where it would be if it were found.
C#
if (index == 0)
Console.Write("beginning of sequence and ");
else
Console.Write($"{array[index - 1]} and ");
if (index == array.Length)
Console.WriteLine("end of sequence.");
else
Console.WriteLine($"{array[index]}.");
}
else
{
Console.WriteLine($"Found at index {index}.");
}
}
C#
Console.WriteLine("Non-sorted order:");
foreach (string s in lines)
{
Console.WriteLine($" {s}");
}
Console.WriteLine("\n\rSorted order:");
Once sorted, the list of strings can be searched using a binary search. The following
sample shows how to search the sorted list using the same comparison function. The
local function ShowWhere shows where the sought text is or would be:
C#
List<string> lines = new List<string>
{
@"c:\public\textfile.txt",
@"c:\public\textFile.TXT",
@"c:\public\Text.txt",
@"c:\public\testfile2.txt"
};
lines.Sort((left, right) => left.CompareTo(right));
if (index == 0)
Console.Write("beginning of sequence and ");
else
Console.Write($"{collection[index - 1]} and ");
if (index == collection.Count)
Console.WriteLine("end of sequence.");
else
Console.WriteLine($"{collection[index]}.");
}
else
{
Console.WriteLine($"Found at index {index}.");
}
}
Always make sure to use the same type of comparison for sorting and searching. Using
different comparison types for sorting and searching produces unexpected results.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to catch a non-CLS exception
Article • 09/15/2021
Some .NET languages, including C++/CLI, allow objects to throw exceptions that do not
derive from Exception. Such exceptions are called non-CLS exceptions or non-Exceptions.
In C# you cannot throw non-CLS exceptions, but you can catch them in two ways:
Within a general catch block (a catch block without an exception type specified)
that is put after all other catch blocks.
Use this method when you want to perform some action (such as writing to a log
file) in response to non-CLS exceptions, and you do not need access to the
exception information. By default the common language runtime wraps all
exceptions. To disable this behavior, add this assembly-level attribute to your code,
typically in the AssemblyInfo.cs file: [assembly:
RuntimeCompatibilityAttribute(WrapNonExceptionThrows = false)] .
Example
The following example shows how to catch a non-CLS exception that was thrown from a
class library written in C++/CLI. Note that in this example, the C# client code knows in
advance that the exception type being thrown is a System.String. You can cast the
RuntimeWrappedException.WrappedException property back its original type as long as
that type is accessible from your code.
C#
// Class library written in C++/CLI.
var myClass = new ThrowNonCLS.Class1();
try
{
// throws gcnew System::String(
// "I do not derive from System.Exception!");
myClass.TestThrow();
}
catch (RuntimeWrappedException e)
{
String s = e.WrappedException as String;
if (s != null)
{
Console.WriteLine(s);
}
}
See also
RuntimeWrappedException
Exceptions and Exception Handling
Attributes
Article • 03/15/2023
Attributes add metadata to your program. Metadata is information about the types
defined in a program. All .NET assemblies contain a specified set of metadata that
describes the types and type members defined in the assembly. You can add
custom attributes to specify any additional information that is required.
You can apply one or more attributes to entire assemblies, modules, or smaller
program elements such as classes and properties.
Attributes can accept arguments in the same way as methods and properties.
Your program can examine its own metadata or the metadata in other programs
by using reflection.
Reflection provides objects (of type Type) that describe assemblies, modules, and types.
You can use reflection to dynamically create an instance of a type, bind the type to an
existing object, or get the type from an existing object and invoke its methods or access
its fields and properties. If you're using attributes in your code, reflection enables you to
access them. For more information, see Attributes.
Here's a simple example of reflection using the GetType() method - inherited by all types
from the Object base class - to obtain the type of a variable:
7 Note
Make sure you add using System; and using System.Reflection; at the top of
your .cs file.
C#
The following example uses reflection to obtain the full name of the loaded assembly.
C#
7 Note
Using attributes
Attributes can be placed on almost any declaration, though a specific attribute might
restrict the types of declarations on which it's valid. In C#, you specify an attribute by
placing the name of the attribute enclosed in square brackets ( [] ) above the
declaration of the entity to which it applies.
C#
[Serializable]
public class SampleClass
{
// Objects of this type can be serialized.
}
A method with the attribute DllImportAttribute is declared like the following example:
C#
[System.Runtime.InteropServices.DllImport("user32.dll")]
extern static void SampleMethod();
More than one attribute can be placed on a declaration as the following example shows:
C#
Some attributes can be specified more than once for a given entity. An example of such
a multiuse attribute is ConditionalAttribute:
C#
[Conditional("DEBUG"), Conditional("TEST1")]
void TraceMethod()
{
// ...
}
7 Note
By convention, all attribute names end with the word "Attribute" to distinguish
them from other items in the .NET libraries. However, you do not need to specify
the attribute suffix when using attributes in code. For example, [DllImport] is
equivalent to [DllImportAttribute] , but DllImportAttribute is the attribute's
actual name in the .NET Class Library.
Attribute parameters
Many attributes have parameters, which can be positional, unnamed, or named. Any
positional parameters must be specified in a certain order and can't be omitted. Named
parameters are optional and can be specified in any order. Positional parameters are
specified first. For example, these three attributes are equivalent:
C#
[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]
The first parameter, the DLL name, is positional and always comes first; the others are
named. In this case, both named parameters default to false, so they can be omitted.
Positional parameters correspond to the parameters of the attribute constructor. Named
or optional parameters correspond to either properties or fields of the attribute. Refer to
the individual attribute's documentation for information on default parameter values.
For more information on allowed parameter types, see the Attributes section of the C#
language specification
Attribute targets
The target of an attribute is the entity that the attribute applies to. For example, an
attribute may apply to a class, a particular method, or an entire assembly. By default, an
attribute applies to the element that follows it. But you can also explicitly identify, for
example, whether an attribute is applied to a method, or to its parameter, or to its return
value.
C#
[target : attribute-list]
ノ Expand table
event Event
property Property
The following example shows how to apply attributes to assemblies and modules. For
more information, see Common Attributes (C#).
C#
using System;
using System.Reflection;
[assembly: AssemblyTitleAttribute("Production assembly 4")]
[module: CLSCompliant(true)]
The following example shows how to apply attributes to methods, method parameters,
and method return values in C#.
C#
// applies to method
[method: ValidatedContract]
int Method2() { return 0; }
// applies to parameter
int Method3([ValidatedContract] string contract) { return 0; }
7 Note
only to return values. In other words, the compiler will not use AttributeUsage
information to resolve ambiguous attribute targets. For more information, see
AttributeUsage.
Reflection overview
Reflection is useful in the following situations:
When you have to access attributes in your program's metadata. For more
information, see Retrieving Information Stored in Attributes.
For examining and instantiating types in an assembly.
For building new types at run time. Use classes in System.Reflection.Emit.
For performing late binding, accessing methods on types created at run time. See
the article Dynamically Loading and Using Types.
Related sections
For more information:
You can create your own custom attributes by defining an attribute class, a class that
derives directly or indirectly from Attribute, which makes identifying attribute definitions
in metadata fast and easy. Suppose you want to tag types with the name of the
programmer who wrote the type. You might define a custom Author attribute class:
C#
[System.AttributeUsage(System.AttributeTargets.Class |
System.AttributeTargets.Struct)
]
public class AuthorAttribute : System.Attribute
{
private string Name;
public double Version;
The class name AuthorAttribute is the attribute's name, Author , plus the Attribute
suffix. It's derived from System.Attribute , so it's a custom attribute class. The
constructor's parameters are the custom attribute's positional parameters. In this
example, name is a positional parameter. Any public read-write fields or properties are
named parameters. In this case, version is the only named parameter. Note the use of
the AttributeUsage attribute to make the Author attribute valid only on class and
struct declarations.
C#
C#
[System.AttributeUsage(System.AttributeTargets.Class |
System.AttributeTargets.Struct,
AllowMultiple = true) // Multiuse attribute.
]
public class AuthorAttribute : System.Attribute
{
string Name;
public double Version;
// Default value.
Version = 1.0;
}
In the following code example, multiple attributes of the same type are applied to a
class.
C#
See also
System.Reflection
Writing Custom Attributes
AttributeUsage (C#)
Access attributes using reflection
Article • 03/15/2023
The fact that you can define custom attributes and place them in your source code
would be of little value without some way of retrieving that information and acting on it.
By using reflection, you can retrieve the information that was defined with custom
attributes. The key method is GetCustomAttributes , which returns an array of objects
that are the run-time equivalents of the source code attributes. This method has many
overloaded versions. For more information, see Attribute.
C#
C#
However, the code isn't executed until SampleClass is queried for attributes. Calling
GetCustomAttributes on SampleClass causes an Author object to be constructed and
initialized. If the class has other attributes, other attribute objects are constructed
similarly. GetCustomAttributes then returns the Author object and any other attribute
objects in an array. You can then iterate over this array, determine what attributes were
applied based on the type of each array element, and extract information from the
attribute objects.
C#
// Multiuse attribute.
[System.AttributeUsage(System.AttributeTargets.Class |
System.AttributeTargets.Struct,
AllowMultiple = true) // Multiuse attribute.
]
public class AuthorAttribute : System.Attribute
{
string Name;
public double Version;
// Default value.
Version = 1.0;
}
class TestAuthorAttribute
{
public static void Test()
{
PrintAuthorInfo(typeof(FirstClass));
PrintAuthorInfo(typeof(SecondClass));
PrintAuthorInfo(typeof(ThirdClass));
}
// Using reflection.
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(t);
// Reflection.
// Displaying output.
foreach (System.Attribute attr in attrs)
{
if (attr is AuthorAttribute a)
{
System.Console.WriteLine($" {a.GetName()}, version
{a.Version:f}");
}
}
}
}
/* Output:
Author information for FirstClass
P. Ackerman, version 1.00
Author information for SecondClass
Author information for ThirdClass
R. Koch, version 2.00
P. Ackerman, version 1.00
*/
See also
System.Reflection
Attribute
Retrieving Information Stored in Attributes
How to create a C/C++ union by using
attributes in C#
Article • 03/15/2023
By using attributes, you can customize how structs are laid out in memory. For example,
you can create what is known as a union in C/C++ by using the
StructLayout(LayoutKind.Explicit) and FieldOffset attributes.
In this code segment, all of the fields of TestUnion start at the same location in memory.
C#
[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
struct TestUnion
{
[System.Runtime.InteropServices.FieldOffset(0)]
public int i;
[System.Runtime.InteropServices.FieldOffset(0)]
public double d;
[System.Runtime.InteropServices.FieldOffset(0)]
public char c;
[System.Runtime.InteropServices.FieldOffset(0)]
public byte b;
}
The following code is another example where fields start at different explicitly set
locations.
C#
[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
struct TestExplicit
{
[System.Runtime.InteropServices.FieldOffset(0)]
public long lg;
[System.Runtime.InteropServices.FieldOffset(0)]
public int i1;
[System.Runtime.InteropServices.FieldOffset(4)]
public int i2;
[System.Runtime.InteropServices.FieldOffset(8)]
public double d;
[System.Runtime.InteropServices.FieldOffset(12)]
public char c;
[System.Runtime.InteropServices.FieldOffset(14)]
public byte b;
}
The two integer fields, i1 and i2 combined, share the same memory locations as lg .
Either lg uses the first 8 bytes, or i1 uses the first 4 bytes and i2 uses the next 4 bytes.
This sort of control over struct layout is useful when using platform invocation.
See also
System.Reflection
Attribute
Attributes
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Generics and Attributes
Article • 03/15/2023
Attributes can be applied to generic types in the same way as nongeneric types.
However, you can apply attributes only on open generic types and closed constructed
generic types, not on partially constructed generic types. An open generic type is one
where none of the type arguments are specified, such as Dictionary<TKey, TValue> A
closed constructed generic type specifies all type arguments, such as Dictionary<string,
object> . A partially constructed generic type specifies some, but not all, type arguments.
An example is Dictionary<string, TValue> .
C#
C#
[CustomAttribute(info = typeof(GenericClass1<>))]
class ClassA { }
Specify multiple type parameters using the appropriate number of commas. In this
example, GenericClass2 has two type parameters:
C#
[CustomAttribute(info = typeof(GenericClass2<,>))]
class ClassB { }
C#
public class GenericClass3<T, U, V> { }
C#
C#
To obtain information about a generic type or type parameter at run time, you can use
the methods of System.Reflection. For more information, see Generics and Reflection.
See also
Generics
Attributes
How to query an assembly's metadata
with Reflection (LINQ)
Article • 03/15/2023
You use the .NET reflection APIs to examine the metadata in a .NET assembly and create
collections of types, type members, and parameters that are in that assembly. Because
these collections support the generic IEnumerable<T> interface, they can be queried by
using LINQ.
The following example shows how LINQ can be used with reflection to retrieve specific
metadata about methods that match a specified search criterion. In this case, the query
finds the names of all the methods in the assembly that return enumerable types such
as arrays.
C#
typeof(System.Collections.Generic.IEnumerable<>).FullName!) != null
&& method.ReturnType.FullName != "System.String")
group method.ToString() by type.ToString();
The example uses the Assembly.GetTypes method to return an array of types in the
specified assembly. The where filter is applied so that only public types are returned. For
each public type, a subquery is generated by using the MethodInfo array that is
returned from the Type.GetMethods call. These results are filtered to return only those
methods whose return type is an array or else a type that implements IEnumerable<T>.
Finally, these results are grouped by using the type name as a key.
Generics and reflection
Article • 03/15/2023
Because the Common Language Runtime (CLR) has access to generic type information
at run time, you can use reflection to obtain information about generic types in the
same way as for nongeneric types. For more information, see Generics in the Runtime.
For a list of the invariant conditions for terms used in generic reflection, see the
IsGenericType property remarks:
In addition, members of the MethodInfo class enable run-time information for generic
methods. See the IsGenericMethod property remarks for a list of invariant conditions for
terms used to reflect on generic methods:
See also
Generics
Reflection and Generic Types
Generics
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Define and read custom attributes
Article • 03/15/2023
Attributes provide a way of associating information with code in a declarative way. They
can also provide a reusable element that can be applied to various targets. Consider the
ObsoleteAttribute. It can be applied to classes, structs, methods, constructors, and more.
It declares that the element is obsolete. It's then up to the C# compiler to look for this
attribute, and do some action in response.
In this tutorial, you learn how to add attributes to your code, how to create and use your
own attributes, and how to use some attributes that are built into .NET.
Prerequisites
You need to set up your machine to run .NET. You can find the installation instructions
on the .NET Downloads page. You can run this application on Windows, Ubuntu Linux,
macOS, or in a Docker container. You need to install your favorite code editor. The
following descriptions use Visual Studio Code , which is an open-source, cross-
platform editor. However, you can use whatever tools you're comfortable with.
.NET CLI
This command creates bare-bones .NET project files. You run dotnet restore to restore
the dependencies needed to compile this project.
You don't have to run dotnet restore because it's run implicitly by all commands that
require a restore to occur, such as dotnet new , dotnet build , dotnet run , dotnet test ,
dotnet publish , and dotnet pack . To disable implicit restore, use the --no-restore
option.
The dotnet restore command is still useful in certain scenarios where explicitly
restoring makes sense, such as continuous integration builds in Azure DevOps Services
or in build systems that need to explicitly control when the restore occurs.
For information about how to manage NuGet feeds, see the dotnet restore
documentation.
To execute the program, use dotnet run . You should see "Hello, World" output to the
console.
C#
[Obsolete]
public class MyClass
{
}
While the class is called ObsoleteAttribute , it's only necessary to use [Obsolete] in the
code. Most C# code follows this convention. You can use the full name
[ObsoleteAttribute] if you choose.
When marking a class obsolete, it's a good idea to provide some information as to why
it's obsolete, and/or what to use instead. You include a string parameter to the Obsolete
attribute to provide this explanation.
C#
C#
With the preceding code, you can use [MySpecial] (or [MySpecialAttribute] ) as an
attribute elsewhere in the code base.
C#
[MySpecial]
public class SomeOtherClass
{
}
Attributes in the .NET base class library like ObsoleteAttribute trigger certain behaviors
within the compiler. However, any attribute you create acts only as metadata, and
doesn't result in any code within the attribute class being executed. It's up to you to act
on that metadata elsewhere in your code.
There's a 'gotcha' here to watch out for. As mentioned earlier, only certain types can be
passed as arguments when using attributes. However, when creating an attribute type,
the C# compiler doesn't stop you from creating those parameters. In the following
example, you've created an attribute with a constructor that compiles correctly.
C#
C#
[Gotcha(new Foo(), "test")] // does not compile
public class AttributeFail
{
}
The preceding code causes a compiler error like Attribute constructor parameter
'myClass' has type 'Foo', which is not a valid attribute parameter type
Assembly
Class
Constructor
Delegate
Enum
Event
Field
GenericParameter
Interface
Method
Module
Parameter
Property
ReturnValue
Struct
When you create an attribute class, by default, C# allows you to use that attribute on
any of the possible attribute targets. If you want to restrict your attribute to certain
targets, you can do so by using the AttributeUsageAttribute on your attribute class.
That's right, an attribute on an attribute!
C#
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class MyAttributeForClassAndStructOnly : Attribute
{
}
If you attempt to put the above attribute on something that's not a class or a struct, you
get a compiler error like Attribute 'MyAttributeForClassAndStructOnly' is not valid
on this declaration type. It is only valid on 'class, struct' declarations
C#
To find and act on attributes, reflection is needed. Reflection allows you to write code in
C# that examines other code. For instance, you can use Reflection to get information
about a class(add using System.Reflection; at the head of your code):
C#
PublicKeyToken=null
Once you have a TypeInfo object (or a MemberInfo , FieldInfo , or other object), you can
use the GetCustomAttributes method. This method returns a collection of Attribute
objects. You can also use GetCustomAttribute and specify an Attribute type.
C#
var attrs = typeInfo.GetCustomAttributes();
foreach(var attr in attrs)
Console.WriteLine("Attribute on MyClass: " + attr.GetType().Name);
It's important to note that these Attribute objects are instantiated lazily. That is, they
aren't be instantiated until you use GetCustomAttribute or GetCustomAttributes . They're
also instantiated each time. Calling GetCustomAttributes twice in a row returns two
different instances of ObsoleteAttribute .
Here are a few notable attributes built into the .NET Core base class libraries:
[Obsolete] . This one was used in the above examples, and it lives in the System
namespace. It's useful to provide declarative documentation about a changing
code base. A message can be provided in the form of a string, and another
boolean parameter can be used to escalate from a compiler warning to a compiler
error.
[Conditional] . This attribute is in the System.Diagnostics namespace. This
attribute can be applied to methods (or attribute classes). You must pass a string to
the constructor. If that string doesn't match a #define directive, then the C#
compiler removes any calls to that method (but not the method itself). Typically
you use this technique for debugging (diagnostics) purposes.
[CallerMemberName] . This attribute can be used on parameters, and lives in the
System.Runtime.CompilerServices namespace. CallerMemberName is an attribute
that is used to inject the name of the method that is calling another method. It's a
way to eliminate 'magic strings' when implementing INotifyPropertyChanged in
various UI frameworks. As an example:
C#
public class MyUIClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
In the above code, you don't have to have a literal "Name" string. Using
CallerMemberName prevents typo-related bugs and also makes for smoother
You can define an implementation when you declare a member of an interface. The
most common scenario is to safely add members to an interface already released and
used by innumerable clients.
Prerequisites
You need to set up your machine to run .NET, including the C# compiler. The C#
compiler is available with Visual Studio 2022 or the .NET SDK .
Scenario overview
This tutorial starts with version 1 of a customer relationship library. You can get the
starter application on our samples repo on GitHub . The company that built this library
intended customers with existing applications to adopt their library. They provided
minimal interface definitions for users of their library to implement. Here's the interface
definition for a customer:
C#
From those interfaces, the team could build a library for their users to create a better
experience for their customers. Their goal was to create a deeper relationship with
existing customers and improve their relationships with new customers.
Now, it's time to upgrade the library for the next release. One of the requested features
enables a loyalty discount for customers that have lots of orders. This new loyalty
discount gets applied whenever a customer makes an order. The specific discount is a
property of each individual customer. Each implementation of ICustomer can set
different rules for the loyalty discount.
The most natural way to add this functionality is to enhance the ICustomer interface
with a method to apply any loyalty discount. This design suggestion caused concern
among experienced developers: "Interfaces are immutable once they've been released!
Don't make a breaking change!" You should use default interface implementations for
upgrading interfaces. The library authors can add new members to the interface and
provide a default implementation for those members.
The upgrade should provide the functionality to set two properties: the number of
orders needed to be eligible for the discount, and the percentage of the discount. These
features make it a perfect scenario for default interface methods. You can add a method
to the ICustomer interface, and provide the most likely implementation. All existing, and
any new implementations can use the default implementation, or provide their own.
First, add the new method to the interface, including the body of the method:
C#
// Version 1:
public decimal ComputeLoyaltyDiscount()
{
DateTime TwoYearsAgo = DateTime.Now.AddYears(-2);
if ((DateJoined < TwoYearsAgo) && (PreviousOrders.Count() > 10))
{
return 0.10m;
}
return 0;
}
C#
C#
Provide parameterization
The default implementation is too restrictive. Many consumers of this system may
choose different thresholds for number of purchases, a different length of membership,
or a different percentage discount. You can provide a better upgrade experience for
more customers by providing a way to set those parameters. Let's add a static method
that sets those three parameters controlling the default implementation:
C#
// Version 2:
public static void SetLoyaltyThresholds(
TimeSpan ago,
int minimumOrders = 10,
decimal percentageDiscount = 0.10m)
{
length = ago;
orderCount = minimumOrders;
discountPercent = percentageDiscount;
}
private static TimeSpan length = new TimeSpan(365 * 2, 0,0,0); // two years
private static int orderCount = 10;
private static decimal discountPercent = 0.10m;
There are many new language capabilities shown in that small code fragment. Interfaces
can now include static members, including fields and methods. Different access
modifiers are also enabled. The other fields are private, the new method is public. Any of
the modifiers are allowed on interface members.
Applications that use the general formula for computing the loyalty discount, but
different parameters, don't need to provide a custom implementation; they can set the
arguments through a static method. For example, the following code sets a "customer
appreciation" that rewards any customer with more than one month's membership:
C#
Consider a startup that wants to attract new customers. They offer a 50% discount off a
new customer's first order. Otherwise, existing customers get the standard discount. The
library author needs to move the default implementation into a protected static
method so that any class implementing this interface can reuse the code in their
implementation. The default implementation of the interface member calls this shared
method as well:
C#
In an implementation of a class that implements this interface, the override can call the
static helper method, and extend that logic to provide the "new customer" discount:
C#
You can see the entire finished code in our samples repo on GitHub . You can get the
starter application on our samples repo on GitHub .
These new features mean that interfaces can be updated safely when there's a
reasonable default implementation for those new members. Carefully design interfaces
to express single functional ideas implemented by multiple classes. That makes it easier
to upgrade those interface definitions when new requirements are discovered for that
same functional idea.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Tutorial: Mix functionality in when
creating classes using interfaces with
default interface methods
Article • 07/31/2024
You can define an implementation when you declare a member of an interface. This
feature provides new capabilities where you can define default implementations for
features declared in interfaces. Classes can pick when to override functionality, when to
use the default functionality, and when not to declare support for discrete features.
Prerequisites
You need to set up your machine to run .NET, including the C# compiler. The C#
compiler is available with Visual Studio 2022 , or the .NET SDK .
Extension methods are resolved at compile time, using the declared type of the variable.
Classes that implement the interface can provide a better implementation for any
extension method. Variable declarations must match the implementing type to enable
the compiler to choose that implementation. When the compile-time type matches the
interface, method calls resolve to the extension method. Another concern with extension
methods is that those methods are accessible wherever the class containing the
extension methods is accessible. Classes can't declare if they should or shouldn't provide
features declared in extension methods.
You can declare the default implementations as interface methods. Then, every class
automatically uses the default implementation. Any class that can provide a better
implementation can override the interface method definition with a better algorithm. In
one sense, this technique sounds similar to how you could use extension methods.
In this article, you learn how default interface implementations enable new scenarios.
Some of these extended capabilities could be emulated in devices that support the
minimal set. That indicates providing a default implementation. For those devices that
have more capabilities built in, the device software would use the native capabilities. For
other lights, they could choose to implement the interface and use the default
implementation.
Default interface members provide a better solution for this scenario than extension
methods. Class authors can control which interfaces they choose to implement. Those
interfaces they choose are available as methods. In addition, because default interface
methods are virtual by default, the method dispatch always chooses the implementation
in the class.
Create interfaces
Start by creating the interface that defines the behavior for all lights:
C#
C#
In this tutorial, the code doesn't drive IoT devices, but emulates those activities by
writing messages to the console. You can explore the code without automating your
house.
Next, let's define the interface for a light that can automatically turn off after a timeout:
C#
You could add a basic implementation to the overhead light, but a better solution is to
modify this interface definition to provide a virtual default implementation:
C#
C#
A different light type might support a more sophisticated protocol. It can provide its
own implementation for TurnOnFor , as shown in the following code:
C#
The default implementation enables any light to blink. The overhead light can add both
timer and blink capabilities using the default implementation:
C#
A new light type, the LEDLight supports both the timer function and the blink function
directly. This light style implements both the ITimerLight and IBlinkingLight
interfaces, and overrides the Blink method:
C#
C#
The HalogenLight you created earlier doesn't support blinking. So, don't add the
IBlinkingLight to the list of its supported interfaces.
C#
private static async Task TestLightCapabilities(ILight light)
{
// Perform basic tests:
light.SwitchOn();
Console.WriteLine($"\tAfter switching on, the light is {(light.IsOn() ?
"on" : "off")}");
light.SwitchOff();
Console.WriteLine($"\tAfter switching off, the light is {(light.IsOn() ?
"on" : "off")}");
The following code in your Main method creates each light type in sequence and tests
that light:
C#
C#
C#
These changes compile cleanly, even though the ExtraFancyLight declares support for
the ILight interface and both derived interfaces, ITimerLight and IBlinkingLight .
There's only one "closest" implementation declared in the ILight interface. Any class
that declared an override would become the one "closest" implementation. You saw
examples in the preceding classes that overrode the members of other derived
interfaces.
Avoid overriding the same method in multiple derived interfaces. Doing so creates an
ambiguous method call whenever a class implements both derived interfaces. The
compiler can't pick a single better method so it issues an error. For example, if both the
IBlinkingLight and ITimerLight implemented an override of PowerStatus , the
OverheadLight would need to provide a more specific override. Otherwise, the compiler
can't pick between the implementations in the two derived interfaces. This situation is
shown in the following diagram:
« interface »
ILight
PowerStatus
« interface » « interface »
IBlinkingLight ITimerLight
PowerStatus PowerStatus
OverheadLight
the ambiguity.
You can usually avoid this situation by keeping interface definitions small and focused
on one feature. In this scenario, each capability of a light is its own interface; only classes
inherit multiple interfaces.
This sample shows one scenario where you can define discrete features that can be
mixed into classes. You declare any set of supported functionality by declaring which
interfaces a class supports. The use of virtual default interface methods enables classes
to use or define a different implementation for any or all the interface methods. This
language capability provides new ways to model the real-world systems you're building.
Default interface methods provide a clearer way to express related classes that might
mix and match different features using virtual implementations of those capabilities.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Expression Trees
Article • 05/29/2024
Expression trees represent code in a tree-like data structure, where each node is an
expression, for example, a method call or a binary operation such as x < y .
If you used LINQ, you have experience with a rich library where the Func types are part
of the API set. (If you aren't familiar with LINQ, you probably want to read the LINQ
tutorial and the article about lambda expressions before this one.) Expression Trees
provide richer interaction with the arguments that are functions.
You write function arguments, typically using Lambda Expressions, when you create
LINQ queries. In a typical LINQ query, those function arguments are transformed into a
delegate the compiler creates.
You already write code that uses Expression trees. Entity Framework's LINQ APIs accept
Expression trees as the arguments for the LINQ Query Expression Pattern. That enables
Entity Framework to translate the query you wrote in C# into SQL that executes in the
database engine. Another example is Moq , which is a popular mocking framework for
.NET.
When you want to have a richer interaction, you need to use Expression Trees. Expression
Trees represent code as a structure that you examine, modify, or execute. These tools
give you the power to manipulate code during run time. You write code that examines
running algorithms, or injects new capabilities. In more advanced scenarios, you modify
running algorithms and even translate C# expressions into another form for execution in
another environment.
You compile and run code represented by expression trees. Building and running
expression trees enables dynamic modification of executable code, the execution of
LINQ queries in various databases, and the creation of dynamic queries. For more
information about expression trees in LINQ, see How to use expression trees to build
dynamic queries.
Expression trees are also used in the dynamic language runtime (DLR) to provide
interoperability between dynamic languages and .NET and to enable compiler writers to
emit expression trees instead of Microsoft intermediate language (CIL). For more
information about the DLR, see Dynamic Language Runtime Overview.
You can have the C# or Visual Basic compiler create an expression tree for you based on
an anonymous lambda expression, or you can create expression trees manually by using
the System.Linq.Expressions namespace.
When a lambda expression is assigned to a variable of type Expression<TDelegate>, the
compiler emits code to build an expression tree that represents the lambda expression.
The following code examples demonstrate how to have the C# compiler create an
expression tree that represents the lambda expression num => num < 5 .
C#
You create expression trees in your code. You build the tree by creating each node and
attaching the nodes into a tree structure. You learn how to create expressions in the
article on building expression trees.
Expression trees are immutable. If you want to modify an expression tree, you must
construct a new expression tree by copying the existing one and replacing nodes in it.
You use an expression tree visitor to traverse the existing expression tree. For more
information, see the article on translating expression trees.
Once you build an expression tree, you execute the code represented by the expression
tree.
Limitations
The C# compiler generates expression trees only from expression lambdas (or single-
line lambdas). It can't parse statement lambdas (or multi-line lambdas). For more
information about lambda expressions in C#, see Lambda Expressions.
There are some newer C# language elements that don't translate well into expression
trees. Expression trees can't contain await expressions, or async lambda expressions.
Many of the features added in C# 6 and later don't appear exactly as written in
expression trees. Instead, newer features are exposed in expression trees in the
equivalent, earlier syntax, where possible. Other constructs aren't available. It means that
code that interprets expression trees works the same when new language features are
introduced. However, even with these limitations, expression trees do enable you to
create dynamic algorithms that rely on interpreting and modifying code that is
represented as a data structure. It enables rich libraries such as Entity Framework to
accomplish what they do.
Expression trees won't support new expression node types. It would be a breaking
change for all libraries interpreting expression trees to introduce new node types. The
following list includes most C# language elements that can't be used:
Conditional methods removed from the output
base access
Method group expressions, including address-of (&) a method group, and
anonymous method expressions
References to local functions
Statements, including assignment ( = ) and statement bodied expressions
Partial methods with only a defining declaration
Unsafe pointer operations
dynamic operations
Coalescing operators with null or default literal left side, null coalescing
assignment, and the null propagating operator (?.)
Multi-dimensional array initializers, indexed properties, and dictionary initializers
Collection expressions
throw expressions
Accessing static virtual or abstract interface members
Lambda expressions that have attributes
Interpolated strings
UTF-8 string conversions or UTF-8 string literals
Method invocations using variable arguments, named arguments, or optional
arguments
Expressions using System.Index or System.Range, index "from end" (^) operator or
range expressions (..)
async lambda expressions or await expressions, including await foreach and await
using
Tuple literals, tuple conversions, tuple == or !=, or with expressions
Discards (_), deconstructing assignment, pattern matching is operator, or the
pattern matching switch expression
COM call with ref omitted on the arguments
ref, in or out parameters, ref return values, out arguments, or any values of ref
struct type
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Expression trees - data that defines
code
Article • 03/09/2023
An Expression Tree is a data structure that defines code. Expression trees are based on
the same structures that a compiler uses to analyze code and generate the compiled
output. As you read this article, you notice quite a bit of similarity between Expression
Trees and the types used in the Roslyn APIs to build Analyzers and CodeFixes .
(Analyzers and CodeFixes are NuGet packages that perform static analysis on code and
suggest potential fixes for a developer.) The concepts are similar, and the end result is a
data structure that allows examination of the source code in a meaningful way.
However, Expression Trees are based on a different set of classes and APIs than the
Roslyn APIs. Here's a line of code:
C#
var sum = 1 + 2;
If you analyze the preceding code as an expression tree, the tree contains several nodes.
The outermost node is a variable declaration statement with assignment ( var sum = 1 +
2; ) That outermost node contains several child nodes: a variable declaration, an
assignment operator, and an expression representing the right hand side of the equals
sign. That expression is further subdivided into expressions that represent the addition
operation, and left and right operands of the addition.
Let's drill down a bit more into the expressions that make up the right side of the equals
sign. The expression is 1 + 2 , a binary expression. More specifically, it's a binary
addition expression. A binary addition expression has two children, representing the left
and right nodes of the addition expression. Here, both nodes are constant expressions:
The left operand is the value 1 , and the right operand is the value 2 .
Visually, the entire statement is a tree: You could start at the root node, and travel to
each node in the tree to see the code that makes up the statement:
The preceding tree may look complicated, but it's very powerful. Following the same
process, you decompose much more complicated expressions. Consider this expression:
C#
there are constant arguments of different types. And finally, there's a binary addition
operator. Depending on the return type of SecretSauceFunction() or
MoreSecretSauce() , that binary addition operator may be a method call to an overridden
addition operator, resolving to a static method call to the binary addition operator
defined for a class.
Despite this perceived complexity, the preceding expression creates a tree structure
navigated as easily as the first sample. You keep traversing child nodes to find leaf
nodes in the expression. Parent nodes have references to their children, and each node
has a property that describes what kind of node it is.
The structure of an expression tree is very consistent. Once you've learned the basics,
you understand even the most complex code when it's represented as an expression
tree. The elegance in the data structure explains how the C# compiler analyzes the most
complex C# programs and creates proper output from that complicated source code.
Once you become familiar with the structure of expression trees, you find that
knowledge you've gained quickly enables you to work with many more advanced
scenarios. There's incredible power to expression trees.
The APIs for Expression Trees enable you to create trees that represent almost any valid
code construct. However, to keep things as simple as possible, some C# idioms can't be
created in an expression tree. One example is asynchronous expressions (using the
async and await keywords). If your needs require asynchronous algorithms, you would
need to manipulate the Task objects directly, rather than rely on the compiler support.
Another is in creating loops. Typically, you create these loops by using for , foreach ,
while or do loops. As you see later in this series, the APIs for expression trees support a
single loop expression, with break and continue expressions that control repeating the
loop.
The one thing you can't do is modify an expression tree. Expression Trees are immutable
data structures. If you want to mutate (change) an expression tree, you must create a
new tree that is a copy of the original, but with your desired changes.
.NET Runtime support for expression
trees
Article • 03/09/2023
There's a large list of classes in the .NET runtime that work with Expression Trees. You
can see the full list at System.Linq.Expressions. Rather than enumerate the full list, let's
understand how the runtime classes have been designed.
In language design, an expression is a body of code that evaluates and returns a value.
Expressions may be simple: the constant expression 1 returns the constant value of 1.
They may be more complicated: The expression (-B + Math.Sqrt(B*B - 4 * A * C)) /
(2 * A) returns one root for a quadratic equation (in the case where the equation has a
solution).
For example, this code prints the name of a variable for a variable access expression. The
following code shows the practice of checking the node type, then casting to a variable
access expression and then checking the properties of the specific expression type:
C#
C#
You can see from this simple example that many types are involved in creating and
working with expression trees. That complexity is necessary to provide the capabilities of
the rich vocabulary provided by the C# language.
You find more as you look at each of those three areas. Invariably, you find what you
need when you start with one of those three steps.
Execute expression trees
Article • 03/09/2023
An expression tree is a data structure that represents some code. It isn't compiled and
executable code. If you want to execute the .NET code represented by an expression
tree, you must convert it into executable IL instructions. Executing an expression tree
may return a value, or it may just perform an action such as calling a method.
Only expression trees that represent lambda expressions can be executed. Expression
trees that represent lambda expressions are of type LambdaExpression or
Expression<TDelegate>. To execute these expression trees, call the Compile method to
create an executable delegate, and then invoke the delegate.
7 Note
If the type of the delegate is not known, that is, the lambda expression is of type
LambdaExpression and not Expression<TDelegate>, call the DynamicInvoke
method on the delegate instead of invoking it directly.
If an expression tree doesn't represent a lambda expression, you can create a new
lambda expression that has the original expression tree as its body, by calling the
Lambda<TDelegate>(Expression, IEnumerable<ParameterExpression>) method. Then,
you can execute the lambda expression as described earlier in this section.
In most cases, a simple mapping between an expression and its corresponding delegate
exists. For example, an expression tree represented by Expression<Func<int>> would be
converted to a delegate of the type Func<int> . For a lambda expression with any return
type and argument list, there exists a delegate type that is the target type for the
executable code represented by that lambda expression.
) Important
You would convert an expression into a delegate using the following code:
C#
The following code example demonstrates the concrete types used when you compile
and execute an expression tree.
C#
Expression<Func<int, bool>> expr = num => num < 5;
// Prints True.
The following code example demonstrates how to execute an expression tree that
represents raising a number to a power by creating a lambda expression and executing
it. The result, which represents the number raised to the power, is displayed.
C#
You invoke that delegate by calling func() , which executes the code.
That delegate represents the code in the expression tree. You can retain the handle to
that delegate and invoke it later. You don't need to compile the expression tree each
time you want to execute the code it represents. (Remember that expression trees are
immutable, and compiling the same expression tree later creates a delegate that
executes the same code.)
U Caution
Caveats
Compiling a lambda expression to a delegate and invoking that delegate is one of the
simplest operations you can perform with an expression tree. However, even with this
simple operation, there are caveats you must be aware of.
Lambda Expressions create closures over any local variables that are referenced in the
expression. You must guarantee that any variables that would be part of the delegate
are usable at the location where you call Compile , and when you execute the resulting
delegate. The compiler ensures that variables are in scope. However, if your expression
accesses a variable that implements IDisposable , it's possible that your code might
dispose of the object while it's still held by the expression tree.
For example, this code works fine, because int doesn't implement IDisposable :
C#
The delegate has captured a reference to the local variable constant . That variable is
accessed at any time later, when the function returned by CreateBoundFunc executes.
However, consider the following (rather contrived) class that implements
System.IDisposable:
C#
C#
The delegate returned from this method has closed over the constant object, which has
been disposed of. (It's been disposed, because it was declared in a using statement.)
Now, when you execute the delegate returned from this method, you have an
ObjectDisposedException thrown at the point of execution.
It does seem strange to have a runtime error representing a compile-time construct, but
that's the world you enter when you work with expression trees.
There are numerous permutations of this problem, so it's hard to offer general guidance
to avoid it. Be careful about accessing local variables when defining expressions, and be
careful about accessing state in the current object (represented by this ) when creating
an expression tree returned via a public API.
The code in your expression may reference methods or properties in other assemblies.
That assembly must be accessible when the expression is defined, when it's compiled,
and when the resulting delegate is invoked. You're met with a
ReferencedAssemblyNotFoundException in cases where it isn't present.
Summary
Expression Trees that represent lambda expressions can be compiled to create a
delegate that you can execute. Expression trees provide one mechanism to execute the
code represented by an expression tree.
The Expression Tree does represent the code that would execute for any given construct
you create. As long as the environment where you compile and execute the code
matches the environment where you create the expression, everything works as
expected. When that doesn't happen, the errors are predictable, and they're caught in
your first tests of any code using the expression trees.
Interpret expressions
Article • 03/09/2023
The following code example demonstrates how the expression tree that represents the
lambda expression num => num < 5 can be decomposed into its parts.
C#
Now, let's write some code to examine the structure of an expression tree. Every node in
an expression tree is an object of a class that is derived from Expression .
That design makes visiting all the nodes in an expression tree a relatively straightforward
recursive operation. The general strategy is to start at the root node and determine what
kind of node it is.
If the node type has children, recursively visit the children. At each child node, repeat the
process used at the root node: determine the type, and if the type has children, visit
each of the children.
C#
var constant = Expression.Constant(24, typeof(int));
Output
Now, let's write the code that would examine this expression and write out some
important properties about it.
Addition expression
Let's start with the addition sample from the introduction to this section.
C#
7 Note
Don't use var to declare this expression tree, because the natural type of the
delegate is Func<int> , not Expression<Func<int>> .
The root node is a LambdaExpression . In order to get the interesting code on the right-
hand side of the => operator, you need to find one of the children of the
LambdaExpression . You do that with all the expressions in this section. The parent node
does help us find the return type of the LambdaExpression .
To examine each node in this expression, you need to recursively visit many nodes.
Here's a simple first implementation:
C#
Output
You notice much repetition in the preceding code sample. Let's clean that up and build a
more general purpose expression node visitor. That's going to require us to write a
recursive algorithm. Any node could be of a type that might have children. Any node
that has children requires us to visit those children and determine what that node is.
Here's the cleaned up version that utilizes recursion to visit the addition operations:
C#
using System.Linq.Expressions;
namespace Visitors;
// Base Visitor class:
public abstract class Visitor
{
private readonly Expression node;
// Lambda Visitor
public class LambdaVisitor : Visitor
{
private readonly LambdaExpression node;
public LambdaVisitor(LambdaExpression node) : base(node) => this.node =
node;
// Parameter visitor:
public class ParameterVisitor : Visitor
{
private readonly ParameterExpression node;
public ParameterVisitor(ParameterExpression node) : base(node)
{
this.node = node;
}
// Constant visitor:
public class ConstantVisitor : Visitor
{
private readonly ConstantExpression node;
public ConstantVisitor(ConstantExpression node) : base(node) =>
this.node = node;
This algorithm is the basis of an algorithm that visits any arbitrary LambdaExpression . The
code you created only looks for a small sample of the possible sets of expression tree
nodes that it may encounter. However, you can still learn quite a bit from what it
produces. (The default case in the Visitor.CreateFromExpression method prints a
message to the error console when a new node type is encountered. That way, you
know to add a new expression type.)
When you run this visitor on the preceding addition expression, you get the following
output:
Output
Now that you've built a more general visitor implementation, you can visit and process
many more different types of expressions.
C#
Before you run these examples on the visitor algorithm, try a thought exercise to work
out what the output might be. Remember that the + operator is a binary operator: it
must have two children, representing the left and right operands. There are several
possible ways to construct a tree that could be correct:
C#
You can see the separation into two possible answers to highlight the most promising.
The first represents right associative expressions. The second represent left associative
expressions. The advantage of both of those two formats is that the format scales to any
arbitrary number of addition expressions.
If you do run this expression through the visitor, you see this output, verifying that the
simple addition expression is left associative.
In order to run this sample, and see the full expression tree, you make one change to
the source expression tree. When the expression tree contains all constants, the
resulting tree simply contains the constant value of 10 . The compiler performs all the
addition and reduces the expression to its simplest form. Simply adding one variable in
the expression is sufficient to see the original tree:
C#
Create a visitor for this sum and run the visitor you see this output:
Output
You can run any of the other samples through the visitor code and see what tree it
represents. Here's an example of the preceding sum3 expression (with an additional
parameter to prevent the compiler from computing the constant):
C#
Output
Notice that the parentheses aren't part of the output. There are no nodes in the
expression tree that represent the parentheses in the input expression. The structure of
the expression tree contains all the information necessary to communicate the
precedence.
C#
This code represents one possible implementation for the mathematical factorial
function. The way you've written this code highlights two limitations of building
expression trees by assigning lambda expressions to Expressions. First, statement
lambdas aren't allowed. That means you can't use loops, blocks, if / else statements, and
other control structures common in C#. You're limited to using expressions. Second, you
can't recursively call the same expression. You could if it were already a delegate, but
you can't call it in its expression tree form. In the section on building expression trees,
you learn techniques to overcome these limitations.
One way to modify the visitor algorithm is to keep executing it, and write the node type
every time you reach your default clause. After a few iterations, you've see each of the
potential nodes. Then, you have all you need. The result would be something like this:
C#
C#
Output
First, the visitors only handle constants that are integers. Constant values could be any
other numeric type, and the C# language supports conversions and promotions
between those types. A more robust version of this code would mirror all those
capabilities.
Even the last example recognizes a subset of the possible node types. You can still feed
it many expressions that cause it to fail. A full implementation is included in .NET
Standard under the name ExpressionVisitor and can handle all the possible node types.
Finally, the library used in this article was built for demonstration and learning. It's not
optimized. It makes the structures clear, and to highlight the techniques used to visit the
nodes and analyze what's there.
Even with those limitations, you should be well on your way to writing algorithms that
read and understand expression trees.
Build expression trees
Article • 03/09/2023
The C# compiler created all the expression trees you've seen so far. You created a
lambda expression assigned to a variable typed as an Expression<Func<T>> or some
similar type. For many scenarios, you build an expression in memory at run time.
Expression trees are immutable. Being immutable means that you must build the tree
from the leaves up to the root. The APIs you use to build expression trees reflect this
fact: The methods you use to build a node take all its children as arguments. Let's walk
through a few examples to show you the techniques.
Create nodes
You start with the addition expression you've been working with throughout these
sections:
C#
To construct that expression tree, you first construct the leaf nodes. The leaf nodes are
constants. Use the Constant method to create the nodes:
C#
C#
Once you've built the addition expression, you create the lambda expression:
C#
For expressions like this one, you may combine all the calls into a single statement:
C#
Build a tree
The previous section showed the basics of building an expression tree in memory. More
complex trees generally mean more node types, and more nodes in the tree. Let's run
through one more example and show two more node types that you typically build
when you create expression trees: the argument nodes, and method call nodes. Let's
build an expression tree to create this expression:
C#
C#
Creating the multiplication and addition expressions follows the pattern you've already
seen:
C#
Next, you need to create a method call expression for the call to Math.Sqrt .
C#
The GetMethod call could return null if the method isn't found. Most likely that's
because you've misspelled the method name. Otherwise, it could mean the required
assembly isn't loaded. Finally, you put the method call into a lambda expression, and
make sure to define the arguments to the lambda expression:
C#
In this more complicated example, you see a couple more techniques that you often
need to create expression trees.
First, you need to create the objects that represent parameters or local variables before
you use them. Once you've created those objects, you can use them in your expression
tree wherever you need.
C#
The preceding code didn't build the expression tree, but simply the delegate. Using the
Expression class, you can't build statement lambdas. Here's the code that is required to
build the same functionality. There isn't an API for building a while loop, instead you
need to build a loop that contains a conditional test, and a label target to break out of
the loop.
C#
The code to build the expression tree for the factorial function is quite a bit longer,
more complicated, and it's riddled with labels and break statements and other elements
you'd like to avoid in our everyday coding tasks.
For this section, you wrote code to visit every node in this expression tree and write out
information about the nodes that are created in this sample. You can view or download
the sample code at the dotnet/docs GitHub repository. Experiment for yourself by
building and running the samples.
C#
The expression trees API also supports assignments and control flow expressions such
as loops, conditional blocks, and try-catch blocks. By using the API, you can create
expression trees that are more complex than those that can be created from lambda
expressions by the C# compiler. The following example demonstrates how to create an
expression tree that calculates the factorial of a number.
C#
Console.WriteLine(factorial);
// Prints 120.
For more information, see Generating Dynamic Methods with Expression Trees in Visual
Studio 2010 , which also applies to later versions of Visual Studio.
Translate expression trees
Article • 03/09/2023
In this article, you learn how to visit each node in an expression tree while building a
modified copy of that expression tree. You translate expression trees to understand the
algorithms so that it can be translated into another environment. You change the
algorithm that has been created. You might add logging, intercept method calls and
track them, or other purposes.
The code you build to translate an expression tree is an extension of what you've
already seen to visit all the nodes in a tree. When you translate an expression tree, you
visit all the nodes, and while visiting them, build the new tree. The new tree may contain
references to the original nodes, or new nodes that you've placed in the tree.
Let's visit an expression tree, and creating a new tree with some replacement nodes. In
this example, let's replace any constant with a constant that is 10 times larger.
Otherwise, you leave the expression tree intact. Rather than reading the value of the
constant and replacing it with a new constant, you make this replacement by replacing
the constant node with a new node that performs the multiplication.
Here, once you find a constant node, you create a new multiplication node whose
children are the original constant and the constant 10 :
C#
Create a new tree by replacing the original node with the substitute. You verify the
changes by compiling and executing the replaced tree.
C#
var one = Expression.Constant(1, typeof(int));
var two = Expression.Constant(2, typeof(int));
var addition = Expression.Add(one, two);
var sum = ReplaceNodes(addition);
var executableFunc = Expression.Lambda(sum);
Building a new tree is a combination of visiting the nodes in the existing tree, and
creating new nodes and inserting them into the tree. The previous example shows the
importance of expression trees being immutable. Notice that the new tree created in the
preceding code contains a mixture of newly created nodes, and nodes from the existing
tree. Nodes can be used in both trees because the nodes in the existing tree can't be
modified. Reusing nodes results in significant memory efficiencies. The same nodes can
be used throughout a tree, or in multiple expression trees. Since nodes can't be
modified, the same node can be reused whenever it's needed.
C#
There's quite a bit of code here, but the concepts are approachable. This code visits
children in a depth first search. When it encounters a constant node, the visitor returns
the value of the constant. After the visitor has visited both children, those children have
computed the sum computed for that subtree. The addition node can now compute its
sum. Once all the nodes in the expression tree have been visited, the sum has been
computed. You can trace the execution by running the sample in the debugger and
tracing the execution.
Let's make it easier to trace how the nodes are analyzed and how the sum is computed
by traversing the tree. Here's an updated version of the Aggregate method that includes
quite a bit of tracing information:
C#
Output
10
Found Addition Expression
Computing Left node
Found Addition Expression
Computing Left node
Found Constant: 1
Left is: 1
Computing Right node
Found Constant: 2
Right is: 2
Computed sum: 3
Left is: 3
Computing Right node
Found Addition Expression
Computing Left node
Found Constant: 3
Left is: 3
Computing Right node
Found Constant: 4
Right is: 4
Computed sum: 7
Right is: 7
Computed sum: 10
10
Trace the output and follow along in the preceding code. You should be able to work
out how the code visits each node and computes the sum as it goes through the tree
and finds the sum.
Now, let's look at a different run, with the expression given by sum1 :
C#
Output
While the final answer is the same, the tree traversal is different. The nodes are traveled
in a different order, because the tree was constructed with different operations
occurring first.
C#
return base.VisitBinary(b);
}
}
This class inherits the ExpressionVisitor class and is specialized to modify expressions
that represent conditional AND operations. It changes these operations from a
conditional AND to a conditional OR . The class overrides the VisitBinary method of the
base type, because conditional AND expressions are represented as binary expressions.
In the VisitBinary method, if the expression that is passed to it represents a conditional
AND operation, the code constructs a new expression that contains the conditional OR
operator instead of the conditional AND operator. If the expression that is passed to
VisitBinary doesn't represent a conditional AND operation, the method defers to the
base class implementation. The base class methods construct nodes that are like the
expression trees that are passed in, but the nodes have their sub trees replaced with the
expression trees produced recursively by the visitor.
Add a using directive to the file for the System.Linq.Expressions namespace. Add code
to the Main method in the Program.cs file to create an expression tree and pass it to the
method that modifies it.
C#
Console.WriteLine(modifiedExpr);
The code creates an expression that contains a conditional AND operation. It then
creates an instance of the AndAlsoModifier class and passes the expression to the
Modify method of this class. Both the original and the modified expression trees are
Learn more
This sample shows a small subset of the code you would build to traverse and interpret
the algorithms represented by an expression tree. For information on building a general
purpose library that translates expression trees into another language, read this series
by Matt Warren. It goes into great detail on how to translate any of the code you might
find in an expression tree.
You've now seen the true power of expression trees. You examine a set of code, make
any changes you'd like to that code, and execute the changed version. Because the
expression trees are immutable, you create new trees by using the components of
existing trees. Reusing nodes minimizes the amount of memory needed to create
modified expression trees.
Debugging expression trees in Visual
Studio
Article • 03/09/2023
You can analyze the structure and content of expression trees when you debug your
applications. To get a quick overview of the expression tree structure, you can use the
DebugView property, which represents expression trees using a special syntax. DebugView
Since DebugView is a string, you can use the built-in Text Visualizer to view it across
multiple lines, by selecting Text Visualizer from the magnifying glass icon next to the
DebugView label.
Alternatively, you can install and use a custom visualizer for expression trees, such as:
The DebugView property (available only when debugging) provides a string rendering
of expression trees. Most of the syntax is fairly straightforward to understand; the
special cases are described in the following sections.
ParameterExpression
ParameterExpression variable names are displayed with a $ symbol at the beginning.
If a parameter doesn't have a name, it's assigned an automatically generated name, such
as $var1 or $var2 .
C#
ConstantExpression
For ConstantExpression objects that represent integer values, strings, and null , the
value of the constant is displayed.
For numeric types that have standard suffixes as C# literals, the suffix is added to the
value. The following table shows the suffixes associated with various numeric types.
System.UInt32 uint U
System.Int64 long L
System.UInt64 ulong UL
Type Keyword Suffix
System.Double double D
System.Single float F
System.Decimal decimal M
C#
BlockExpression
If the type of a BlockExpression object differs from the type of the last expression in the
block, the type is displayed within angle brackets ( < and > ). Otherwise, the type of the
BlockExpression object isn't displayed.
C#
LambdaExpression
LambdaExpression objects are displayed together with their delegate types.
C#
LabelExpression
If you specify a default value for the LabelExpression object, this value is displayed
before the LabelTarget object.
The .Label token indicates the start of the label. The .LabelTarget token indicates the
destination of the target to jump to.
If a label doesn't have a name, it's assigned an automatically generated name, such as
#Label1 or #Label2 .
C#
Checked Operators
Checked operators are displayed with the # symbol in front of the operator. For
example, the checked addition operator is displayed as #+ .
C#
This article provides supplementary remarks to the reference documentation for this
API.
The Add method returns a BinaryExpression that has the Method property set to the
implementing method. The Type property is set to the type of the node. If the node is
lifted, the IsLifted and IsLiftedToNull properties are both true . Otherwise, they are
false . The Conversion property is null .
The following information describes the implementing method, the node type, and
whether a node is lifted.
Implementing method
The following rules determine the selected implementing method for the operation:
If the Type property of either left or right represents a user-defined type that
overloads the addition operator, the MethodInfo that represents that method is
the implementing method.
Otherwise, if left .Type and right .Type are numeric types, the implementing
method is null .
If left .Type and right .Type are assignable to the corresponding argument types
of the implementing method, the node is not lifted. The type of the node is the
return type of the implementing method.
If the following two conditions are satisfied, the node is lifted and the type of the
node is the nullable type that corresponds to the return type of the implementing
method:
left .Type and right .Type are both value types of which at least one is nullable
If left .Type and right .Type are both non-nullable, the node is not lifted. The type
of the node is the result type of the predefined addition operator.
If left .Type and right .Type are both nullable, the node is lifted. The type of the
node is the nullable type that corresponds to the result type of the predefined
addition operator.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
System.Linq.Expressions.BinaryExpressio
n class
Article • 01/09/2024
This article provides supplementary remarks to the reference documentation for this
API.
The following tables summarize the factory methods that can be used to create a
BinaryExpression that has a specific node type, represented by the NodeType property.
Each table contains information for a specific class of operations such as arithmetic or
bitwise.
Add Add
AddChecked AddChecked
Divide Divide
Modulo Modulo
Multiply Multiply
MultiplyChecked MultiplyChecked
Power Power
Subtract Subtract
SubtractChecked SubtractChecked
Bitwise operations
ノ Expand table
Node Type Factory Method
And And
Or Or
ExclusiveOr ExclusiveOr
Shift operations
ノ Expand table
LeftShift LeftShift
RightShift RightShift
AndAlso AndAlso
OrElse OrElse
Comparison operations
ノ Expand table
Equal Equal
NotEqual NotEqual
GreaterThanOrEqual GreaterThanOrEqual
GreaterThan GreaterThan
LessThan LessThan
Node Type Factory Method
LessThanOrEqual LessThanOrEqual
Coalescing operations
ノ Expand table
Coalesce Coalesce
ArrayIndex ArrayIndex
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Interoperability Overview
Article • 02/25/2023
.NET enables interoperability with unmanaged code through platform invoke services,
the System.Runtime.InteropServices namespace, C++ interoperability, and COM
interoperability (COM interop).
Platform Invoke
Platform invoke is a service that enables managed code to call unmanaged functions
implemented in dynamic link libraries (DLLs), such as the Microsoft Windows API. It
locates and invokes an exported function and marshals its arguments (integers, strings,
arrays, structures, and so on) across the interoperation boundary as needed.
For more information, see Consuming Unmanaged DLL Functions and How to use
platform invoke to play a WAV file.
7 Note
C++ Interop
You can use C++ interop, also known as It Just Works (IJW), to wrap a native C++ class.
C++ interop enables code authored in C# or another .NET language to access it. You
write C++ code to wrap a native DLL or COM component. Unlike other .NET languages,
Visual C++ has interoperability support that enables managed and unmanaged code in
the same application and even in the same file. You then build the C++ code by using
the /clr compiler switch to produce a managed assembly. Finally, you add a reference to
the assembly in your C# project and use the wrapped objects just as you would use
other managed classes.
1. Locate a COM component to use and register it. Use regsvr32.exe to register or
un–register a COM DLL.
2. Add to the project a reference to the COM component or type library. When you
add the reference, Visual Studio uses the Tlbimp.exe (Type Library Importer), which
takes a type library as input, to output a .NET interop assembly. The assembly, also
named a runtime callable wrapper (RCW), contains managed classes and interfaces
that wrap the COM classes and interfaces that are in the type library. Visual Studio
adds to the project a reference to the generated assembly.
3. Create an instance of a class defined in the RCW. Creating an instance of that class
creates an instance of the COM object.
4. Use the object just as you use other managed objects. When the object is
reclaimed by garbage collection, the instance of the COM object is also released
from memory.
For more information, see Exposing COM Components to the .NET Framework.
Exposing C# to COM
COM clients can consume C# types that have been correctly exposed. The basic steps to
expose C# types are as follows:
1. Add interop attributes in the C# project. You can make an assembly COM visible by
modifying C# project properties. For more information, see Assembly Information
Dialog Box.
2. Generate a COM type library and register it for COM usage. You can modify C#
project properties to automatically register the C# assembly for COM interop.
Visual Studio uses the Regasm.exe (Assembly Registration Tool), using the /tlb
command-line switch, which takes a managed assembly as input, to generate a
type library. This type library describes the public types in the assembly and adds
registry entries so that COM clients can create managed classes.
For more information, see Exposing .NET Framework Components to COM and Example
COM Class.
See also
Improving Interop Performance
Introduction to Interoperability between COM and .NET
Introduction to COM Interop in Visual Basic
Marshaling between Managed and Unmanaged Code
Interoperating with Unmanaged Code
Registering Assemblies with COM
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Example COM Class
Article • 02/25/2023
The following code is an example of a class that you would expose as a COM object.
After you place this code in a .cs file added to your project, set the Register for COM
Interop property to True. For more information, see How to: Register a Component for
COM Interop.
Other public members in the class that you don't declare in these interfaces aren't
visible to COM, but they're visible to other .NET objects. To expose properties and
methods to COM, you must declare them on the class interface and mark them with a
DispId attribute, and implement them in the class. The order in which you declare the
members in the interface is the order used for the COM vtable. To expose events from
your class, you must declare them on the events interface and mark them with a DispId
attribute. The class shouldn't implement this interface.
The class implements the class interface; it can implement more than one interface, but
the first implementation is the default class interface. Implement the methods and
properties exposed to COM here. They must be public and must match the declarations
in the class interface. Also, declare the events raised by the class here. They must be
public and must match the declarations in the events interface.
Example
C#
using System.Runtime.InteropServices;
namespace project_name
{
[Guid("EAA4976A-45C3-4BC5-BC0B-E474F4C3C83F")]
public interface ComClass1Interface
{
}
[Guid("7BD20046-DF8C-44A6-8F6B-687FAA26FA71"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ComClass1Events
{
}
[Guid("0D53A3E8-E51A-49C7-944E-E72A2064F938"),
ClassInterface(ClassInterfaceType.None),
ComSourceInterfaces(typeof(ComClass1Events))]
public class ComClass1 : ComClass1Interface
{
}
}
See also
Interoperability
Build Page, Project Designer (C#)
Walkthrough: Office Programming in C#
Article • 03/01/2023
) Important
VSTO (Visual Studio Tools for Office) relies on the .NET Framework. COM add-ins
can also be written with the .NET Framework. Office Add-ins cannot be created with
.NET Core and .NET 5+, the latest versions of .NET. This is because .NET Core/.NET
5+ cannot work together with .NET Framework in the same process and may lead
to add-in load failures. You can continue to use .NET Framework to write VSTO and
COM add-ins for Office. Microsoft will not be updating VSTO or the COM add-in
platform to use .NET Core or .NET 5+. You can take advantage of .NET Core and
.NET 5+, including ASP.NET Core, to create the server side of Office Web Add-ins.
Prerequisites
You must have Microsoft Office Excel and Microsoft Office Word installed on your
computer to complete this walkthrough.
7 Note
Your computer might show different names or locations for some of the Visual
Studio user interface elements in the following instructions. The Visual Studio
edition that you have and the settings that you use determine these elements. For
more information, see Personalizing the IDE.
Add references
1. In Solution Explorer, right-click your project's name and then select Add
Reference. The Add Reference dialog box appears.
2. On the Assemblies tab, select Microsoft.Office.Interop.Excel, version
<version>.0.0.0 (for a key to the Office product version numbers, see Microsoft
Versions ), in the Component Name list, and then hold down the CTRL key and
select Microsoft.Office.Interop.Word, version <version>.0.0.0 . If you don't see
the assemblies, you may need to install them (see How to: Install Office Primary
Interop Assemblies).
3. Select OK.
C#
using System.Collections.Generic;
using Excel = Microsoft.Office.Interop.Excel;
using Word = Microsoft.Office.Interop.Word;
C#
class Account
{
public int ID { get; set; }
public double Balance { get; set; }
}
To create a bankAccounts list that contains two accounts, add the following code to the
ThisAddIn_Startup method in ThisAddIn.cs. The list declarations use collection
initializers.
C#
C#
Optional Arguments.
The Range and Offset properties of the Range object use the indexed properties
feature. This feature enables you to consume these properties from COM types by
using the following typical C# syntax. Indexed properties also enable you to use
the Value property of the Range object, eliminating the need to use the Value2
property. The Value property is indexed, but the index is optional. Optional
arguments and indexed properties work together in the following example.
C#
You can't create indexed properties of your own. The feature only supports consumption
of existing indexed properties.
Add the following code at the end of DisplayInExcel to adjust the column widths to fit
the content.
C#
excelApp.Columns[1].AutoFit();
excelApp.Columns[2].AutoFit();
These additions demonstrate another feature in C#: treating Object values returned
from COM hosts such as Office as if they have type dynamic. COM objects are treated as
dynamic automatically when Embed Interop Types has its default value, True , or,
equivalently, when you reference the assembly with the EmbedInteropTypes compiler
option. For more information about embedding interop types, see procedures "To find
the PIA reference" and "To restore the PIA dependency" later in this article. For more
information about dynamic , see dynamic or Using Type dynamic.
Invoke DisplayInExcel
Add the following code at the end of the ThisAddIn_StartUp method. The call to
DisplayInExcel contains two arguments. The first argument is the name of the list of
C#
To run the program, press F5. An Excel worksheet appears that contains the data from
the accounts.
This code demonstrates several of the features in C#: the ability to omit the ref
keyword in COM programming, named arguments, and optional arguments.The
PasteSpecial method has seven parameters, all of which are optional reference
parameters. Named and optional arguments enable you to designate the parameters
you want to access by name and to send arguments to only those parameters. In this
example, arguments indicate creating a link to the workbook on the Clipboard
(parameter Link ) and displaying that the link in the Word document as an icon
(parameter DisplayAsIcon ). C# also enables you to omit the ref keyword for these
arguments.
list. Because you imported the types your project needs into your assembly, you
aren't required to install references to a PIA. Importing the types into your
assembly makes deployment easier. The PIAs don't have to be present on the
user's computer. An application doesn't require deployment of a specific version of
a PIA. Applications can work with multiple versions of Office, provided that the
necessary APIs exist in all versions. Because deployment of PIAs is no longer
necessary, you can create an application in advanced scenarios that works with
multiple versions of Office, including earlier versions. Your code can't use any APIs
that aren't available in the version of Office you're working with. It isn't always clear
whether a particular API was available in an earlier version. Working with earlier
versions of Office isn't recommended.
6. Close the manifest window and the assembly window.
the Excel and Word PIAs, and the Embed Interop Types property is False, both
assemblies must exist on the end user's computer.
8. In Visual Studio, select Clean Solution on the Build menu to clean up the
completed project.
See also
Auto-Implemented Properties (C#)
Object and Collection Initializers
Visual Studio Tools for Office (VSTO)
Named and Optional Arguments
dynamic
Using Type dynamic
Lambda Expressions (C#)
Walkthrough: Embedding Type Information from Microsoft Office Assemblies in
Visual Studio
Walkthrough: Embedding Types from Managed Assemblies
Walkthrough: Creating Your First VSTO Add-in for Excel
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to use platform invoke to play a
WAV file
Article • 02/25/2023
The following C# code example illustrates how to use platform invoke services to play a
WAV sound file on the Windows operating system.
Example
This example code uses DllImportAttribute to import winmm.dll 's PlaySound method
entry point as Form1 PlaySound() . The example has a simple Windows Form with a
button. Clicking the button opens a standard windows OpenFileDialog dialog box so
that you can open a file to play. When a wave file is selected, it's played by using the
PlaySound() method of the winmm.dll library. For more information about this method,
see Using the PlaySound function with Waveform-Audio Files. Browse and select a file
that has a .wav extension, and then select Open to play the wave file by using platform
invoke. A text box shows the full path of the file selected.
C#
using System.Runtime.InteropServices;
namespace WinSound;
[System.Flags]
public enum PlaySoundFlags : int
{
SND_SYNC = 0x0000,
SND_ASYNC = 0x0001,
SND_NODEFAULT = 0x0002,
SND_LOOP = 0x0008,
SND_NOSTOP = 0x0010,
SND_NOWAIT = 0x00002000,
SND_FILENAME = 0x00020000,
SND_RESOURCE = 0x00040004
}
if (dialog1.ShowDialog() == DialogResult.OK)
{
textBox1.Text = dialog1.FileName;
PlaySound(dialog1.FileName, new System.IntPtr(),
PlaySoundFlags.SND_SYNC);
}
}
The Open Files dialog box is filtered to show only files that have a .wav extension
through the filter settings.
C#
this.button1 = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(192, 40);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(88, 24);
this.button1.TabIndex = 0;
this.button1.Text = "Browse";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(8, 40);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(168, 20);
this.textBox1.TabIndex = 1;
this.textBox1.Text = "File path";
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(5, 13);
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Platform Invoke WinSound C#";
this.ResumeLayout(false);
this.PerformLayout();
See also
A Closer Look at Platform Invoke
Marshaling Data with Platform Invoke
How to use indexed properties in COM
interop programming
Article • 03/01/2023
Indexed properties work together with other features in C#, such as named and optional
arguments, a new type (dynamic), and embedded type information, to enhance
Microsoft Office programming.
) Important
VSTO (Visual Studio Tools for Office) relies on the .NET Framework. COM add-ins
can also be written with the .NET Framework. Office Add-ins cannot be created with
.NET Core and .NET 5+, the latest versions of .NET. This is because .NET Core/.NET
5+ cannot work together with .NET Framework in the same process and may lead
to add-in load failures. You can continue to use .NET Framework to write VSTO and
COM add-ins for Office. Microsoft will not be updating VSTO or the COM add-in
platform to use .NET Core or .NET 5+. You can take advantage of .NET Core and
.NET 5+, including ASP.NET Core, to create the server side of Office Web Add-ins.
In earlier versions of C#, methods are accessible as properties only if the get method
has no parameters and the set method has one and only one value parameter.
However, not all COM properties meet those restrictions. For example, the Excel Range[]
property has a get accessor that requires a parameter for the name of the range. In the
past, because you couldn't access the Range property directly, you had to use the
get_Range method instead, as shown in the following example.
C#
C#
// Visual C# 2010.
var excelApp = new Excel.Application();
// . . .
Excel.Range targetRange = excelApp.Range["A1"];
The previous example also uses the optional arguments feature, which enables you to
omit Type.Missing .
C#
// Visual C# 2010.
targetRange.Value = "Name";
You can't create indexed properties of your own. The feature only supports consumption
of existing indexed properties.
Example
The following code shows a complete example. For more information about how to set
up a project that accesses the Office API, see How to access Office interop objects by
using C# features.
C#
namespace IndexedProperties
{
class Program
{
static void Main(string[] args)
{
CSharp2010();
}
See also
Named and Optional Arguments
dynamic
Using Type dynamic
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to access Office interop objects
Article • 03/01/2023
C# has features that simplify access to Office API objects. The new features include
named and optional arguments, a new type called dynamic , and the ability to pass
arguments to reference parameters in COM methods as if they were value parameters.
In this article, you use the new features to write code that creates and displays a
Microsoft Office Excel worksheet. You write code to add an Office Word document that
contains an icon that is linked to the Excel worksheet.
To complete this walkthrough, you must have Microsoft Office Excel 2007 and Microsoft
Office Word 2007, or later versions, installed on your computer.
7 Note
Your computer might show different names or locations for some of the Visual
Studio user interface elements in the following instructions. The Visual Studio
edition that you have and the settings that you use determine these elements. For
more information, see Personalizing the IDE.
) Important
VSTO (Visual Studio Tools for Office) relies on the .NET Framework. COM add-ins
can also be written with the .NET Framework. Office Add-ins cannot be created with
.NET Core and .NET 5+, the latest versions of .NET. This is because .NET Core/.NET
5+ cannot work together with .NET Framework in the same process and may lead
to add-in load failures. You can continue to use .NET Framework to write VSTO and
COM add-ins for Office. Microsoft will not be updating VSTO or the COM add-in
platform to use .NET Core or .NET 5+. You can take advantage of .NET Core and
.NET 5+, including ASP.NET Core, to create the server side of Office Web Add-ins.
To add references
1. In Solution Explorer, right-click your project's name and then select Add
Reference. The Add Reference dialog box appears.
2. On the Assemblies page, select Microsoft.Office.Interop.Word in the Component
Name list, and then hold down the CTRL key and select
Microsoft.Office.Interop.Excel. If you don't see the assemblies, you may need to
install them. See How to: Install Office Primary Interop Assemblies.
3. Select OK.
C#
C#
C#
C#
Add the following code at the end of DisplayInExcel . The code inserts values into the
first two columns of the first row of the worksheet.
C#
Add the following code at the end of DisplayInExcel . The foreach loop puts the
information from the list of accounts into the first two columns of successive rows of the
worksheet.
C#
var row = 1;
foreach (var acct in accounts)
{
row++;
workSheet.Cells[row, "A"] = acct.ID;
workSheet.Cells[row, "B"] = acct.Balance;
}
Add the following code at the end of DisplayInExcel to adjust the column widths to fit
the content.
C#
workSheet.Columns[1].AutoFit();
workSheet.Columns[2].AutoFit();
C#
((Excel.Range)workSheet.Columns[1]).AutoFit();
((Excel.Range)workSheet.Columns[2]).AutoFit();
C# converts the returned Object to dynamic automatically if the assembly is referenced
by the EmbedInteropTypes compiler option or, equivalently, if the Excel Embed Interop
Types property is true. True is the default value for this property.
C#
Press CTRL+F5. An Excel worksheet appears that contains the data from the two
accounts.
complexity of the method calls to Add and PasteSpecial. These calls incorporate two
other features that simplify calls to COM methods that have reference parameters. First,
you can send arguments to the reference parameters as if they were value parameters.
That is, you can send values directly, without creating a variable for each reference
parameter. The compiler generates temporary variables to hold the argument values,
and discards the variables when you return from the call. Second, you can omit the ref
keyword in the argument list.
The Add method has four reference parameters, all of which are optional. You can omit
arguments for any or all of the parameters if you want to use their default values.
The PasteSpecial method inserts the contents of the Clipboard. The method has seven
reference parameters, all of which are optional. The following code specifies arguments
for two of them: Link , to create a link to the source of the Clipboard contents, and
DisplayAsIcon , to display the link as an icon. You can use named arguments for those
two arguments and omit the others. Although these arguments are reference
parameters, you don't have to use the ref keyword, or to create variables to send in as
arguments. You can send the values directly.
C#
static void CreateIconInWordDoc()
{
var wordApp = new Word.Application();
wordApp.Visible = true;
// The Add method has four reference parameters, all of which are
// optional. Visual C# allows you to omit arguments for them if
// the default values are what you want.
wordApp.Documents.Add();
C#
Add the following statement at the end of DisplayInExcel . The Copy method adds the
worksheet to the Clipboard.
C#
// Put the spreadsheet contents on the clipboard. The Copy method has one
// optional parameter for specifying a destination. Because no argument
// is sent, the destination is the Clipboard.
workSheet.Range["A1:B3"].Copy();
Press CTRL+F5. A Word document appears that contains an icon. Double-click the icon
to bring the worksheet to the foreground.
In addition, programming is easier because the dynamic type represents the required
and returned types declared in COM methods. Variables that have type dynamic aren't
evaluated until run time, which eliminates the need for explicit casting. For more
information, see Using Type dynamic.
Embedding type information instead of using PIAs is default behavior. Because of that
default, several of the previous examples are simplified. You don't need any explicit
casting. For example, the declaration of worksheet in DisplayInExcel is written as
Excel._Worksheet workSheet = excelApp.ActiveSheet rather than Excel._Worksheet
method also would require explicit casting without the default, because
ExcelApp.Columns[1] returns an Object , and AutoFit is an Excel method. The following
C#
((Excel.Range)workSheet.Columns[1]).AutoFit();
((Excel.Range)workSheet.Columns[2]).AutoFit();
To change the default and use PIAs instead of embedding type information, expand the
References node in Solution Explorer, and then select Microsoft.Office.Interop.Excel or
Microsoft.Office.Interop.Word. If you can't see the Properties window, press F4. Find
Embed Interop Types in the list of properties, and change its value to False.
Equivalently, you can compile by using the References compiler option instead of
EmbedInteropTypes at a command prompt.
C#
The AutoFormat method has seven value parameters, all of which are optional. Named
and optional arguments enable you to provide arguments for none, some, or all of
them. In the previous statement, you supply an argument for only one of the
parameters, Format . Because Format is the first parameter in the parameter list, you
don't have to provide the parameter name. However, the statement might be easier to
understand if you include the parameter name, as shown in the following code.
C#
Press CTRL+F5 to see the result. You can find other formats in the listed in the
XlRangeAutoFormat enumeration.
Example
The following code shows the complete example.
C#
using System.Collections.Generic;
using Excel = Microsoft.Office.Interop.Excel;
using Word = Microsoft.Office.Interop.Word;
namespace OfficeProgrammingWalkthruComplete
{
class Walkthrough
{
static void Main(string[] args)
{
// Create a list of accounts.
var bankAccounts = new List<Account>
{
new Account {
ID = 345678,
Balance = 541.27
},
new Account {
ID = 1230221,
Balance = -127.44
}
};
var row = 1;
foreach (var acct in accounts)
{
row++;
workSheet.Cells[row, "A"] = acct.ID;
workSheet.Cells[row, "B"] = acct.Balance;
}
workSheet.Columns[1].AutoFit();
workSheet.Columns[2].AutoFit();
See also
Type.Missing
dynamic
Named and Optional Arguments
How to use named and optional arguments in Office programming
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to use named and optional
arguments in Office programming
Article • 03/01/2023
) Important
VSTO (Visual Studio Tools for Office) relies on the .NET Framework. COM add-ins
can also be written with the .NET Framework. Office Add-ins cannot be created with
.NET Core and .NET 5+, the latest versions of .NET. This is because .NET Core/.NET
5+ cannot work together with .NET Framework in the same process and may lead
to add-in load failures. You can continue to use .NET Framework to write VSTO and
COM add-ins for Office. Microsoft will not be updating VSTO or the COM add-in
platform to use .NET Core or .NET 5+. You can take advantage of .NET Core and
.NET 5+, including ASP.NET Core, to create the server side of Office Web Add-ins.
You must have Microsoft Office Word installed on your computer to complete these
procedures.
7 Note
Your computer might show different names or locations for some of the Visual
Studio user interface elements in the following instructions. The Visual Studio
edition that you have and the settings that you use determine these elements. For
more information, see Personalizing the IDE.
Add a reference
In Solution Explorer, right-click your project's name and then select Add Reference. The
Add Reference dialog box appears. On the .NET page, select
Microsoft.Office.Interop.Word in the Component Name list. Select OK.
C#
C#
C#
C#
DisplayInWord();
Press CTRL + F5 to run the project. A Word document appears that contains the
specified text.
Named and optional arguments enable you to specify values for only the parameters
that you want to change. Add the following code to the end of method DisplayInWord
to create a table. The argument specifies that the commas in the text string in range
separate the cells of the table.
C#
// Convert to a simple table. The table will have a single row with
// three columns.
range.ConvertToTable(Separator: ",");
C#
Specify a predefined format for the table, replace the last line in DisplayInWord with the
following statement and then type CTRL + F5 . The format can be any of the
WdTableFormat constants.
C#
Example
The following code includes the full example:
C#
using System;
using Word = Microsoft.Office.Interop.Word;
namespace OfficeHowTo
{
class WordProgram
{
static void Main(string[] args)
{
DisplayInWord();
}
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
be found on GitHub, where you Select a link to provide feedback:
can also create and review
issues and pull requests. For Open a documentation issue
more information, see our
contributor guide. Provide product feedback
Using type dynamic
Article • 02/25/2023
The dynamic type is a static type, but an object of type dynamic bypasses static type
checking. In most cases, it functions like it has type object . The compiler assumes a
dynamic element supports any operation. Therefore, you don't have to determine
whether the object gets its value from a COM API, from a dynamic language such as
IronPython, from the HTML Document Object Model (DOM), from reflection, or from
somewhere else in the program. However, if the code isn't valid, errors surface at run
time.
For example, if instance method exampleMethod1 in the following code has only one
parameter, the compiler recognizes that the first call to the method,
ec.exampleMethod1(10, 4) , isn't valid because it contains two arguments. The call causes
a compiler error. The compiler doesn't check the second call to the method,
dynamic_ec.exampleMethod1(10, 4) , because the type of dynamic_ec is dynamic .
Therefore, no compiler error is reported. However, the error doesn't escape notice
indefinitely. It appears at run time and causes a run-time exception.
C#
C#
class ExampleClass
{
public ExampleClass() { }
public ExampleClass(int v) { }
The role of the compiler in these examples is to package together information about
what each statement is proposing to do to the dynamic object or expression. The
runtime examines the stored information and any statement that isn't valid causes a
run-time exception.
The result of most dynamic operations is itself dynamic . For example, if you rest the
mouse pointer over the use of testSum in the following example, IntelliSense displays
the type (local variable) dynamic testSum.
C#
dynamic d = 1;
var testSum = d + 3;
// Rest the mouse pointer over testSum in the following statement.
System.Console.WriteLine(testSum);
For example, the type of testInstance in the following declaration is ExampleClass , not
dynamic :
C#
Conversions
Conversions between dynamic objects and other types are easy. Conversions enable the
developer to switch between dynamic and non-dynamic behavior.
You can convert any to dynamic implicitly, as shown in the following examples.
C#
dynamic d1 = 7;
dynamic d2 = "a string";
dynamic d3 = System.DateTime.Today;
dynamic d4 = System.Diagnostics.Process.GetProcesses();
Conversely, you can dynamically apply any implicit conversion to any expression of type
dynamic .
C#
int i = d1;
string str = d2;
DateTime dt = d3;
System.Diagnostics.Process[] procs = d4;
C#
// Valid.
ec.exampleMethod2("a string");
// The following statement does not cause a compiler error, even though ec
is not
// dynamic. A run-time exception is raised because the run-time type of d1
is int.
ec.exampleMethod2(d1);
// The following statement does cause a compiler error.
//ec.exampleMethod2(7);
COM interop
Many COM methods allow for variation in argument types and return type by
designating the types as object . COM interop necessitates explicit casting of the values
to coordinate with strongly typed variables in C#. If you compile by using the
EmbedInteropTypes (C# Compiler Options) option, the introduction of the dynamic type
enables you to treat the occurrences of object in COM signatures as if they were of
type dynamic , and thereby to avoid much of the casting. For more information on using
the dynamic type with COM objects, see the article on How to access Office interop
objects by using C# features.
Related articles
ノ Expand table
Title Description
Walkthrough: Creating and Provides step-by-step instructions for creating a custom dynamic
Using Dynamic Objects object and for creating a project that accesses an IronPython
library.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Walkthrough: Creating and Using
Dynamic Objects in C#
Article • 02/25/2023
Dynamic objects expose members such as properties and methods at run time, instead
of at compile time. Dynamic objects enable you to create objects to work with structures
that don't match a static type or format. For example, you can use a dynamic object to
reference the HTML Document Object Model (DOM), which can contain any
combination of valid HTML markup elements and attributes. Because each HTML
document is unique, the members for a particular HTML document are determined at
run time. A common method to reference an attribute of an HTML element is to pass
the name of the attribute to the GetProperty method of the element. To reference the
id attribute of the HTML element <div id="Div1"> , you first obtain a reference to the
You reference a dynamic object by using late binding. You specify the type of a late-
bound object as dynamic .For more information, see dynamic.
You can create custom dynamic objects by using the classes in the System.Dynamic
namespace. For example, you can create an ExpandoObject and specify the members of
that object at run time. You can also create your own type that inherits the
DynamicObject class. You can then override the members of the DynamicObject class to
provide run-time dynamic functionality.
Create a custom object that dynamically exposes the contents of a text file as
properties of an object.
Create a project that uses an IronPython library.
Prerequisites
Visual Studio 2022 version 17.3 or a later version with the .NET desktop
development workload installed. The .NET 7 SDK is included when you select this
workload.
7 Note
Your computer might show different names or locations for some of the Visual
Studio user interface elements in the following instructions. The Visual Studio
edition that you have and the settings that you use determine these elements. For
more information, see Personalizing the IDE.
For the second walkthrough, install IronPython for .NET. Go to their Download
page to obtain the latest version.
for "Sample" at the start of each line, and doesn't remove leading and trailing spaces.
The default behavior of the dynamic class is to search for a match at the start of each
line and to remove leading and trailing spaces.
using System.IO;
using System.Dynamic;
The custom dynamic object uses an enum to determine the search criteria. Before the
class statement, add the following enum definition.
C#
Update the class statement to inherit the DynamicObject class, as shown in the following
code example.
C#
Add the following code to the ReadOnlyFile class to define a private field for the file
path and a constructor for the ReadOnlyFile class.
C#
// Store the path to the file and the initial line count value.
private string p_filePath;
// Public constructor. Verify that file exists and store the path in
// the private variable.
public ReadOnlyFile(string filePath)
{
if (!File.Exists(filePath))
{
throw new Exception("File path does not exist.");
}
p_filePath = filePath;
}
results.
C#
try
{
sr = new StreamReader(p_filePath);
while (!sr.EndOfStream)
{
line = sr.ReadLine();
switch (StringSearchOption)
{
case StringSearchOption.StartsWith:
if (testLine.StartsWith(propertyName.ToUpper())) {
results.Add(line); }
break;
case StringSearchOption.Contains:
if (testLine.Contains(propertyName.ToUpper())) {
results.Add(line); }
break;
case StringSearchOption.EndsWith:
if (testLine.EndsWith(propertyName.ToUpper())) {
results.Add(line); }
break;
}
}
}
catch
{
// Trap any exception that occurs in reading the file and return
null.
results = null;
}
finally
{
if (sr != null) {sr.Close();}
}
return results;
}
After the GetPropertyValue method, add the following code to override the
TryGetMember method of the DynamicObject class. The TryGetMember method is called
when a member of a dynamic class is requested and no arguments are specified. The
binder argument contains information about the referenced member, and the result
argument references the result returned for the specified member. The TryGetMember
method returns a Boolean value that returns true if the requested member exists;
otherwise it returns false .
C#
After the TryGetMember method, add the following code to override the
TryInvokeMember method of the DynamicObject class. The TryInvokeMember method is
called when a member of a dynamic class is requested with arguments. The binder
argument contains information about the referenced member, and the result
argument references the result returned for the specified member. The args argument
contains an array of the arguments that are passed to the member. The
TryInvokeMember method returns a Boolean value that returns true if the requested
member exists; otherwise it returns false .
The custom version of the TryInvokeMember method expects the first argument to be a
value from the StringSearchOption enum that you defined in a previous step. The
TryInvokeMember method expects the second argument to be a Boolean value. If one or
both arguments are valid values, they're passed to the GetPropertyValue method to
retrieve the results.
C#
// Implement the TryInvokeMember method of the DynamicObject class for
// dynamic member calls that have arguments.
public override bool TryInvokeMember(InvokeMemberBinder binder,
object[] args,
out object result)
{
StringSearchOption StringSearchOption = StringSearchOption.StartsWith;
bool trimSpaces = true;
try
{
if (args.Length > 0) { StringSearchOption =
(StringSearchOption)args[0]; }
}
catch
{
throw new ArgumentException("StringSearchOption argument must be a
StringSearchOption enum value.");
}
try
{
if (args.Length > 1) { trimSpaces = (bool)args[1]; }
}
catch
{
throw new ArgumentException("trimSpaces argument must be a Boolean
value.");
}
text
The code uses late binding to call dynamic members and retrieve lines of text that
contain the string "Customer".
C#
Save the file and press Ctrl+ F5 to build and run the application.
C#
using System.Linq;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
C#
After the code to load the random.py module, add the following code to create an array
of integers. The array is passed to the shuffle method of the random.py module, which
randomly sorts the values in the array.
C#
Save the file and press Ctrl+ F5 to build and run the application.
See also
System.Dynamic
System.Dynamic.DynamicObject
Using Type dynamic
dynamic
Implementing Dynamic Interfaces (downloadable PDF from Microsoft TechNet)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Reduce memory allocations using new
C# features
Article • 10/17/2023
) Important
The techniques described in this section improve performance when applied to hot
paths in your code. Hot paths are those sections of your codebase that are
executed often and repeatedly in normal operations. Applying these techniques to
code that isn't often executed will have minimal impact. Before making any
changes to improve performance, it's critical to measure a baseline. Then, analyze
that baseline to determine where memory bottlenecks occur. You can learn about
many cross platform tools to measure your application's performance in the section
on Diagnostics and instrumentation. You can practice a profiling session in the
tutorial to Measure memory usage in the Visual Studio documentation.
Once you've measured memory usage and have determined that you can reduce
allocations, use the techniques in this section to reduce allocations. After each
successive change, measure memory usage again. Make sure each change has a
positive impact on the memory usage in your application.
Performance work in .NET often means removing allocations from your code. Every
block of memory you allocate must eventually be freed. Fewer allocations reduce time
spent in garbage collection. It allows for more predictable execution time by removing
garbage collections from specific code paths.
A common tactic to reduce allocations is to change critical data structures from class
types to struct types. This change impacts the semantics of using those types.
Parameters and returns are now passed by value instead of by reference. The cost of
copying a value is negligible if the types are small, three words or less (considering one
word being of natural size of one integer). It's measurable and can have real
performance impact for larger types. To combat the effect of copying, developers can
pass these types by ref to get back the intended semantics.
The C# ref features give you the ability to express the desired semantics for struct
types without negatively impacting their overall usability. Prior to these enhancements,
developers needed to resort to unsafe constructs with pointers and raw memory to
achieve the same performance impact. The compiler generates verifiably safe code for
the new ref related features. Verifiably safe code means the compiler detects possible
buffer overruns or accessing unallocated or freed memory. The compiler detects and
prevents some errors.
In C#, parameters to methods are passed by value, and return values are return by value.
The value of the argument is passed to the method. The value of the return argument is
the return value.
The ref , in , ref readonly , or out modifier indicates that the argument is passed by
reference. A reference to the storage location is passed to the method. Adding ref to
the method signature means the return value is returned by reference. A reference to the
storage location is the return value.
You can also use ref assignment to have a variable refer to another variable. A typical
assignment copies the value of the right hand side to the variable on the left hand side
of the assignment. A ref assignment copies the memory location of the variable on the
right hand side to the variable on the left hand side. The ref now refers to the original
variable:
C#
Console.WriteLine(location); // output: 42
Console.WriteLine(anInteger); // output: 19
When you assign a variable, you change its value. When you ref assign a variable, you
change what it refers to.
You can work directly with the storage for values using ref variables, pass by reference,
and ref assignment. Scope rules enforced by the compiler ensure safety when working
directly with storage.
The ref readonly and in modifiers both indicate that the argument should be passed
by reference and can't be reassigned in the method. The difference is that ref readonly
indicates that the method uses the parameter as a variable. The method might capture
the parameter, or it might return the parameter by readonly reference. In those cases,
you should use the ref readonly modifier. Otherwise, the in modifier offers more
flexibility. You don't need to add the in modifier to an argument for an in parameter,
so you can update existing API signatures safely using the in modifier. The compiler
issues a warning if you don't add either the ref or in modifier to an argument for a
ref readonly parameter.
C#
The compiler reports an error because you can't return a reference to a local variable
from a method. The caller can't access the storage being referred to. The ref safe context
defines the scope in which a ref expression is safe to access or modify. The following
table lists the ref safe contexts for variable types. ref fields can't be declared in a class
or a non-ref struct , so those rows aren't in the table:
A variable can be ref returned if its ref safe context is the calling method. If its ref safe
context is the current method or a block, ref return is disallowed. The following snippet
shows two examples. A member field can be accessed from the scope calling a method,
so a class or struct field's ref safe context is the calling method. The ref safe context for a
parameter with the ref , or in modifiers is the entire method. Both can be ref returned
from a member method:
C#
7 Note
The compiler ensures that a reference can't escape its ref safe context. You can use ref
parameters, ref return , and ref local variables safely because the compiler detects if
you've accidentally written code where a ref expression could be accessed when its
storage isn't valid.
type can include ref fields. That requires the introduction of a safe context. For most
types, the safe context is the calling method. In other words, a value that's not a ref
struct can always be returned from a method.
Informally, the safe context for a ref struct is the scope where all of its ref fields can
be accessed. In other words, it's the intersection of the ref safe context of all its ref
fields. The following method returns a ReadOnlySpan<char> to a member field, so its safe
context is the method:
C#
In contrast, the following code emits an error because the ref field member of the
Span<int> refers to the stack allocated array of integers. It can't escape the method:
C#
struct types. Spans contain a ref field . Therefore instances of a span can't leave its
safe context. The safe context of a ref struct is the ref safe context of its ref field . The
implementation of Memory<T> and ReadOnlyMemory<T> remove this restriction. You use
these types to directly access memory buffers.
Avoid allocations: When you change a type from a class to a struct , you change
how it's stored. Local variables are stored on the stack. Members are stored inline
when the container object is allocated. This change means fewer allocations and
that decreases the work the garbage collector does. It might also decrease
memory pressure so the garbage collector runs less often.
Preserve reference semantics: Changing a type from a class to a struct changes
the semantics of passing a variable to a method. Code that modified the state of
its parameters needs modification. Now that the parameter is a struct , the
method is modifying a copy of the original object. You can restore the original
semantics by passing that parameter as a ref parameter. After that change, the
method modifies the original struct again.
Avoid copying data: Copying larger struct types can impact performance in some
code paths. You can also add the ref modifier to pass larger data structures to
methods by reference instead of by value.
Restrict modifications: When a struct type is passed by reference, the called
method could modify the state of the struct. You can replace the ref modifier with
the ref readonly or in modifiers to indicate that the argument can't be modified.
Prefer ref readonly when the method captures the parameter or returns it by
readonly reference. You can also create readonly struct types or struct types
with readonly members to provide more control over what members of a struct
can be modified.
Directly manipulate memory: Some algorithms are most efficient when treating
data structures as a block of memory containing a sequence of elements. The Span
and Memory types provide safe access to blocks of memory.
None of these techniques require unsafe code. Used wisely, you can get performance
characteristics from safe code that was previously only possible by using unsafe
techniques. You can try the techniques yourself in the tutorial on reducing memory
allocations.
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Tutorial: Reduce memory allocations
with ref safety
Article • 10/13/2023
Often, performance tuning for a .NET application involves two techniques. First, reduce
the number and size of heap allocations. Second, reduce how often data is copied.
Visual Studio provides great tools that help analyze how your application is using
memory. Once you've determined where your app makes unnecessary allocations, you
make changes to minimize those allocations. You convert class types to struct types.
You use ref safety features to preserve semantics and minimize extra copying.
Use Visual Studio 17.5 for the best experience with this tutorial. The .NET object
allocation tool used to analyze memory usage is part of Visual Studio. You can use Visual
Studio Code and the command line to run the application and make all the changes.
However, you won't be able to see the analysis results of your changes.
The application you'll use is a simulation of an IoT application that monitors several
sensors to determine if an intruder has entered a secret gallery with valuables. The IoT
sensors are constantly sending data that measures the mix of Oxygen (O2) and Carbon
Dioxide (CO2) in the air. They also report the temperature and relative humidity. Each of
these values is fluctuating slightly all the time. However, when a person enters the room,
the change a bit more, and always in the same direction: Oxygen decreases, Carbon
Dioxide increases, temperature increases, as does relative humidity. When the sensors
combine to show increases, the intruder alarm is triggered.
In this tutorial, you'll run the application, take measurements on memory allocations,
then improve the performance by reducing the number of allocations. The source code
is available in the samples browser.
Console
Debounced measurements:
Temp: 67.332
Humidity: 41.077%
Oxygen: 21.097%
CO2 (ppm): 404.906
Average measurements:
Temp: 67.332
Humidity: 41.077%
Oxygen: 21.097%
CO2 (ppm): 404.906
Debounced measurements:
Temp: 67.349
Humidity: 46.605%
Oxygen: 20.998%
CO2 (ppm): 408.707
Average measurements:
Temp: 67.349
Humidity: 46.605%
Oxygen: 20.998%
CO2 (ppm): 408.707
Console
Debounced measurements:
Temp: 67.597
Humidity: 46.543%
Oxygen: 19.021%
CO2 (ppm): 429.149
Average measurements:
Temp: 67.568
Humidity: 45.684%
Oxygen: 19.631%
CO2 (ppm): 423.498
Current intruders: 3
Calculated intruder risk: High
Debounced measurements:
Temp: 67.602
Humidity: 46.835%
Oxygen: 19.003%
CO2 (ppm): 429.393
Average measurements:
Temp: 67.568
Humidity: 45.684%
Oxygen: 19.631%
CO2 (ppm): 423.498
Current intruders: 3
Calculated intruder risk: High
You can explore the code to learn how the application works. The main program runs
the simulation. After you press <Enter> , it creates a room, and gathers some initial
baseline data:
C#
int counter = 0;
room.TakeMeasurements(
m =>
{
Console.WriteLine(room.Debounce);
Console.WriteLine(room.Average);
Console.WriteLine();
counter++;
return counter < 20000;
});
Once that baseline data has been established, it runs the simulation on the room, where
a random number generator determines if an intruder has entered the room:
C#
counter = 0;
room.TakeMeasurements(
m =>
{
Console.WriteLine(room.Debounce);
Console.WriteLine(room.Average);
room.Intruders += (room.Intruders, r.Next(5)) switch
{
( > 0, 0) => -1,
( < 3, 1) => 1,
_ => 0
};
Other types contain the measurements, a debounced measurement that is the average
of the last 50 measurements, and the average of all measurements taken.
Next, run the application using the .NET object allocation tool. Make sure you're using
the Release build, not the Debug build. On the Debug menu, open the Performance
profiler. Check the .NET Object Allocation Tracking option, but nothing else. Run your
application to completion. The profiler measures object allocations and reports on
allocations and garbage collection cycles. You should see a graph similar to the
following image:
The previous graph shows that working to minimize allocations will provide
performance benefits. You see a sawtooth pattern in the live objects graph. That tells
you that numerous objects are created that quickly become garbage. They're later
collected, as shown in the object delta graph. The downward red bars indicate a
garbage collection cycle.
Next, look at the Allocations tab below the graphs. This table shows what types are
allocated the most:
The System.String type accounts for the most allocations. The most important task
should be to minimize the frequency of string allocations. This application prints
numerous formatted output to the console constantly. For this simulation, we want to
keep messages, so we'll concentrate on the next two rows: the SensorMeasurement type,
and the IntruderRisk type.
Double-click on the SensorMeasurement line. You can see that all the allocations take
place in the static method SensorMeasurement.TakeMeasurement . You can see the
method in the following snippet:
C#
C#
The type was originally created as a class because it contains numerous double
measurements. It's larger than you'd want to copy in hot paths. However, that decision
meant a large number of allocations. Change the type from a class to a struct .
Changing from a class to struct introduces a few compiler errors because the original
code used null reference checks in a few spots. The first is in the DebounceMeasurement
class, in the AddMeasurement method:
C#
test instead:
C#
The other three compiler errors are all in the method that repeatedly takes
measurements in a room:
C#
In the starter method, the local variable for the SensorMeasurement is a nullable reference:
C#
Now that the SensorMeasurement is a struct instead of a class , the nullable is a nullable
value type. You can change the declaration to a value type to fix the remaining compiler
errors:
C#
Now that the compiler errors have been addressed, you should examine the code to
ensure the semantics haven't changed. Because struct types are passed by value,
modifications made to method parameters aren't visible after the method returns.
) Important
Changing a type from a class to a struct can change the semantics of your
program. When a class type is passed to a method, any mutations made in the
method are made to the argument. When a struct type is passed to a method,
and mutations made in the method are made to a copy of the argument. That
means any method that modifies its arguments by design should be updated to
use the ref modifier on any argument type you've changed from a class to a
struct .
The SensorMeasurement type doesn't include any methods that change state, so that's
not a concern in this sample. You can prove that by adding the readonly modifier to the
SensorMeasurement struct:
C#
The compiler enforces the readonly nature of the SensorMeasurement struct. If your
inspection of the code missed some method that modified state, the compiler would tell
you. Your app still builds without errors, so this type is readonly . Adding the readonly
modifier when you change a type from a class to a struct can help you find members
that modify the state of the struct .
The next step is to find methods that return a measurement, or take a measurement as
an argument, and use references where possible. Start in the SensorMeasurement struct.
The static TakeMeasurement method creates and returns a new SensorMeasurement :
C#
We'll leave this one as is, returning by value. If you tried to return by ref , you'd get a
compiler error. You can't return a ref to a new structure locally created in the method.
The design of the immutable struct means you can only set the values of the
measurement at construction. This method must create a new measurement struct.
C#
That saves one copy operation. The in parameter is a reference to the copy already
created by the caller. You can also save a copy with the TakeMeasurement method in the
Room type. This method illustrates how the compiler provides safety when you pass
arguments by ref . The initial TakeMeasurement method in the Room type takes an
argument of Func<SensorMeasurement, bool> . If you try to add the in or ref modifier to
that declaration, the compiler reports an error. You can't pass a ref argument to a
lambda expression. The compiler can't guarantee that the called expression doesn't
copy the reference. If the lambda expression captures the reference, the reference could
have a lifetime longer than the value it refers to. Accessing it outside its ref safe context
would result in memory corruption. The ref safety rules don't allow it. You can learn
more in the overview of ref safety features.
Preserve semantics
The final sets of changes won't have a major impact on this application's performance
because the types aren't created in hot paths. These changes illustrate some of the
other techniques you'd use in your performance tuning. Let's take a look at the initial
Room class:
C#
This type contains several properties. Some are class types. Creating a Room object
involves multiple allocations. One for the Room itself, and one for each of the members
of a class type that it contains. You can convert two of these properties from class
types to struct types: the DebounceMeasurement and AverageMeasurement types. Let's
work through that transformation with both types.
C#
public DebounceMeasurement() { }
You can learn more about this requirement in the language reference article on structs.
The Object.ToString() override doesn't modify any of the values of the struct. You can
add the readonly modifier to that method declaration. The DebounceMeasurement type is
mutable, so you'll need to take care that modifications don't affect copies that are
discarded. The AddMeasurement method does modify the state of the object. It's called
from the Room class, in the TakeMeasurements method. You want those changes to persist
after calling the method. You can change the Room.Debounce property to return a
reference to a single instance of the DebounceMeasurement type:
C#
There are a few changes in the previous example. First, the property is a readonly
property that returns a readonly reference to the instance owned by this room. It's now
backed by a declared field that's initialized when the Room object is instantiated. After
making these changes, you'll update the implementation of AddMeasurement method. It
uses the private backing field, debounce , not the readonly property Debounce . That way,
the changes take place on the single instance created during initialization.
The same technique works with the Average property. First, you modify the
AverageMeasurement type from a class to a struct , and add the readonly modifier on
C#
namespace IntruderAlert;
public AverageMeasurement() { }
Then, you modify the Room class following the same technique you used for the
Debounce property. The Average property returns a readonly ref to the private field for
the average measurement. The AddMeasurement method modifies the internal fields.
C#
Avoid boxing
There's one final change to improve performance. The main program is printing stats for
the room, including the risk assessment:
C#
The call to the generated ToString boxes the enum value. You can avoid that by writing
an override in the Room class that formats the string based on the value of estimated
risk:
C#
Then, modify the code in the main program to call this new ToString method:
C#
Console.WriteLine(room.ToString());
Run the app using the profiler and look at the updated table for allocations.
You've removed numerous allocations, and provided your app with a performance
boost.
Measure allocations: Determine what types are being allocated the most, and when
you can reduce the heap allocations.
Convert class to struct: Many times, types can be converted from a class to a
struct . Your app uses stack space instead of making heap allocations.
Preserve semantics: Converting a class to a struct can impact the semantics for
parameters and return values. Any method that modifies its parameters should
now mark those parameters with the ref modifier. That ensures the modifications
are made to the correct object. Similarly, if a property or method return value
should be modified by the caller, that return should be marked with the ref
modifier.
Avoid copies: When you pass a large struct as a parameter, you can mark the
parameter with the in modifier. You can pass a reference in fewer bytes, and
ensure that the method doesn't modify the original value. You can also return
values by readonly ref to return a reference that can't be modified.
Using these techniques you can improve performance in hot paths of your code.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
The .NET Compiler Platform SDK
Article • 09/15/2021
Compilers build a detailed model of application code as they validate the syntax and
semantics of that code. They use this model to build the executable output from the
source code. The .NET Compiler Platform SDK provides access to this model.
Increasingly, we rely on integrated development environment (IDE) features such as
IntelliSense, refactoring, intelligent rename, "Find all references," and "Go to definition"
to increase our productivity. We rely on code analysis tools to improve our code quality,
and code generators to aid in application construction. As these tools get smarter, they
need access to more and more of the model that only compilers create as they process
application code. This is the core mission of the Roslyn APIs: opening up the opaque
boxes and allowing tools and end users to share in the wealth of information compilers
have about our code. Instead of being opaque source-code-in and object-code-out
translators, through Roslyn, compilers become platforms: APIs that you can use for
code-related tasks in your tools and applications.
The .NET Compiler Platform SDK enables you to build analyzers and code fixes that find
and correct coding mistakes. Analyzers understand the syntax (structure of code) and
semantics to detect practices that should be corrected. Code fixes provide one or more
suggested fixes for addressing coding mistakes found by analyzers or compiler
diagnostics. Typically, an analyzer and the associated code fixes are packaged together
in a single project.
Analyzers and code fixes use static analysis to understand code. They do not run the
code or provide other testing benefits. They can, however, point out practices that often
lead to bugs, unmaintainable code, or standard guideline violation.
In addition to analyzers and code fixes, The .NET Compiler Platform SDK also enables
you to build code refactorings. It also provides a single set of APIs that enable you to
examine and understand a C# or Visual Basic codebase. Because you can use this single
codebase, you can write analyzers and code fixes more easily by leveraging the syntactic
and semantic analysis APIs provided by the .NET Compiler Platform SDK. Freed from the
large task of replicating the analysis done by the compiler, you can concentrate on the
more focused task of finding and fixing common coding errors for your project or
library.
A smaller benefit is that your analyzers and code fixes are smaller and use much less
memory when loaded in Visual Studio than they would if you wrote your own codebase
to understand the code in a project. By leveraging the same classes used by the
compiler and Visual Studio, you can create your own static analysis tools. This means
your team can use analyzers and code fixes without a noticeable impact on the IDE's
performance.
There are three main scenarios for writing analyzers and code fixes:
Analyzers run as a developer writes code. The developer gets immediate feedback that
encourages following the guidance immediately. The developer builds habits to write
compliant code as soon as they begin prototyping. When the feature is ready for
humans to review, all the standard guidance has been enforced.
Teams can build analyzers and code fixes that look for the most common practices that
violate team coding practices. These can be installed on each developer's machine to
enforce the standards.
Tip
Before building your own analyzer, check out the built-in ones. For more
information, see Code-style rules.
Provide guidance with library packages
There is a wealth of libraries available for .NET developers on NuGet. Some of these
come from Microsoft, some from third-party companies, and others from community
members and volunteers. These libraries get more adoption and higher reviews when
developers can succeed with those libraries.
In addition to providing documentation, you can provide analyzers and code fixes that
find and correct common mis-uses of your library. These immediate corrections will help
developers succeed more quickly.
You can package analyzers and code fixes with your library on NuGet. In that scenario,
every developer who installs your NuGet package will also install the analyzer package.
All developers using your library will immediately get guidance from your team in the
form of immediate feedback on mistakes and suggested corrections.
These analyzers can be uploaded to the Visual Studio Marketplace and downloaded
by developers using Visual Studio. Newcomers to the language and the platform learn
accepted practices quickly and become productive earlier in their .NET journey. As these
become more widely used, the community adopts these practices.
Next steps
The .NET Compiler Platform SDK includes the latest language object models for code
generation, analysis, and refactoring. This section provides a conceptual overview of the
.NET Compiler Platform SDK. Further details can be found in the quickstarts, samples,
and tutorials sections.
You can learn more about the concepts in the .NET Compiler Platform SDK in these five
topics:
To get started, you'll need to install the .NET Compiler Platform SDK:
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
1. Check the box for DGML editor. You'll find it under the Code tools section.
Understand the .NET Compiler Platform
SDK model
Article • 09/15/2021
Compilers process the code you write following structured rules that often differ from
the way humans read and understand code. A basic understanding of the model used
by compilers is essential to understanding the APIs you use when building Roslyn-based
tools.
Each phase of this pipeline is a separate component. First, the parse phase tokenizes
and parses source text into syntax that follows the language grammar. Second, the
declaration phase analyzes source and imported metadata to form named symbols.
Next, the bind phase matches identifiers in the code to symbols. Finally, the emit phase
emits an assembly with all the information built up by the compiler.
Corresponding to each of those phases, the .NET Compiler Platform SDK exposes an
object model that allows access to the information at that phase. The parsing phase
exposes a syntax tree, the declaration phase exposes a hierarchical symbol table, the
binding phase exposes the result of the compiler's semantic analysis, and the emit phase
is an API that produces IL byte codes.
Each compiler combines these components together as a single end-to-end whole.
These APIs are the same ones used by Visual Studio. For instance, the code outlining
and formatting features use the syntax trees, the Object Browser, and navigation
features use the symbol table, refactorings and Go to Definition use the semantic
model, and Edit and Continue uses all of these, including the Emit API.
API layers
The .NET compiler SDK consists of several layers of APIs: compiler APIs, diagnostic APIs,
scripting APIs, and workspaces APIs.
Compiler APIs
The compiler layer contains the object models that correspond to information exposed
at each phase of the compiler pipeline, both syntactic and semantic. The compiler layer
also contains an immutable snapshot of a single invocation of a compiler, including
assembly references, compiler options, and source code files. There are two distinct APIs
that represent the C# language and the Visual Basic language. The two APIs are similar
in shape but tailored for high-fidelity to each individual language. This layer has no
dependencies on Visual Studio components.
Diagnostic APIs
As part of its analysis, the compiler may produce a set of diagnostics covering
everything from syntax, semantic, and definite assignment errors to various warnings
and informational diagnostics. The Compiler API layer exposes diagnostics through an
extensible API that allows user-defined analyzers to be plugged into the compilation
process. It allows user-defined diagnostics, such as those produced by tools like
StyleCop, to be produced alongside compiler-defined diagnostics. Producing
diagnostics in this way has the benefit of integrating naturally with tools such as
MSBuild and Visual Studio, which depend on diagnostics for experiences such as halting
a build based on policy and showing live squiggles in the editor and suggesting code
fixes.
Scripting APIs
Hosting and scripting APIs are built on top of the compiler layer. You can use the
scripting APIs to run code snippets and accumulate a runtime execution context. The C#
interactive REPL (Read-Evaluate-Print Loop) uses these APIs. The REPL enables you to
use C# as a scripting language, running the code interactively as you write it.
Workspaces APIs
The Workspaces layer contains the Workspace API, which is the starting point for doing
code analysis and refactoring over entire solutions. It assists you in organizing all the
information about the projects in a solution into a single object model, offering you
direct access to the compiler layer object models without needing to parse files,
configure options, or manage project-to-project dependencies.
In addition, the Workspaces layer surfaces a set of APIs used when implementing code
analysis and refactoring tools that function within a host environment like the Visual
Studio IDE. Examples include the Find All References, Formatting, and Code Generation
APIs.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open
source. Provide feedback here.
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Work with syntax
Article • 09/15/2021
The syntax tree is a fundamental immutable data structure exposed by the compiler
APIs. These trees represent the lexical and syntactic structure of source code. They serve
two important purposes:
To allow tools - such as an IDE, add-ins, code analysis tools, and refactorings - to
see and process the syntactic structure of source code in a user's project.
To enable tools - such as refactorings and an IDE - to create, modify, and rearrange
source code in a natural manner without having to use direct text edits. By creating
and manipulating trees, tools can easily create and rearrange source code.
Syntax trees
Syntax trees are the primary structure used for compilation, code analysis, binding,
refactoring, IDE features, and code generation. No part of the source code is understood
without it first being identified and categorized into one of many well-known structural
language elements.
7 Note
RoslynQuoter is an open-source tool that shows the syntax factory API calls used
to construct a program's syntax tree. To try it out live, see
http://roslynquoter.azurewebsites.net .
They hold all the source information in full fidelity. Full fidelity means that the
syntax tree contains every piece of information found in the source text, every
grammatical construct, every lexical token, and everything else in between,
including white space, comments, and preprocessor directives. For example, each
literal mentioned in the source is represented exactly as it was typed. Syntax trees
also capture errors in source code when the program is incomplete or malformed
by representing skipped or missing tokens.
They can produce the exact text that they were parsed from. From any syntax
node, it's possible to get the text representation of the subtree rooted at that
node. This ability means that syntax trees can be used as a way to construct and
edit source text. By creating a tree you have, by implication, created the equivalent
text, and by making a new tree out of changes to an existing tree, you have
effectively edited the text.
They are immutable and thread-safe. After a tree is obtained, it's a snapshot of the
current state of the code and never changes. This allows multiple users to interact
with the same syntax tree at the same time in different threads without locking or
duplication. Because the trees are immutable and no modifications can be made
directly to a tree, factory methods help create and modify syntax trees by creating
additional snapshots of the tree. The trees are efficient in the way they reuse
underlying nodes, so a new version can be rebuilt fast and with little extra memory.
A syntax tree is literally a tree data structure, where non-terminal structural elements
parent other elements. Each syntax tree is made up of nodes, tokens, and trivia.
Syntax nodes
Syntax nodes are one of the primary elements of syntax trees. These nodes represent
syntactic constructs such as declarations, statements, clauses, and expressions. Each
category of syntax nodes is represented by a separate class derived from
Microsoft.CodeAnalysis.SyntaxNode. The set of node classes is not extensible.
All syntax nodes are non-terminal nodes in the syntax tree, which means they always
have other nodes and tokens as children. As a child of another node, each node has a
parent node that can be accessed through the SyntaxNode.Parent property. Because
nodes and trees are immutable, the parent of a node never changes. The root of the
tree has a null parent.
Each node has a SyntaxNode.ChildNodes() method, which returns a list of child nodes in
sequential order based on their position in the source text. This list does not contain
tokens. Each node also has methods to examine Descendants, such as
DescendantNodes, DescendantTokens, or DescendantTrivia - that represent a list of all
the nodes, tokens, or trivia that exist in the subtree rooted by that node.
In addition, each syntax node subclass exposes all the same children through strongly
typed properties. For example, a BinaryExpressionSyntax node class has three additional
properties specific to binary operators: Left, OperatorToken, and Right. The type of Left
and Right is ExpressionSyntax, and the type of OperatorToken is SyntaxToken.
Some syntax nodes have optional children. For example, an IfStatementSyntax has an
optional ElseClauseSyntax. If the child is not present, the property returns null.
Syntax tokens
Syntax tokens are the terminals of the language grammar, representing the smallest
syntactic fragments of the code. They are never parents of other nodes or tokens. Syntax
tokens consist of keywords, identifiers, literals, and punctuation.
For efficiency purposes, the SyntaxToken type is a CLR value type. Therefore, unlike
syntax nodes, there is only one structure for all kinds of tokens with a mix of properties
that have meaning depending on the kind of token that is being represented.
For example, an integer literal token represents a numeric value. In addition to the raw
source text the token spans, the literal token has a Value property that tells you the
exact decoded integer value. This property is typed as Object because it may be one of
many primitive types.
The ValueText property tells you the same information as the Value property; however
this property is always typed as String. An identifier in C# source text may include
Unicode escape characters, yet the syntax of the escape sequence itself is not
considered part of the identifier name. So although the raw text spanned by the token
does include the escape sequence, the ValueText property does not. Instead, it includes
the Unicode characters identified by the escape. For example, if the source text contains
an identifier written as \u03C0 , then the ValueText property for this token will return π .
Syntax trivia
Syntax trivia represent the parts of the source text that are largely insignificant for
normal understanding of the code, such as white space, comments, and preprocessor
directives. Like syntax tokens, trivia are value types. The single
Microsoft.CodeAnalysis.SyntaxTrivia type is used to describe all kinds of trivia.
Because trivia are not part of the normal language syntax and can appear anywhere
between any two tokens, they are not included in the syntax tree as a child of a node.
Yet, because they are important when implementing a feature like refactoring and to
maintain full fidelity with the source text, they do exist as part of the syntax tree.
Unlike syntax nodes and tokens, syntax trivia do not have parents. Yet, because they are
part of the tree and each is associated with a single token, you may access the token it is
associated with using the SyntaxTrivia.Token property.
Spans
Each node, token, or trivia knows its position within the source text and the number of
characters it consists of. A text position is represented as a 32-bit integer, which is a
zero-based char index. A TextSpan object is the beginning position and a count of
characters, both represented as integers. If TextSpan has a zero length, it refers to a
location between two characters.
The Span property is the text span from the start of the first token in the node's subtree
to the end of the last token. This span does not include any leading or trailing trivia.
The FullSpan property is the text span that includes the node's normal span, plus the
span of any leading or trailing trivia.
For example:
C#
if (x > 3)
{
|| // this is bad
|throw new Exception("Not right.");| // better exception?||
}
The statement node inside the block has a span indicated by the single vertical bars (|). It
includes the characters throw new Exception("Not right."); . The full span is indicated
by the double vertical bars (||). It includes the same characters as the span and the
characters associated with the leading and trailing trivia.
Kinds
Each node, token, or trivia has a SyntaxNode.RawKind property, of type System.Int32,
that identifies the exact syntax element represented. This value can be cast to a
language-specific enumeration. Each language, C# or Visual Basic, has a single
SyntaxKind enumeration (Microsoft.CodeAnalysis.CSharp.SyntaxKind and
The RawKind property allows for easy disambiguation of syntax node types that share
the same node class. For tokens and trivia, this property is the only way to distinguish
one type of element from another.
For example, a single BinaryExpressionSyntax class has Left, OperatorToken, and Right as
children. The Kind property distinguishes whether it is an AddExpression,
SubtractExpression, or MultiplyExpression kind of syntax node.
Tip
It's recommended to check kinds using IsKind (for C#) or IsKind (for VB) extension
methods.
Errors
Even when the source text contains syntax errors, a full syntax tree that is round-
trippable to the source is exposed. When the parser encounters code that does not
conform to the defined syntax of the language, it uses one of two techniques to create a
syntax tree:
If the parser expects a particular kind of token but does not find it, it may insert a
missing token into the syntax tree in the location that the token was expected. A
missing token represents the actual token that was expected, but it has an empty
span, and its SyntaxNode.IsMissing property returns true .
The parser may skip tokens until it finds one where it can continue parsing. In this
case, the skipped tokens are attached as a trivia node with the kind
SkippedTokensTrivia.
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Work with semantics
Article • 09/15/2021
Syntax trees represent the lexical and syntactic structure of source code. Although this
information alone is enough to describe all the declarations and logic in the source, it is
not enough information to identify what is being referenced. A name may represent:
a type
a field
a method
a local variable
There are program elements represented in source code, and programs can also refer to
previously compiled libraries, packaged in assembly files. Although no source code, and
therefore no syntax nodes or trees, are available for assemblies, programs can still refer
to elements inside them.
In addition to a syntactic model of the source code, a semantic model encapsulates the
language rules, giving you an easy way to correctly match identifiers with the correct
program element being referenced.
Compilation
A compilation is a representation of everything needed to compile a C# or Visual Basic
program, which includes all the assembly references, compiler options, and source files.
Because all this information is in one place, the elements contained in the source code
can be described in more detail. The compilation represents each declared type,
member, or variable as a symbol. The compilation contains a variety of methods that
help you find and relate the symbols that have either been declared in the source code
or imported as metadata from an assembly.
Similar to syntax trees, compilations are immutable. After you create a compilation, it
cannot be changed by you or anyone else you might be sharing it with. However, you
can create a new compilation from an existing compilation, specifying a change as you
do so. For example, you might create a compilation that is the same in every way as an
existing compilation, except it may include an additional source file or assembly
reference.
Symbols
A symbol represents a distinct element declared by the source code or imported from
an assembly as metadata. Every namespace, type, method, property, field, event,
parameter, or local variable is represented by a symbol.
A variety of methods and properties on the Compilation type help you find symbols. For
example, you can find a symbol for a declared type by its common metadata name. You
can also access the entire symbol table as a tree of symbols rooted by the global
namespace.
Symbols also contain additional information that the compiler determines from the
source or metadata, such as other referenced symbols. Each kind of symbol is
represented by a separate interface derived from ISymbol, each with its own methods
and properties detailing the information the compiler has gathered. Many of these
properties directly reference other symbols. For example, the
IMethodSymbol.ReturnType property tells you the actual type symbol that the method
returns.
Symbols are similar in concept to the CLR type system as represented by the
System.Reflection API, yet they are richer in that they model more than just types.
Namespaces, local variables, and labels are all symbols. In addition, symbols are a
representation of language concepts, not CLR concepts. There is a lot of overlap, but
there are many meaningful distinctions as well. For instance, an iterator method in C# or
Visual Basic is a single symbol. However, when the iterator method is translated to CLR
metadata, it is a type and multiple methods.
Semantic model
A semantic model represents all the semantic information for a single source file. You
can use it to discover the following:
The Workspaces layer is the starting point for doing code analysis and refactoring over
entire solutions. Within this layer, the Workspace API assists you in organizing all the
information about the projects in a solution into a single object model, offering you
direct access to compiler layer object models like source text, syntax trees, semantic
models, and compilations without needing to parse files, configure options, or manage
inter-project dependencies.
Host environments, like an IDE, provide a workspace for you corresponding to the open
solution. It is also possible to use this model outside of an IDE by simply loading a
solution file.
Workspace
A workspace is an active representation of your solution as a collection of projects, each
with a collection of documents. A workspace is typically tied to a host environment that
is constantly changing as a user types or manipulates properties.
The Workspace provides access to the current model of the solution. When a change in
the host environment occurs, the workspace fires corresponding events, and the
Workspace.CurrentSolution property is updated. For example, when the user types in a
text editor corresponding to one of the source documents, the workspace uses an event
to signal that the overall model of the solution has changed and which document was
modified. You can then react to those changes by analyzing the new model for
correctness, highlighting areas of significance, or making a suggestion for a code
change.
You can also create stand-alone workspaces that are disconnected from the host
environment or used in an application that has no host environment.
A solution is an immutable model of the projects and documents. This means that the
model can be shared without locking or duplication. After you obtain a solution instance
from the Workspace.CurrentSolution property, that instance will never change. However,
like with syntax trees and compilations, you can modify solutions by constructing new
instances based on existing solutions and specific changes. To get the workspace to
reflect your changes, you must explicitly apply the changed solution back to the
workspace.
A project is a part of the overall immutable solution model. It represents all the source
code documents, parse and compilation options, and both assembly and project-to-
project references. From a project, you can access the corresponding compilation
without needing to determine project dependencies or parse any source files.
The following diagram is a representation of how the Workspace relates to the host
environment, tools, and how edits are made.
Summary
Roslyn exposes a set of compiler APIs and Workspaces APIs that provides rich
information about your source code and that has full fidelity with the C# and Visual
Basic languages. The .NET Compiler Platform SDK dramatically lowers the barrier to
entry for creating code-focused tools and applications. It creates many opportunities for
innovation in areas such as meta-programming, code generation and transformation,
interactive use of the C# and Visual Basic languages, and embedding of C# and Visual
Basic in domain-specific languages.
Explore code with the Roslyn syntax
visualizer in Visual Studio
Article • 10/11/2022
This article provides an overview of the Syntax Visualizer tool that ships as part of the
.NET Compiler Platform ("Roslyn") SDK. The Syntax Visualizer is a tool window that helps
you inspect and explore syntax trees. It's an essential tool to understand the models for
code you want to analyze. It's also a debugging aid when you develop your own
applications using the .NET Compiler Platform (“Roslyn”) SDK. Open this tool as you
create your first analyzers. The visualizer helps you understand the models used by the
APIs. You can also use tools like SharpLab or LINQPad to inspect code and
understand syntax trees.
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
1. Check the box for DGML editor. You'll find it under the Code tools section.
Familiarize yourself with the concepts used in the .NET Compiler Platform SDK by
reading the overview article. It provides an introduction to syntax trees, nodes, tokens,
and trivia.
Syntax Visualizer
The Syntax Visualizer enables inspection of the syntax tree for the C# or Visual Basic
code file in the current active editor window inside the Visual Studio IDE. The visualizer
can be launched by clicking on View > Other Windows > Syntax Visualizer. You can
also use the Quick Launch toolbar in the upper right corner. Type "syntax", and the
command to open the Syntax Visualizer should appear.
This command opens the Syntax Visualizer as a floating tool window. If you don't have a
code editor window open, the display is blank, as shown in the following figure.
Dock this tool window at a convenient location inside Visual Studio, such as the left side.
The Visualizer shows information about the current code file.
Create a new project using the File > New Project command. You can create either a
Visual Basic or C# project. When Visual Studio opens the main code file for this project,
the visualizer displays the syntax tree for it. You can open any existing C# / Visual Basic
file in this Visual Studio instance, and the visualizer displays that file's syntax tree. If you
have multiple code files open inside Visual Studio, the visualizer displays the syntax tree
for the currently active code file, (the code file that has keyboard focus.)
C#
As shown in the preceding images, the visualizer tool window displays the syntax tree at
the top and a property grid at the bottom. The property grid displays the properties of
the item that is currently selected in the tree, including the .NET Type and the Kind
(SyntaxKind) of the item.
Syntax trees comprise three types of items – nodes, tokens, and trivia. You can read more
about these types in the Work with syntax article. Items of each type are represented
using a different color. Click on the ‘Legend’ button for an overview of the colors used.
Each item in the tree also displays its own span. The span is the indices (the starting and
ending position) of that node in the text file. In the preceding C# example, the selected
“UsingKeyword [0..5)” token has a Span that is five characters wide, [0..5). The "[..)"
notation means that the starting index is part of the span, but the ending index is not.
Expand or click on items in the tree. The visualizer automatically selects the text
corresponding to this item’s span in the code editor.
Click or select text in the code editor. In the preceding Visual Basic example, if you
select the line containing "Module Module1" in the code editor, the visualizer
automatically navigates to the corresponding ModuleStatement node in the tree.
The visualizer highlights the item in the tree whose span best matches the span of the
text selected in the editor.
The visualizer refreshes the tree to match modifications in the active code file. Add a call
to Console.WriteLine() inside Main() . As you type, the visualizer refreshes the tree.
Pause typing once you have typed Console. . The tree has some items colored in pink. At
this point, there are errors (also referred to as ‘Diagnostics’) in the typed code. These
errors are attached to nodes, tokens, and trivia in the syntax tree. The visualizer shows
you which items have errors attached to them highlighting the background in pink. You
can inspect the errors on any item colored pink by hovering over the item. The visualizer
only displays syntactic errors (those errors related to the syntax of the typed code); it
doesn't display any semantic errors.
Syntax Graphs
Right click on any item in the tree and click on View Directed Syntax Graph.
C#
The syntax graph viewer has an option to display a legend for its coloring scheme. You
can also hover over individual items in the syntax graph with the mouse to view the
properties corresponding to that item.
You can view syntax graphs for different items in the tree repeatedly and the graphs will
always be displayed in the same window inside Visual Studio. You can dock this window
at a convenient location inside Visual Studio so that you don’t have to switch between
tabs to view a new syntax graph. The bottom, below code editor windows, is often
convenient.
Here is the docking layout to use with the visualizer tool window and the syntax graph
window:
Another option is to put the syntax graph window on a second monitor, in a dual
monitor setup.
Inspecting semantics
The Syntax Visualizer enables rudimentary inspection of symbols and semantic
information. Type double x = 1 + 1; inside Main() in the C# example. Then, select the
expression 1 + 1 in the code editor window. The visualizer highlights the
AddExpression node in the visualizer. Right click on this AddExpression and click on
View Symbol (if any). Notice that most of the menu items have the "if any" qualifier. The
Syntax Visualizer inspects properties of a Node, including properties that may not be
present for all nodes.
The property grid in the visualizer updates as shown in the following figure: The symbol
for the expression is a SynthesizedIntrinsicOperatorSymbol with Kind = Method.
Try View TypeSymbol (if any) for the same AddExpression node. The property grid in
the visualizer updates as shown in the following figure, indicating that the type of the
selected expression is Int32 .
Try View Converted TypeSymbol (if any) for the same AddExpression node. The
property grid updates indicating that although the type of the expression is Int32 , the
converted type of the expression is Double as shown in the following figure. This node
includes converted type symbol information because the Int32 expression occurs in a
context where it must be converted to a Double . This conversion satisfies the Double
type specified for the variable x on the left-hand side of the assignment operator.
Finally, try View Constant Value (if any) for the same AddExpression node. The property
grid shows that the value of the expression is a compile time constant with value 2 .
The preceding example can also be replicated in Visual Basic. Type Dim x As Double = 1
+ 1 in a Visual Basic file. Select the expression 1 + 1 in the code editor window. The
visualizer highlights the corresponding AddExpression node in the visualizer. Repeat the
preceding steps for this AddExpression and you should see identical results.
Examine more code in Visual Basic. Update your main Visual Basic file with the following
code:
VB
Imports C = System.Console
Module Program
Sub Main(args As String())
C.WriteLine()
End Sub
End Module
This code introduces an alias named C that maps to the type System.Console at the top
of the file and uses this alias inside Main() . Select the use of this alias, the C in
C.WriteLine() , inside the Main() method. The visualizer selects the corresponding
IdentifierName node in the visualizer. Right-click this node and click on View Symbol (if
any). The property grid indicates that this identifier is bound to the type System.Console
as shown in the following figure:
Try View AliasSymbol (if any) for the same IdentifierName node. The property grid
indicates the identifier is an alias with name C that is bound to the System.Console
target. In other words, the property grid provides information regarding the
AliasSymbol corresponding to the identifier C .
Inspect the symbol corresponding to any declared type, method, property. Select the
corresponding node in the visualizer and click on View Symbol (if any). Select the
method Sub Main() , including the body of the method. Click on View Symbol (if any)
for the corresponding SubBlock node in the visualizer. The property grid shows the
MethodSymbol for this SubBlock has name Main with return type Void .
The above Visual Basic examples can be easily replicated in C#. Type using C =
System.Console; in place of Imports C = System.Console for the alias. The preceding
steps in C# yield identical results in the visualizer window.
Semantic inspection operations are only available on nodes. They are not available on
tokens or trivia. Not all nodes have interesting semantic information to inspect. When a
node doesn't have interesting semantic information, clicking on View * Symbol (if any)
shows a blank property grid.
You can read more about APIs for performing semantic analysis in the Work with
semantics overview document.
This article provides an overview of Source Generators that ships as part of the .NET
Compiler Platform ("Roslyn") SDK. Source Generators let C# developers inspect user
code as it is being compiled. The generator can create new C# source files on the fly
that are added to the user's compilation. In this way, you have code that runs during
compilation. It inspects your program to produce additional source files that are
compiled together with the rest of your code.
A Source Generator is a new kind of component that C# developers can write that lets
you do two major things:
1. Retrieve a compilation object that represents all user code that is being compiled.
This object can be inspected, and you can write code that works with the syntax
and semantic models for the code being compiled, just like with analyzers today.
When combined, these two things are what make Source Generators so useful. You can
inspect user code with all of the rich metadata that the compiler builds up during
compilation. Your generator then emits C# code back into the same compilation that is
based on the data you've analyzed. If you're familiar with Roslyn Analyzers, you can
think of Source Generators as analyzers that can emit C# source code.
A Source Generator is a .NET Standard 2.0 assembly that is loaded by the compiler along
with any analyzers. It's usable in environments where .NET Standard components can be
loaded and run.
) Important
Currently only .NET Standard 2.0 assemblies can be used as Source Generators.
Common scenarios
There are three general approaches to inspecting user code and generating information
or code based on that analysis used by technologies today:
Runtime reflection.
Juggling MSBuild tasks.
Intermediate Language (IL) weaving (not discussed in this article).
Runtime reflection
Runtime reflection is a powerful technology that was added to .NET a long time ago.
There are countless scenarios for using it. A common scenario is to perform some
analysis of user code when an app starts up and use that data to generate things.
For example, ASP.NET Core uses reflection when your web service first runs to discover
constructs you've defined so that it can "wire up" things like controllers and razor pages.
Although this enables you to write straightforward code with powerful abstractions, it
comes with a performance penalty at run time: when your web service or app first starts
up, it can’t accept any requests until all the runtime reflection code that discovers
information about your code is finished running. Although this performance penalty
isn't enormous, it's somewhat of a fixed cost that you can’t improve yourself in your own
app.
With a Source Generator, the controller discovery phase of startup could instead happen
at compile time. A generator can analyze your source code and emit the code it needs
to "wire up" your app. Using source generators could result in some faster startup times,
since an action happening at run time today could get pushed into compile time.
Another capability Source Generators can offer is obviating the use of some "stringly
typed" APIs, such as how ASP.NET Core routing between controllers and razor pages
work. With a Source Generator, routing can be strongly typed with the necessary strings
being generated as a compile-time detail. This would reduce the number of times a
mistyped string literal leads to a request not hitting the correct controller.
2. Replace the Program class with the following code. The following code doesn't use
top level statements. The classic form is required because this first source
generator writes a partial method in that Program class:
C#
namespace ConsoleApp;
7 Note
You can run this sample as-is, but nothing will happen yet.
3. Next, we'll create a source generator project that will implement the partial void
HelloFrom method counterpart.
4. Create a .NET standard library project that targets the netstandard2.0 target
framework moniker (TFM). Add the NuGet packages
Microsoft.CodeAnalysis.Analyzers and Microsoft.CodeAnalysis.CSharp:
XML
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp"
Version="4.9.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers"
Version="3.3.4" PrivateAssets="all" />
</ItemGroup>
</Project>
Tip
The source generator project should typically target the netstandard2.0 TFM,
as to support their consumption within .NET Framework environments
including the Visual Studio IDE and MSBuild CLI.
5. Create a new C# file named HelloSourceGenerator.cs that specifies your own Source
Generator like so:
C#
using Microsoft.CodeAnalysis;
namespace SourceGenerator
{
[Generator]
public class HelloSourceGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// Code generation goes here
}
C#
using Microsoft.CodeAnalysis;
namespace SourceGenerator
{
[Generator]
public class HelloSourceGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
// Find the main method
var mainMethod =
context.Compilation.GetEntryPoint(context.CancellationToken);
namespace {mainMethod.ContainingNamespace.ToDisplayString()}
{{
public static partial class {mainMethod.ContainingType.Name}
{{
static partial void HelloFrom(string name) =>
Console.WriteLine($""Generator says: Hi from '{{name}}'"");
}}
}}
";
var typeName = mainMethod.ContainingType.Name;
From the context object we can access the compilations' entry point, or Main
method. The mainMethod instance is an IMethodSymbol, and it represents a
method or method-like symbol (including constructor, destructor, operator, or
property/event accessor). The Microsoft.CodeAnalysis.Compilation.GetEntryPoint
method returns the IMethodSymbol for the program's entry point. Other methods
enable you to find any method symbol in a project. From this object, we can
reason about the containing namespace (if one is present) and the type. The
source in this example is an interpolated string that templates the source code to
be generated, where the interpolated holes are filled with the containing
namespace and type information. The source is added to the context with a hint
name. For this example, the generator creates a new generated source file that
contains an implementation of the partial method in the console application. You
can write source generators to add any source you'd like.
Tip
XML
This new reference isn't a traditional project reference, and has to be manually
edited to include the OutputItemType and ReferenceOutputAssembly attributes. For
more information on the OutputItemType and ReferenceOutputAssembly attributes
of ProjectReference , see Common MSBuild project items: ProjectReference.
8. Now, when you run the console application, you should see that the generated
code gets run and prints to the screen. The console application itself doesn't
implement the HelloFrom method, instead it's source generated during
compilation from the Source Generator project. The following text is an example
output from the application:
Console
7 Note
You might need to restart Visual Studio to see IntelliSense and get rid of
errors as the tooling experience is actively being improved.
9. If you're using Visual Studio, you can see the source generated files. From the
Solution Explorer window, expand the Dependencies > Analyzers >
SourceGenerator > SourceGenerator.HelloSourceGenerator, and double-click the
Program.g.cs file.
When you open this generated file, Visual Studio will indicate that the file is auto-
generated and that it can't be edited.
10. You can also set build properties to save the generated file and control where the
generated files are stored. In the console application's project file, add the
<EmitCompilerGeneratedFiles> element to a <PropertyGroup> , and set its value to
true . Build your project again. Now, the generated files are created under
obj/Debug/net6.0/generated/SourceGenerator/SourceGenerator.HelloSourceGenerat
or. The components of the path map to the build configuration, target framework,
source generator project name, and fully qualified type name of the generator. You
can choose a more convenient output folder by adding the
<CompilerGeneratedFilesOutputPath> element to the application's project file.
Next steps
The Source Generators Cookbook goes over some of these examples with some
recommended approaches to solving them. Additionally, we have a set of samples
available on GitHub that you can try on your own.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Choose diagnostic IDs
Article • 11/03/2023
A diagnostic ID is the string associated with a given diagnostic, such as a compiler error
or a diagnostic that is produced by an analyzer.
DiagnosticDescriptor.Id
ObsoleteAttribute.DiagnosticId
ExperimentalAttribute.DiagnosticId
Diagnostic IDs are also used as identifiers in source, for example, from #pragma warning
disable or .editorconfig files.
Considerations
Diagnostic IDs should be unique
Diagnostic IDs must be legal identifiers in C#
Diagnostic IDs should be less than 15 characters long
Diagnostic IDs should be of the form <PREFIX><number>
The prefix is specific to your project
The number represents the specific diagnostic
7 Note
Don't limit your prefix to two-characters (such as CSXXX and CAXXXX ). Instead, use a
longer prefix to avoid conflicts. For example, the System.* diagnostics use SYSLIB as
their prefix.
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Get started with syntax analysis
Article • 09/15/2021
In this tutorial, you'll explore the Syntax API. The Syntax API provides access to the data
structures that describe a C# or Visual Basic program. These data structures have
enough detail that they can fully represent any program of any size. These structures
can describe complete programs that compile and run correctly. They can also describe
incomplete programs, as you write them, in the editor.
To enable this rich expression, the data structures and APIs that make up the Syntax API
are necessarily complex. Let's start with what the data structure looks like for the typical
"Hello World" program:
C#
using System;
using System.Collections.Generic;
using System.Linq;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Look at the text of the previous program. You recognize familiar elements. The entire
text represents a single source file, or a compilation unit. The first three lines of that
source file are using directives. The remaining source is contained in a namespace
declaration. The namespace declaration contains a child class declaration. The class
declaration contains one method declaration.
The Syntax API creates a tree structure with the root representing the compilation unit.
Nodes in the tree represent the using directives, namespace declaration and all the
other elements of the program. The tree structure continues down to the lowest levels:
the string "Hello World!" is a string literal token that is a descendent of an argument.
The Syntax API provides access to the structure of the program. You can query for
specific code practices, walk the entire tree to understand the code, and create new
trees by modifying the existing tree.
That brief description provides an overview of the kind of information accessible using
the Syntax API. The Syntax API is nothing more than a formal API that describes the
familiar code constructs you know from C#. The full capabilities include information
about how the code is formatted including line breaks, white space, and indenting.
Using this information, you can fully represent the code as written and read by human
programmers or the compiler. Using this structure enables you to interact with the
source code on a deeply meaningful level. It's no longer text strings, but data that
represents the structure of a C# program.
To get started, you'll need to install the .NET Compiler Platform SDK:
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
1. Check the box for DGML editor. You'll find it under the Code tools section.
A syntax tree is a data structure used by the C# and Visual Basic compilers to
understand C# and Visual Basic programs. Syntax trees are produced by the same parser
that runs when a project is built or a developer hits F5. The syntax trees have full-fidelity
with the language; every bit of information in a code file is represented in the tree.
Writing a syntax tree to text reproduces the exact original text that was parsed. The
syntax trees are also immutable; once created a syntax tree can never be changed.
Consumers of the trees can analyze the trees on multiple threads, without locks or other
concurrency measures, knowing the data never changes. You can use APIs to create new
trees that are the result of modifying an existing tree.
Trivia, tokens, and nodes are composed hierarchically to form a tree that completely
represents everything in a fragment of Visual Basic or C# code. You can see this
structure using the Syntax Visualizer window. In Visual Studio, choose View > Other
Windows > Syntax Visualizer. For example, the preceding C# source file examined
using the Syntax Visualizer looks like the following figure:
SyntaxNode: Blue | SyntaxToken: Green | SyntaxTrivia: Red
By navigating this tree structure, you can find any statement, expression, token, or bit of
white space in a code file.
While you can find anything in a code file using the Syntax APIs, most scenarios involve
examining small snippets of code, or searching for particular statements or fragments.
The two examples that follow show typical uses to browse the structure of code, or
search for single statements.
Traversing trees
You can examine the nodes in a syntax tree in two ways. You can traverse the tree to
examine each node, or you can query for specific elements or nodes.
Manual traversal
You can see the finished code for this sample in our GitHub repository .
7 Note
The Syntax Tree types use inheritance to describe the different syntax elements that
are valid at different locations in the program. Using these APIs often means
casting properties or collection members to specific derived types. In the following
examples, the assignment and the casts are separate statements, using explicitly
typed variables. You can read the code to see the return types of the API and the
runtime type of the objects returned. In practice, it's more common to use implicitly
typed variables and rely on API names to describe the type of objects being
examined.
In Visual Studio, choose File > New > Project to display the New Project dialog.
Under Visual C# > Extensibility, choose Stand-Alone Code Analysis Tool.
Name your project "SyntaxTreeManualTraversal" and click OK.
You're going to analyze the basic "Hello World!" program shown earlier. Add the text for
the Hello World program as a constant in your Program class:
C#
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Hello, World!"");
}
}
}";
Next, add the following code to build the syntax tree for the code text in the
programText constant. Add the following line to your Main method:
C#
Those two lines create the tree and retrieve the root node of that tree. You can now
examine the nodes in the tree. Add these lines to your Main method to display some of
the properties of the root node in the tree:
C#
Run the application to see what your code has discovered about the root node in this
tree.
Typically, you'd traverse the tree to learn about the code. In this example, you're
analyzing code you know to explore the APIs. Add the following code to examine the
first member of the root node:
C#
C#
Microsoft.CodeAnalysis.CSharp.Syntax.MethodDeclarationSyntax.
C#
var programDeclaration =
(ClassDeclarationSyntax)helloWorldDeclaration.Members[0];
WriteLine($"There are {programDeclaration.Members.Count} members declared in
the {programDeclaration.Identifier} class.");
WriteLine($"The first member is a {programDeclaration.Members[0].Kind()}.");
var mainDeclaration =
(MethodDeclarationSyntax)programDeclaration.Members[0];
The method declaration node contains all the syntactic information about the method.
Let's display the return type of the Main method, the number and types of the
arguments, and the body text of the method. Add the following code:
C#
Run the program to see all the information you've discovered about this program:
text
Query methods
In addition to traversing trees, you can also explore the syntax tree using the query
methods defined on Microsoft.CodeAnalysis.SyntaxNode. These methods should be
immediately familiar to anyone familiar with XPath. You can use these methods with
LINQ to quickly find things in a tree. The SyntaxNode has query methods such as
DescendantNodes, AncestorsAndSelf and ChildNodes.
You can use these query methods to find the argument to the Main method as an
alternative to navigating the tree. Add the following code to the bottom of your Main
method:
C#
WriteLine(argsParameter == argsParameter2);
The first statement uses a LINQ expression and the DescendantNodes method to locate
the same parameter as in the previous example.
Run the program, and you can see that the LINQ expression found the same parameter
as manually navigating the tree.
The sample uses WriteLine statements to display information about the syntax trees as
they are traversed. You can also learn much more by running the finished program
under the debugger. You can examine more of the properties and methods that are part
of the syntax tree created for the hello world program.
Syntax walkers
Often you want to find all nodes of a specific type in a syntax tree, for example, every
property declaration in a file. By extending the
Microsoft.CodeAnalysis.CSharp.CSharpSyntaxWalker class and overriding the
VisitPropertyDeclaration(PropertyDeclarationSyntax) method, you process every
property declaration in a syntax tree without knowing its structure beforehand.
CSharpSyntaxWalker is a specific kind of CSharpSyntaxVisitor that recursively visits a
node and each of its children.
You can see the finished code for this sample in our GitHub repository . The sample on
GitHub contains both projects described in this tutorial.
As in the previous sample, you can define a string constant to hold the text of the
program you're going to analyze:
C#
namespace TopLevel
{
using Microsoft;
using System.ComponentModel;
namespace Child1
{
using Microsoft.Win32;
using System.Runtime.InteropServices;
class Foo { }
}
namespace Child2
{
using System.CodeDom;
using Microsoft.CSharp;
class Bar { }
}
}";
This source text contains using directives scattered across four different locations: the
file-level, in the top-level namespace, and in the two nested namespaces. This example
highlights a core scenario for using the CSharpSyntaxWalker class to query code. It
would be cumbersome to visit every node in the root syntax tree to find using
declarations. Instead, you create a derived class and override the method that gets
called only when the current node in the tree is a using directive. Your visitor does not
do any work on any other node types. This single method examines each of the using
directives and builds a collection of the namespaces that aren't in the System
namespace. You build a CSharpSyntaxWalker that examines all the using directives, but
only the using directives.
Now that you've defined the program text, you need to create a SyntaxTree and get the
root of that tree:
C#
Next, create a new class. In Visual Studio, choose Project > Add New Item. In the Add
New Item dialog type UsingCollector.cs as the filename.
You implement the using visitor functionality in the UsingCollector class. Start by
making the UsingCollector class derive from CSharpSyntaxWalker.
C#
You need storage to hold the namespace nodes that you're collecting. Declare a public
read-only property in the UsingCollector class; you use this variable to store the
UsingDirectiveSyntax nodes you find:
C#
C#
As with the earlier example, you've added a variety of WriteLine statements to aid in
understanding of this method. You can see when it's called, and what arguments are
passed to it each time.
Finally, you need to add two lines of code to create the UsingCollector and have it visit
the root node, collecting all the using directives. Then, add a foreach loop to display all
the using directives your collector found:
C#
Compile and run the program. You should see the following output:
Console
VisitUsingDirective called with System.
VisitUsingDirective called with System.Collections.Generic.
VisitUsingDirective called with System.Linq.
VisitUsingDirective called with System.Text.
VisitUsingDirective called with Microsoft.CodeAnalysis.
Success. Adding Microsoft.CodeAnalysis.
VisitUsingDirective called with Microsoft.CodeAnalysis.CSharp.
Success. Adding Microsoft.CodeAnalysis.CSharp.
VisitUsingDirective called with Microsoft.
Success. Adding Microsoft.
VisitUsingDirective called with System.ComponentModel.
VisitUsingDirective called with Microsoft.Win32.
Success. Adding Microsoft.Win32.
VisitUsingDirective called with System.Runtime.InteropServices.
VisitUsingDirective called with System.CodeDom.
VisitUsingDirective called with Microsoft.CSharp.
Success. Adding Microsoft.CSharp.
Microsoft.CodeAnalysis
Microsoft.CodeAnalysis.CSharp
Microsoft
Microsoft.Win32
Microsoft.CSharp
Press any key to continue . . .
Congratulations! You've used the Syntax API to locate specific kinds of directives and
declarations in C# source code.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Get started with semantic analysis
Article • 09/15/2021
This tutorial assumes you're familiar with the Syntax API. The get started with syntax
analysis article provides sufficient introduction.
In this tutorial, you explore the Symbol and Binding APIs. These APIs provide
information about the semantic meaning of a program. They enable you to ask and
answer questions about the types represented by any symbol in your program.
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
1. Check the box for DGML editor. You'll find it under the Code tools section.
Querying symbols
In this tutorial, you look at the "Hello World" program again. This time, you query the
symbols in the program to understand what types those symbols represent. You query
for the types in a namespace, and learn to find the methods available on a type.
You can see the finished code for this sample in our GitHub repository .
7 Note
The Syntax Tree types use inheritance to describe the different syntax elements that
are valid at different locations in the program. Using these APIs often means
casting properties or collection members to specific derived types. In the following
examples, the assignment and the casts are separate statements, using explicitly
typed variables. You can read the code to see the return types of the API and the
runtime type of the objects returned. In practice, it's more common to use implicitly
typed variables and rely on API names to describe the type of objects being
examined.
In Visual Studio, choose File > New > Project to display the New Project dialog.
Under Visual C# > Extensibility, choose Stand-Alone Code Analysis Tool.
Name your project "SemanticQuickStart" and click OK.
You're going to analyze the basic "Hello World!" program shown earlier. Add the text for
the Hello World program as a constant in your Program class:
C#
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Hello, World!"");
}
}
}";
Next, add the following code to build the syntax tree for the code text in the
programText constant. Add the following line to your Main method:
C#
C#
C#
Binding a name
The Compilation creates the SemanticModel from the SyntaxTree. After creating the
model, you can query it to find the first using directive, and retrieve the symbol
information for the System namespace. Add these two lines to your Main method to
create the semantic model and retrieve the symbol for the first using directive:
C#
The preceding code shows how to bind the name in the first using directive to retrieve
a Microsoft.CodeAnalysis.SymbolInfo for the System namespace. The preceding code
also illustrates that you use the syntax model to find the structure of the code; you use
the semantic model to understand its meaning. The syntax model finds the string
System in the using directive. The semantic model has all the information about the
From the SymbolInfo object you can obtain the Microsoft.CodeAnalysis.ISymbol using
the SymbolInfo.Symbol property. This property returns the symbol this expression refers
to. For expressions that don't refer to anything (such as numeric literals) this property is
null . When the SymbolInfo.Symbol is not null, the ISymbol.Kind denotes the type of the
C#
Run the program and you should see the following output:
Output
System.Collections
System.Configuration
System.Deployment
System.Diagnostics
System.Globalization
System.IO
System.Numerics
System.Reflection
System.Resources
System.Runtime
System.Security
System.StubHelpers
System.Text
System.Threading
Press any key to continue . . .
7 Note
The output does not include every namespace that is a child namespace of the
System namespace. It displays every namespace that is present in this compilation,
Binding an expression
The preceding code shows how to find a symbol by binding to a name. There are other
expressions in a C# program that can be bound that aren't names. To demonstrate this
capability, let's access the binding to a simple string literal.
You find the "Hello, World!" string by locating the single string literal in the program.
Then, once you've located the syntax node, get the type info for that node from the
semantic model. Add the following code to your Main method:
C#
C#
C#
That source sequence contains all members, including properties and fields, so filter it
using the ImmutableArray<T>.OfType method to find elements that are
Microsoft.CodeAnalysis.IMethodSymbol objects:
C#
Next, add another filter to return only those methods that are public and return a
string :
C#
Select only the name property, and only distinct names by removing any overloads:
C#
You can also build the full query using the LINQ query syntax, and then display all the
method names in the console:
C#
Build and run the program. You should see the following output:
Output
Join
Substring
Trim
TrimStart
TrimEnd
Normalize
PadLeft
PadRight
ToLower
ToLowerInvariant
ToUpper
ToUpperInvariant
ToString
Insert
Replace
Remove
Format
Copy
Concat
Intern
IsInterned
Press any key to continue . . .
You've used the Semantic API to find and display information about the symbols that
are part of this program.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Get started with syntax transformation
Article • 09/15/2021
This tutorial builds on concepts and techniques explored in the Get started with syntax
analysis and Get started with semantic analysis quickstarts. If you haven't already, you
should complete those quickstarts before beginning this one.
In this quickstart, you explore techniques for creating and transforming syntax trees. In
combination with the techniques you learned in previous quickstarts, you create your
first command-line refactoring!
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
1. Check the box for DGML editor. You'll find it under the Code tools section.
Start Visual Studio, and create a new C# Stand-Alone Code Analysis Tool project. In
Visual Studio, choose File > New > Project to display the New Project dialog. Under
Visual C# > Extensibility choose a Stand-Alone Code Analysis Tool. This quickstart has
two example projects, so name the solution SyntaxTransformationQuickStart, and name
the project ConstructionCS. Click OK.
C#
You'll create name syntax nodes to build the tree that represents the using
System.Collections.Generic; statement. NameSyntax is the base class for four types of
names that appear in C#. You compose these four types of names together to create
any name that can appear in the C# language:
You use the IdentifierName(String) method to create a NameSyntax node. Add the
following code in your Main method in Program.cs :
C#
C#
Run the code again, and see the results. You're building a tree of nodes that represents
code. You'll continue this pattern to build the QualifiedNameSyntax for the namespace
System.Collections.Generic . Add the following code to Program.cs :
C#
Run the program again to see that you've built the tree for the code to add.
The next step is to create a tree that represents an entire (small) program and then
modify it. Add the following code to the beginning of the Program class:
C#
private const string sampleCode =
@"using System;
using System.Collections;
using System.Linq;
using System.Text;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Hello, World!"");
}
}
}";
7 Note
The example code uses the System.Collections namespace and not the
System.Collections.Generic namespace.
Next, add the following code to the bottom of the Main method to parse the text and
create a tree:
C#
C#
Run the program and look carefully at the output. The newUsing hasn't been placed in
the root tree. The original tree hasn't been changed.
Add the following code using the ReplaceNode extension method to create a new tree.
The new tree is the result of replacing the existing import with the updated newUsing
node. You assign this new tree to the existing root variable:
C#
Run the program again. This time the tree now correctly imports the
System.Collections.Generic namespace.
Create a new C# Stand-Alone Code Analysis Tool project. In Visual Studio, right-click
the SyntaxTransformationQuickStart solution node. Choose Add > New Project to
display the New Project dialog. Under Visual C# > Extensibility, choose Stand-Alone
Code Analysis Tool. Name your project TransformationCS and click OK.
The first step is to create a class that derives from CSharpSyntaxRewriter to perform your
transformations. Add a new class file to the project. In Visual Studio, choose Project >
Add Class.... In the Add New Item dialog type TypeInferenceRewriter.cs as the
filename.
C#
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
C#
Add the following code to declare a private read-only field to hold a SemanticModel
and initialize it in the constructor. You will need this field later on to determine where
type inference can be used:
C#
C#
7 Note
Many of the Roslyn APIs declare return types that are base classes of the actual
runtime types returned. In many scenarios, one kind of node may be replaced by
another kind of node entirely - or even removed. In this example, the
VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax) method
returns a SyntaxNode, instead of the derived type of
LocalDeclarationStatementSyntax. This rewriter returns a new
LocalDeclarationStatementSyntax node based on the existing one.
This quickstart handles local variable declarations. You could extend it to other
declarations such as foreach loops, for loops, LINQ expressions, and lambda
expressions. Furthermore this rewriter will only transform declarations of the simplest
form:
C#
If you want to explore on your own, consider extending the finished sample for these
types of variable declarations:
C#
C#
if (node.Declaration.Variables.Count > 1)
{
return node;
}
if (node.Declaration.Variables[0].Initializer == null)
{
return node;
}
The method indicates that no rewriting takes place by returning the node parameter
unmodified. If neither of those if expressions are true, the node represents a possible
declaration with initialization. Add these statements to extract the type name specified
in the declaration and bind it using the SemanticModel field to obtain a type symbol:
C#
C#
var initializerInfo =
SemanticModel.GetTypeInfo(declarator.Initializer.Value);
Finally, add the following if statement to replace the existing type name with the var
keyword if the type of the initializer expression matches the type specified:
C#
if (SymbolEqualityComparer.Default.Equals(variableType,
initializerInfo.Type))
{
TypeSyntax varTypeName = SyntaxFactory.IdentifierName("var")
.WithLeadingTrivia(variableTypeName.GetLeadingTrivia())
.WithTrailingTrivia(variableTypeName.GetTrailingTrivia());
The conditional is required because the declaration may cast the initializer expression to
a base class or interface. If that's desired, the types on the left and right-hand side of the
assignment don't match. Removing the explicit type in these cases would change the
semantics of a program. var is specified as an identifier rather than a keyword because
var is a contextual keyword. The leading and trailing trivia (white space) are transferred
from the old type name to the var keyword to maintain vertical white space and
indentation. It's simpler to use ReplaceNode rather than With* to transform the
LocalDeclarationStatementSyntax because the type name is actually the grandchild of
the declaration statement.
You've finished the TypeInferenceRewriter . Now return to your Program.cs file to finish
the example. Create a test Compilation and obtain the SemanticModel from it. Use that
SemanticModel to try your TypeInferenceRewriter . You'll do this step last. In the
meantime declare a placeholder variable representing your test compilation:
C#
After pausing a moment, you should see an error squiggle appear reporting that no
CreateTestCompilation method exists. Press Ctrl+Period to open the light-bulb and
then press Enter to invoke the Generate Method Stub command. This command will
generate a method stub for the CreateTestCompilation method in the Program class.
You'll come back to fill in this method later:
Write the following code to iterate over each SyntaxTree in the test Compilation. For
each one, initialize a new TypeInferenceRewriter with the SemanticModel for that tree:
C#
if (newSource != sourceTree.GetRoot())
{
File.WriteAllText(sourceTree.FilePath, newSource.ToFullString());
}
}
Inside the foreach statement you created, add the following code to perform the
transformation on each source tree. This code conditionally writes out the new
transformed tree if any edits were made. Your rewriter should only modify a tree if it
encounters one or more local variable declarations that could be simplified using type
inference:
C#
SyntaxNode newSource = rewriter.Visit(sourceTree.GetRoot());
if (newSource != sourceTree.GetRoot())
{
File.WriteAllText(sourceTree.FilePath, newSource.ToFullString());
}
You should see squiggles under the File.WriteAllText code. Select the light bulb, and
add the necessary using System.IO; statement.
You're almost done! There's one step left: creating a test Compilation. Since you haven't
been using type inference at all during this quickstart, it would have made a perfect test
case. Unfortunately, creating a Compilation from a C# project file is beyond the scope of
this walkthrough. But fortunately, if you've been following instructions carefully, there's
hope. Replace the contents of the CreateTestCompilation method with the following
code. It creates a test compilation that coincidentally matches the project described in
this quickstart:
C#
MetadataReference mscorlib =
MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
MetadataReference codeAnalysis =
MetadataReference.CreateFromFile(typeof(SyntaxTree).Assembly.Location);
MetadataReference csharpCodeAnalysis =
MetadataReference.CreateFromFile(typeof(CSharpSyntaxTree).Assembly.Location)
;
return CSharpCompilation.Create("TransformationCS",
sourceTrees,
references,
new CSharpCompilationOptions(OutputKind.ConsoleApplication));
Cross your fingers and run the project. In Visual Studio, choose Debug > Start
Debugging. You should be prompted by Visual Studio that the files in your project have
changed. Click "Yes to All" to reload the modified files. Examine them to observe your
awesomeness. Note how much cleaner the code looks without all those explicit and
redundant type specifiers.
Congratulations! You've used the Compiler APIs to write your own refactoring that
searches all files in a C# project for certain syntactic patterns, analyzes the semantics of
source code that matches those patterns, and transforms it. You're now officially a
refactoring author!
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Tutorial: Write your first analyzer and
code fix
Article • 02/04/2022
The .NET Compiler Platform SDK provides the tools you need to create custom
diagnostics (analyzers), code fixes, code refactoring, and diagnostic suppressors that
target C# or Visual Basic code. An analyzer contains code that recognizes violations of
your rule. Your code fix contains the code that fixes the violation. The rules you
implement can be anything from code structure to coding style to naming conventions
and more. The .NET Compiler Platform provides the framework for running analysis as
developers are writing code, and all the Visual Studio UI features for fixing code:
showing squiggles in the editor, populating the Visual Studio Error List, creating the
"light bulb" suggestions and showing the rich preview of the suggested fixes.
In this tutorial, you'll explore the creation of an analyzer and an accompanying code fix
using the Roslyn APIs. An analyzer is a way to perform source code analysis and report a
problem to the user. Optionally, a code fix can be associated with the analyzer to
represent a modification to the user's source code. This tutorial creates an analyzer that
finds local variable declarations that could be declared using the const modifier but are
not. The accompanying code fix modifies those declarations to add the const modifier.
Prerequisites
Visual Studio 2019 version 16.8 or later
You'll need to install the .NET Compiler Platform SDK via the Visual Studio Installer:
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
Optionally, you'll also want the DGML editor to display graphs in the visualizer:
1. Check the box for DGML editor. You'll find it under the Code tools section.
7 Note
7 Note
Analyzers should target .NET Standard 2.0 because they can run in .NET Core
environment (command line builds) and .NET Framework environment (Visual
Studio).
Tip
When you run your analyzer, you start a second copy of Visual Studio. This second
copy uses a different registry hive to store settings. That enables you to
differentiate the visual settings in the two copies of Visual Studio. You can pick a
different theme for the experimental run of Visual Studio. In addition, don't roam
your settings or login to your Visual Studio account using the experimental run of
Visual Studio. That keeps the settings different.
The hive includes not only the analyzer under development, but also any previous
analyzers opened. To reset Roslyn hive, you need to manually delete it from
%LocalAppData%\Microsoft\VisualStudio. The folder name of Roslyn hive will end in
Roslyn , for example, 16.0_9ae182f9Roslyn . Note that you may need to clean the
In the second Visual Studio instance that you just started, create a new C# Console
Application project (any target framework will work -- analyzers work at the source
level.) Hover over the token with a wavy underline, and the warning text provided by an
analyzer appears.
The template creates an analyzer that reports a warning on each type declaration where
the type name contains lowercase letters, as shown in the following figure:
The template also provides a code fix that changes any type name containing lower case
characters to all upper case. You can click on the light bulb displayed with the warning
to see the suggested changes. Accepting the suggested changes updates the type name
and all references to that type in the solution. Now that you've seen the initial analyzer
in action, close the second Visual Studio instance and return to your analyzer project.
You don't have to start a second copy of Visual Studio and create new code to test every
change in your analyzer. The template also creates a unit test project for you. That
project contains two tests. TestMethod1 shows the typical format of a test that analyzes
code without triggering a diagnostic. TestMethod2 shows the format of a test that
triggers a diagnostic, and then applies a suggested code fix. As you build your analyzer
and code fix, you'll write tests for different code structures to verify your work. Unit tests
for analyzers are much quicker than testing them interactively with Visual Studio.
Tip
Analyzer unit tests are a great tool when you know what code constructs should
and shouldn't trigger your analyzer. Loading your analyzer in another copy of
Visual Studio is a great tool to explore and find constructs you may not have
thought about yet.
In this tutorial, you write an analyzer that reports to the user any local variable
declarations that can be converted to local constants. For example, consider the
following code:
C#
int x = 0;
Console.WriteLine(x);
In the code above, x is assigned a constant value and is never modified. It can be
declared using the const modifier:
C#
const int x = 0;
Console.WriteLine(x);
The template also shows the basic features that are part of any analyzer:
1. Register actions. The actions represent code changes that should trigger your
analyzer to examine code for violations. When Visual Studio detects code edits that
match a registered action, it calls your analyzer's registered method.
2. Create diagnostics. When your analyzer detects a violation, it creates a diagnostic
object that Visual Studio uses to notify the user of the violation.
The first step is to update the registration constants and Initialize method so these
constants indicate your "Make Const" analyzer. Most of the string constants are defined
in the string resource file. You should follow that practice for easier localization. Open
the Resources.resx file for the MakeConst analyzer project. This displays the resource
editor. Update the string resources as follows:
When you have finished, the resource editor should appear as shown in the following
figure:
The remaining changes are in the analyzer file. Open MakeConstAnalyzer.cs in Visual
Studio. Change the registered action from one that acts on symbols to one that acts on
syntax. In the MakeConstAnalyzerAnalyzer.Initialize method, find the line that registers
the action on symbols:
C#
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
C#
context.RegisterSyntaxNodeAction(AnalyzeNode,
SyntaxKind.LocalDeclarationStatement);
After that change, you can delete the AnalyzeSymbol method. This analyzer examines
SyntaxKind.LocalDeclarationStatement, not SymbolKind.NamedType statements. Notice
that AnalyzeNode has red squiggles under it. The code you just added references an
AnalyzeNode method that hasn't been declared. Declare that method using the following
code:
C#
C#
C#
int x = 0;
Console.WriteLine(x);
The first step is to find local declarations. Add the following code to AnalyzeNode in
MakeConstAnalyzer.cs:
C#
This cast always succeeds because your analyzer registered for changes to local
declarations, and only local declarations. No other node type triggers a call to your
AnalyzeNode method. Next, check the declaration for any const modifiers. If you find
them, return immediately. The following code looks for any const modifiers on the local
declaration:
C#
Finally, you need to check that the variable could be const . That means making sure it is
never assigned after it is initialized.
You'll perform some semantic analysis using the SyntaxNodeAnalysisContext. You use
the context argument to determine whether the local variable declaration can be made
const . A Microsoft.CodeAnalysis.SemanticModel represents all of semantic information
in a single source file. You can learn more in the article that covers semantic models.
You'll use the Microsoft.CodeAnalysis.SemanticModel to perform data flow analysis on
the local declaration statement. Then, you use the results of this data flow analysis to
ensure that the local variable isn't written with a new value anywhere else. Call the
GetDeclaredSymbol extension method to retrieve the ILocalSymbol for the variable and
check that it isn't contained with the DataFlowAnalysis.WrittenOutside collection of the
data flow analysis. Add the following code to the end of the AnalyzeNode method:
C#
// Retrieve the local symbol for each variable in the local declaration
// and ensure that it is not written outside of the data flow analysis
region.
VariableDeclaratorSyntax variable =
localDeclaration.Declaration.Variables.Single();
ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable,
context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
The code just added ensures that the variable isn't modified, and can therefore be made
const . It's time to raise the diagnostic. Add the following code as the last line in
AnalyzeNode :
C#
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(),
localDeclaration.Declaration.Variables.First().Identifier.ValueText));
You can check your progress by pressing F5 to run your analyzer. You can load the
console application you created earlier and then add the following test code:
C#
int x = 0;
Console.WriteLine(x);
The light bulb should appear, and your analyzer should report a diagnostic. However,
depending on your version of Visual Studio, you'll either see:
The light bulb, which still uses the template generated code fix, will tell you it can
be made upper case.
A banner message at the top of the editor saying the 'MakeConstCodeFixProvider'
encountered an error and has been disabled.'. This is because the code fix provider
hasn't yet been changed and still expects to find TypeDeclarationSyntax elements
instead of LocalDeclarationStatementSyntax elements.
diff
- int x = 0;
+ const int x = 0;
Console.WriteLine(x);
The user chooses it from the light bulb UI in the editor and Visual Studio changes the
code.
Open the MakeConstCodeFixProvider.cs file added by the template. This code fix is
already wired up to the Diagnostic ID produced by your diagnostic analyzer, but it
doesn't yet implement the right code transform.
All code fix providers derive from CodeFixProvider. They all override
CodeFixProvider.RegisterCodeFixesAsync(CodeFixContext) to report available code fixes.
In RegisterCodeFixesAsync , change the ancestor node type you're searching for to a
LocalDeclarationStatementSyntax to match the diagnostic:
C#
var declaration =
root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<LocalD
eclarationStatementSyntax>().First();
Next, change the last line to register a code fix. Your fix will create a new document that
results from adding the const modifier to an existing declaration:
C#
You'll notice red squiggles in the code you just added on the symbol MakeConstAsync .
Add a declaration for MakeConstAsync like the following code:
C#
Your new MakeConstAsync method will transform the Document representing the user's
source file into a new Document that now contains a const declaration.
You create a new const keyword token to insert at the front of the declaration
statement. Be careful to first remove any leading trivia from the first token of the
declaration statement and attach it to the const token. Add the following code to the
MakeConstAsync method:
C#
Next, add the const token to the declaration using the following code:
C#
// Insert the const token into the modifiers list, creating a new modifiers
list.
SyntaxTokenList newModifiers = trimmedLocal.Modifiers.Insert(0, constToken);
// Produce the new local declaration.
LocalDeclarationStatementSyntax newLocal = trimmedLocal
.WithModifiers(newModifiers)
.WithDeclaration(localDeclaration.Declaration);
Next, format the new declaration to match C# formatting rules. Formatting your
changes to match existing code creates a better experience. Add the following
statement immediately after the existing code:
C#
A new namespace is required for this code. Add the following using directive to the top
of the file:
C#
using Microsoft.CodeAnalysis.Formatting;
The final step is to make your edit. There are three steps to this process:
C#
// Replace the old local declaration with the new local declaration.
SyntaxNode oldRoot = await
document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode newRoot = oldRoot.ReplaceNode(localDeclaration, formattedLocal);
Your code fix is ready to try. Press F5 to run the analyzer project in a second instance of
Visual Studio. In the second Visual Studio instance, create a new C# Console Application
project and add a few local variable declarations initialized with constant values to the
Main method. You'll see that they are reported as warnings as below.
You've made a lot of progress. There are squiggles under the declarations that can be
made const . But there is still work to do. This works fine if you add const to the
declarations starting with i , then j and finally k . But, if you add the const modifier in
a different order, starting with k , your analyzer creates errors: k can't be declared
const , unless i and j are both already const . You've got to do more analysis to ensure
you handle the different ways variables can be declared and initialized.
Open the MakeConstUnitTests.cs file in the unit test project. The template created two
tests that follow the two common patterns for an analyzer and code fix unit test.
TestMethod1 shows the pattern for a test that ensures the analyzer doesn't report a
diagnostic when it shouldn't. TestMethod2 shows the pattern for reporting a diagnostic
and running the code fix.
Tip
The testing library supports a special markup syntax, including the following:
[|text|] : indicates that a diagnostic is reported for text . By default, this
form may only be used for testing analyzers with exactly one
DiagnosticDescriptor provided by DiagnosticAnalyzer.SupportedDiagnostics .
Replace the template tests in the MakeConstUnitTest class with the following test
method:
C#
[TestMethod]
public async Task LocalIntCouldBeConstant_Diagnostic()
{
await VerifyCS.VerifyCodeFixAsync(@"
using System;
class Program
{
static void Main()
{
[|int i = 0;|]
Console.WriteLine(i);
}
}
", @"
using System;
class Program
{
static void Main()
{
const int i = 0;
Console.WriteLine(i);
}
}
");
}
Run this test to make sure it passes. In Visual Studio, open the Test Explorer by selecting
Test > Windows > Test Explorer. Then select Run All.
C#
[TestMethod]
public async Task VariableIsAssigned_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int i = 0;
Console.WriteLine(i++);
}
}
");
}
This test passes as well. Next, add test methods for conditions you haven't handled yet:
Declarations that are already const , because they are already const:
C#
[TestMethod]
public async Task VariableIsAlreadyConst_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
const int i = 0;
Console.WriteLine(i);
}
}
");
}
C#
[TestMethod]
public async Task NoInitializer_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int i;
i = 0;
Console.WriteLine(i);
}
}
");
}
Declarations where the initializer is not a constant, because they can't be compile-
time constants:
C#
[TestMethod]
public async Task InitializerIsNotConstant_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int i = DateTime.Now.DayOfYear;
Console.WriteLine(i);
}
}
");
}
C#
[TestMethod]
public async Task MultipleInitializers_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int i = 0, j = DateTime.Now.DayOfYear;
Console.WriteLine(i);
Console.WriteLine(j);
}
}
");
}
The variable i can be made constant, but the variable j cannot. Therefore, this
statement cannot be made a const declaration.
Run your tests again, and you'll see these new test cases fail.
Your semantic analysis examined a single variable declaration. This code needs to
be in a foreach loop that examines all the variables declared in the same
statement.
Each declared variable needs to have an initializer.
Each declared variable's initializer must be a compile-time constant.
C#
// Retrieve the local symbol for each variable in the local declaration
// and ensure that it is not written outside of the data flow analysis
region.
VariableDeclaratorSyntax variable =
localDeclaration.Declaration.Variables.Single();
ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable,
context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
C#
// Ensure that all variables in the local declaration have initializers that
// are assigned with constant values.
foreach (VariableDeclaratorSyntax variable in
localDeclaration.Declaration.Variables)
{
EqualsValueClauseSyntax initializer = variable.Initializer;
if (initializer == null)
{
return;
}
Optional<object> constantValue =
context.SemanticModel.GetConstantValue(initializer.Value,
context.CancellationToken);
if (!constantValue.HasValue)
{
return;
}
}
The first foreach loop examines each variable declaration using syntactic analysis. The
first check guarantees that the variable has an initializer. The second check guarantees
that the initializer is a constant. The second loop has the original semantic analysis. The
semantic checks are in a separate loop because it has a greater impact on performance.
Run your tests again, and you should see them all pass.
C#
[TestMethod]
public async Task DeclarationIsInvalid_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int x = {|CS0029:""abc""|};
}
}
");
}
In addition, reference types are not handled properly. The only constant value allowed
for a reference type is null , except in the case of System.String, which allows string
literals. In other words, const string s = "abc" is legal, but const object s = "abc" is
not. This code snippet verifies that condition:
C#
[TestMethod]
public async Task DeclarationIsNotString_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
object s = ""abc"";
}
}
");
}
To be thorough, you need to add another test to make sure that you can create a
constant declaration for a string. The following snippet defines both the code that raises
the diagnostic, and the code after the fix has been applied:
C#
[TestMethod]
public async Task StringCouldBeConstant_Diagnostic()
{
await VerifyCS.VerifyCodeFixAsync(@"
using System;
class Program
{
static void Main()
{
[|string s = ""abc"";|]
}
}
", @"
using System;
class Program
{
static void Main()
{
const string s = ""abc"";
}
}
");
}
Finally, if a variable is declared with the var keyword, the code fix does the wrong thing
and generates a const var declaration, which is not supported by the C# language. To
fix this bug, the code fix must replace the var keyword with the inferred type's name:
C#
[TestMethod]
public async Task VarIntDeclarationCouldBeConstant_Diagnostic()
{
await VerifyCS.VerifyCodeFixAsync(@"
using System;
class Program
{
static void Main()
{
[|var item = 4;|]
}
}
", @"
using System;
class Program
{
static void Main()
{
const int item = 4;
}
}
");
}
[TestMethod]
public async Task VarStringDeclarationCouldBeConstant_Diagnostic()
{
await VerifyCS.VerifyCodeFixAsync(@"
using System;
class Program
{
static void Main()
{
[|var item = ""abc"";|]
}
}
", @"
using System;
class Program
{
static void Main()
{
const string item = ""abc"";
}
}
");
}
Fortunately, all of the above bugs can be addressed using the same techniques that you
just learned.
To fix the first bug, first open MakeConstAnalyzer.cs and locate the foreach loop where
each of the local declaration's initializers are checked to ensure that they're assigned
with constant values. Immediately before the first foreach loop, call
context.SemanticModel.GetTypeInfo() to retrieve detailed information about the
C#
Then, inside your foreach loop, check each initializer to make sure it's convertible to the
variable type. Add the following check after ensuring that the initializer is a constant:
C#
// Ensure that the initializer value can be converted to the type of the
// local declaration without a user-defined conversion.
Conversion conversion =
context.SemanticModel.ClassifyConversion(initializer.Value, variableType);
if (!conversion.Exists || conversion.IsUserDefined)
{
return;
}
The next change builds upon the last one. Before the closing curly brace of the first
foreach loop, add the following code to check the type of the local declaration when the
constant is a string or null.
C#
// Special cases:
// * If the constant value is a string, the type of the local declaration
// must be System.String.
// * If the constant value is null, the type of the local declaration must
// be a reference type.
if (constantValue.Value is string)
{
if (variableType.SpecialType != SpecialType.System_String)
{
return;
}
}
else if (variableType.IsReferenceType && constantValue.Value != null)
{
return;
}
You must write a bit more code in your code fix provider to replace the var keyword
with the correct type name. Return to MakeConstCodeFixProvider.cs. The code you'll add
does the following steps:
That sounds like a lot of code. It's not. Replace the line that declares and initializes
newLocal with the following code. It goes immediately after the initialization of
newModifiers :
C#
You'll need to add one using directive to use the Simplifier type:
C#
using Microsoft.CodeAnalysis.Simplification;
Run your tests, and they should all pass. Congratulate yourself by running your finished
analyzer. Press Ctrl + F5 to run the analyzer project in a second instance of Visual
Studio with the Roslyn Preview extension loaded.
In the second Visual Studio instance, create a new C# Console Application project
and add int x = "abc"; to the Main method. Thanks to the first bug fix, no
warning should be reported for this local variable declaration (though there's a
compiler error as expected).
Next, add object s = "abc"; to the Main method. Because of the second bug fix,
no warning should be reported.
Finally, add another local variable that uses the var keyword. You'll see that a
warning is reported and a suggestion appears beneath to the left.
Move the editor caret over the squiggly underline and press Ctrl + . . to display
the suggested code fix. Upon selecting your code fix, note that the var keyword is
now handled correctly.
C#
int i = 2;
int j = 32;
int k = i + j;
After these changes, you get red squiggles only on the first two variables. Add const to
both i and j , and you get a new warning on k because it can now be const .
Congratulations! You've created your first .NET Compiler Platform extension that
performs on-the-fly code analysis to detect an issue and provides a quick fix to correct
it. Along the way, you've learned many of the code APIs that are part of the .NET
Compiler Platform SDK (Roslyn APIs). You can check your work against the completed
sample in our samples GitHub repository.
Other resources
Get started with syntax analysis
Get started with semantic analysis
Programming concepts (C#)
Article • 04/25/2024
In this section
ノ Expand table
Title Description
Iterators (C#) Describes iterators, which are used to step through collections and
return elements one at a time.
Related sections
Performance Tips
Discusses several basic rules that might help you increase the performance of your
application.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Covariance and Contravariance (C#)
Article • 07/30/2022
In C#, covariance and contravariance enable implicit reference conversion for array
types, delegate types, and generic type arguments. Covariance preserves assignment
compatibility and contravariance reverses it.
C#
// Assignment compatibility.
string str = "test";
// An object of a more derived type is assigned to an object of a less
derived type.
object obj = str;
// Covariance.
IEnumerable<string> strings = new List<string>();
// An object that is instantiated with a more derived type argument
// is assigned to an object instantiated with a less derived type argument.
// Assignment compatibility is preserved.
IEnumerable<object> objects = strings;
// Contravariance.
// Assume that the following method is in the class:
static void SetObject(object o) { }
Action<object> actObject = SetObject;
// An object that is instantiated with a less derived type argument
// is assigned to an object instantiated with a more derived type argument.
// Assignment compatibility is reversed.
Action<string> actString = actObject;
Covariance for arrays enables implicit conversion of an array of a more derived type to
an array of a less derived type. But this operation is not type safe, as shown in the
following code example.
C#
Covariance and contravariance support for method groups allows for matching method
signatures with delegate types. This enables you to assign to delegates not only
methods that have matching signatures, but also methods that return more derived
types (covariance) or that accept parameters that have less derived types
(contravariance) than that specified by the delegate type. For more information, see
Variance in Delegates (C#) and Using Variance in Delegates (C#).
The following code example shows covariance and contravariance support for method
groups.
C#
The following code example shows implicit reference conversion for generic interfaces.
C#
A generic interface or delegate is called variant if its generic parameters are declared
covariant or contravariant. C# enables you to create your own variant interfaces and
delegates. For more information, see Creating Variant Generic Interfaces (C#) and
Variance in Delegates (C#).
Related Topics
ノ Expand table
Title Description
Using Variance in Interfaces Shows how covariance and contravariance support in the
for Generic Collections (C#) IEnumerable<T> and IComparable<T> interfaces can help you
reuse code.
Variance in Delegates (C#) Discusses covariance and contravariance in generic and non-
generic delegates and provides a list of variant generic delegates
in .NET.
Using Variance in Delegates Shows how to use covariance and contravariance support in
(C#) non-generic delegates to match method signatures with
delegate types.
Using Variance for Func and Shows how covariance and contravariance support in the Func
Action Generic Delegates (C#) and Action delegates can help you reuse code.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Variance in Generic Interfaces (C#)
Article • 09/15/2021
.NET Framework 4 introduced variance support for several existing generic interfaces.
Variance support enables implicit conversion of classes that implement these interfaces.
IEnumerable<T> (T is covariant)
IEnumerator<T> (T is covariant)
IQueryable<T> (T is covariant)
IComparer<T> (T is contravariant)
IEqualityComparer<T> (T is contravariant)
IComparable<T> (T is contravariant)
Starting with .NET Framework 4.5, the following interfaces are variant:
IReadOnlyList<T> (T is covariant)
IReadOnlyCollection<T> (T is covariant)
Covariance permits a method to have a more derived return type than that defined by
the generic type parameter of the interface. To illustrate the covariance feature, consider
these generic interfaces: IEnumerable<Object> and IEnumerable<String> . The
IEnumerable<String> interface does not inherit the IEnumerable<Object> interface.
However, the String type does inherit the Object type, and in some cases you may
want to assign objects of these interfaces to each other. This is shown in the following
code example.
C#
In earlier versions of .NET Framework, this code causes a compilation error in C# and, if
Option Strict is on, in Visual Basic. But now you can use strings instead of objects , as
C#
// Comparer class.
class BaseComparer : IEqualityComparer<BaseClass>
{
public int GetHashCode(BaseClass baseInstance)
{
return baseInstance.GetHashCode();
}
public bool Equals(BaseClass x, BaseClass y)
{
return x == y;
}
}
class Program
{
static void Test()
{
IEqualityComparer<BaseClass> baseComparer = new BaseComparer();
For more examples, see Using Variance in Interfaces for Generic Collections (C#).
Variance in generic interfaces is supported for reference types only. Value types do not
support variance. For example, IEnumerable<int> cannot be implicitly converted to
IEnumerable<object> , because integers are represented by a value type.
C#
It is also important to remember that classes that implement variant interfaces are still
invariant. For example, although List<T> implements the covariant interface
IEnumerable<T>, you cannot implicitly convert List<String> to List<Object> . This is
illustrated in the following code example.
C#
See also
Using Variance in Interfaces for Generic Collections (C#)
Creating Variant Generic Interfaces (C#)
Generic Interfaces
Variance in Delegates (C#)
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Creating Variant Generic Interfaces (C#)
Article • 09/15/2021
7 Note
) Important
ref , in , and out parameters in C# cannot be variant. Value types also do not
support variance.
You can declare a generic type parameter covariant by using the out keyword. The
covariant type must satisfy the following conditions:
The type is used only as a return type of interface methods and not used as a type
of method arguments. This is illustrated in the following example, in which the type
R is declared covariant.
C#
There is one exception to this rule. If you have a contravariant generic delegate as
a method parameter, you can use the type as a generic type parameter for the
delegate. This is illustrated by the type R in the following example. For more
information, see Variance in Delegates (C#) and Using Variance for Func and Action
Generic Delegates (C#).
C#
The type is not used as a generic constraint for the interface methods. This is
illustrated in the following code.
C#
You can declare a generic type parameter contravariant by using the in keyword. The
contravariant type can be used only as a type of method arguments and not as a return
type of interface methods. The contravariant type can also be used for generic
constraints. The following code shows how to declare a contravariant interface and use a
generic constraint for one of its methods.
C#
C#
C#
Classes that implement variant interfaces are invariant. For example, consider the
following code.
C#
C#
the same interface. The same rule is applied to contravariant generic type parameters.
You can create an interface that extends both the interface where the generic type
parameter T is covariant and the interface where it is contravariant if in the extending
interface the generic type parameter T is invariant. This is illustrated in the following
code example.
C#
C#
Avoiding Ambiguity
When you implement variant generic interfaces, variance can sometimes lead to
ambiguity. Such ambiguity should be avoided.
For example, if you explicitly implement the same variant generic interface with different
generic type parameters in one class, it can create ambiguity. The compiler does not
produce an error in this case, but it's not specified which interface implementation will
be chosen at run time. This ambiguity could lead to subtle bugs in your code. Consider
the following code example.
C#
IEnumerator IEnumerable.GetEnumerator()
{
// Some code.
return null;
}
IEnumerator<Dog> IEnumerable<Dog>.GetEnumerator()
{
Console.WriteLine("Dog");
// Some code.
return null;
}
}
class Program
{
public static void Test()
{
IEnumerable<Animal> pets = new Pets();
pets.GetEnumerator();
}
}
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Using Variance in Interfaces for Generic
Collections (C#)
Article • 09/15/2021
A covariant interface allows its methods to return more derived types than those
specified in the interface. A contravariant interface allows its methods to accept
parameters of less derived types than those specified in the interface.
For a list of variant interfaces in .NET, see Variance in Generic Interfaces (C#).
C#
class Program
{
// The method has a parameter of the IEnumerable<Person> type.
public static void PrintFullName(IEnumerable<Person> persons)
{
foreach (Person person in persons)
{
Console.WriteLine("Name: {0} {1}",
person.FirstName, person.LastName);
}
}
public static void Test()
{
IEnumerable<Employee> employees = new List<Employee>();
PrintFullName(employees);
}
}
C#
class Program
{
IEnumerable<Employee> noduplicates =
employees.Distinct<Employee>(new PersonComparer());
See also
Variance in Generic Interfaces (C#)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Variance in Delegates (C#)
Article • 09/15/2021
.NET Framework 3.5 introduced variance support for matching method signatures with
delegate types in all delegates in C#. This means that you can assign to delegates not
only methods that have matching signatures, but also methods that return more derived
types (covariance) or that accept parameters that have less derived types
(contravariance) than that specified by the delegate type. This includes both generic and
non-generic delegates.
For example, consider the following code, which has two classes and two delegates:
generic and non-generic.
C#
C#
// Matching signature.
public static First ASecondRFirst(Second second)
{ return new First(); }
The following code example illustrates the implicit conversion between the method
signature and the delegate type.
C#
// Assigning a method with a matching signature
// to a non-generic delegate. No conversion is necessary.
SampleDelegate dNonGeneric = ASecondRFirst;
// Assigning a method with a more derived return type
// and less derived argument type to a non-generic delegate.
// The implicit conversion is used.
SampleDelegate dNonGenericConversion = AFirstRSecond;
For more examples, see Using Variance in Delegates (C#) and Using Variance for Func
and Action Generic Delegates (C#).
The following code example shows how you can create a delegate that has a covariant
generic type parameter.
C#
C#
Action delegates from the System namespace, for example, Action<T> and
Action<T1,T2>
Func delegates from the System namespace, for example, Func<TResult> and
Func<T,TResult>
You can declare a generic type parameter covariant in a generic delegate by using the
out keyword. The covariant type can be used only as a method return type and not as a
type of method arguments. The following code example shows how to declare a
covariant generic delegate.
C#
You can declare a generic type parameter contravariant in a generic delegate by using
the in keyword. The contravariant type can be used only as a type of method
arguments and not as a method return type. The following code example shows how to
declare a contravariant generic delegate.
C#
) Important
It is also possible to support both variance and covariance in the same delegate, but for
different type parameters. This is shown in the following example.
C#
C#
C#
The following example demonstrates that variance in generic type parameters is not
supported for value types.
C#
See also
Generics
Using Variance for Func and Action Generic Delegates (C#)
How to combine delegates (Multicast Delegates)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Using Variance in Delegates (C#)
Article • 09/15/2021
Example 1: Covariance
Description
This example demonstrates how delegates can be used with methods that have return
types that are derived from the return type in the delegate signature. The data type
returned by DogsHandler is of type Dogs , which derives from the Mammals type that is
defined in the delegate.
Code
C#
class Mammals {}
class Dogs : Mammals {}
class Program
{
// Define the delegate.
public delegate Mammals HandlerMethod();
Example 2: Contravariance
Description
This example demonstrates how delegates can be used with methods that have
parameters whose types are base types of the delegate signature parameter type. With
contravariance, you can use one event handler instead of separate handlers. The
following example makes use of two delegates:
C#
C#
The example defines an event handler with an EventArgs parameter and uses it to
handle both the Button.KeyDown and Button.MouseClick events. It can do this because
EventArgs is a base type of both KeyEventArgs and MouseEventArgs.
Code
C#
public Form1()
{
InitializeComponent();
See also
Variance in Delegates (C#)
Using Variance for Func and Action Generic Delegates (C#)
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Using Variance for Func and Action
Generic Delegates (C#)
Article • 09/15/2021
These examples demonstrate how to use covariance and contravariance in the Func and
Action generic delegates to enable reuse of methods and provide more flexibility in
your code.
For more information about covariance and contravariance, see Variance in Delegates
(C#).
C#
}
}
However, you can assign this method to the Action<Employee> delegate because
Employee inherits Person .
C#
See also
Covariance and Contravariance (C#)
Generics
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Iterators (C#)
Article • 04/12/2023
An iterator can be used to step through collections such as lists and arrays.
You consume an iterator from client code by using a foreach statement or by using a
LINQ query.
In the following example, the first iteration of the foreach loop causes execution to
proceed in the SomeNumbers iterator method until the first yield return statement is
reached. This iteration returns a value of 3, and the current location in the iterator
method is retained. On the next iteration of the loop, execution in the iterator method
continues from where it left off, again stopping when it reaches a yield return
statement. This iteration returns a value of 5, and the current location in the iterator
method is again retained. The loop completes when the end of the iterator method is
reached.
C#
For all examples in this topic except the Simple Iterator example, include using
directives for the System.Collections and System.Collections.Generic
namespaces.
Simple Iterator
The following example has a single yield return statement that is inside a for loop. In
Main , each iteration of the foreach statement body creates a call to the iterator
C#
C#
The following example creates a Zoo class that contains a collection of animals.
The foreach statement that refers to the class instance ( theZoo ) implicitly calls the
GetEnumerator method. The foreach statements that refer to the Birds and Mammals
C#
theZoo.AddMammal("Whale");
theZoo.AddMammal("Rhinoceros");
theZoo.AddBird("Penguin");
theZoo.AddBird("Warbler");
Console.ReadKey();
}
// Public methods.
public void AddMammal(string name)
{
animals.Add(new Animal { Name = name, Type = Animal.TypeEnum.Mammal
});
}
// Public members.
public IEnumerable Mammals
{
get { return AnimalsForType(Animal.TypeEnum.Mammal); }
}
// Private methods.
private IEnumerable AnimalsForType(Animal.TypeEnum type)
{
foreach (Animal theAnimal in animals)
{
if (theAnimal.Type == type)
{
yield return theAnimal.Name;
}
}
}
// Private class.
private class Animal
{
public enum TypeEnum { Bird, Mammal }
The example uses named iterators to support various ways of iterating through the
same collection of data. These named iterators are the TopToBottom and BottomToTop
properties, and the TopN method.
C#
Console.ReadKey();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
Syntax Information
An iterator can occur as a method or get accessor. An iterator cannot occur in an event,
instance constructor, static constructor, or static finalizer.
An implicit conversion must exist from the expression type in the yield return
statement to the type argument for the IEnumerable<T> returned by the iterator.
In C#, yield is not a reserved word and has special meaning only when it is used before
a return or break keyword.
Technical Implementation
Although you write an iterator as a method, the compiler translates it into a nested class
that is, in effect, a state machine. This class keeps track of the position of the iterator as
long the foreach loop in the client code continues.
To see what the compiler does, you can use the Ildasm.exe tool to view the common
intermediate language code that's generated for an iterator method.
When you create an iterator for a class or struct, you don't have to implement the whole
IEnumerator interface. When the compiler detects the iterator, it automatically generates
the Current , MoveNext , and Dispose methods of the IEnumerator or IEnumerator<T>
interface.
On each successive iteration of the foreach loop (or the direct call to
IEnumerator.MoveNext ), the next iterator code body resumes after the previous yield
return statement. It then continues to the next yield return statement until the end of
Iterators don't support the IEnumerator.Reset method. To reiterate from the start, you
must obtain a new iterator. Calling Reset on the iterator returned by an iterator method
throws a NotSupportedException.
Use of Iterators
Iterators enable you to maintain the simplicity of a foreach loop when you need to use
complex code to populate a list sequence. This can be useful when you want to do the
following:
Modify the list sequence after the first foreach loop iteration.
Avoid fully loading a large list before the first iteration of a foreach loop. An
example is a paged fetch to load a batch of table rows. Another example is the
EnumerateFiles method, which implements iterators in .NET.
Encapsulate building the list in the iterator. In the iterator method, you can build
the list and then yield each result in a loop.
See also
System.Collections.Generic
IEnumerable<T>
foreach, in
Generics
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Statements (C# Programming Guide)
Article • 04/22/2023
The actions that a program takes are expressed in statements. Common actions include
declaring variables, assigning values, calling methods, looping through collections, and
branching to one or another block of code, depending on a given condition. The order
in which statements are executed in a program is called the flow of control or flow of
execution. The flow of control may vary every time that a program is run, depending on
how the program reacts to input that it receives at run time.
A statement can consist of a single line of code that ends in a semicolon, or a series of
single-line statements in a block. A statement block is enclosed in {} brackets and can
contain nested blocks. The following code shows two examples of single-line
statements, and a multi-line statement block:
C#
// Assignment statement.
counter = 1;
Types of statements
The following table lists the various types of statements in C# and their associated
keywords, with links to topics that include more information:
ノ Expand table
Expression Expression statements that calculate a value must store the value in a variable.
statements
Iteration Iteration statements enable you to loop through collections like arrays, or
statements perform the same set of statements repeatedly until a specified condition is
met. For more information, see the following topics:
do
for
foreach
while
Jump Jump statements transfer control to another section of code. For more
statements information, see the following topics:
break
continue
Category C# keywords / notes
goto
return
yield
checked and The checked and unchecked statements enable you to specify whether integral-
unchecked type numerical operations are allowed to cause an overflow when the result is
stored in a variable that is too small to hold the resulting value.
The await If you mark a method with the async modifier, you can use the await operator in
statement the method. When control reaches an await expression in the async method,
control returns to the caller, and progress in the method is suspended until the
awaited task completes. When the task is complete, execution can resume in the
method.
For a simple example, see the "Async Methods" section of Methods. For more
information, see Asynchronous Programming with async and await.
The yield An iterator performs a custom iteration over a collection, such as a list or an
return array. An iterator uses the yield return statement to return each element one at
statement a time. When a yield return statement is reached, the current location in code
is remembered. Execution is restarted from that location when the iterator is
called the next time.
The fixed The fixed statement prevents the garbage collector from relocating a movable
statement variable. For more information, see fixed.
The lock The lock statement enables you to limit access to blocks of code to only one
statement thread at a time. For more information, see lock.
Labeled You can give a statement a label and then use the goto keyword to jump to the
statements labeled statement. (See the example in the following row.)
The empty The empty statement consists of a single semicolon. It does nothing and can be
statement used in places where a statement is required but no action needs to be
performed.
Declaration statements
The following code shows examples of variable declarations with and without an initial
assignment, and a constant declaration with the necessary initialization.
C#
Expression statements
The following code shows examples of expression statements, including assignment,
object creation with assignment, and method invocation.
C#
C#
void ProcessMessages()
{
while (ProcessMessage())
; // Statement needed here.
}
void F()
{
//...
if (done) goto exit;
//...
exit:
; // Statement needed here.
}
Embedded statements
Some statements, for example, iteration statements, always have an embedded
statement that follows them. This embedded statement may be either a single
statement or multiple statements enclosed by {} brackets in a statement block. Even
single-line embedded statements can be enclosed in {} brackets, as shown in the
following example:
C#
// Not recommended.
foreach (string s in System.IO.Directory.GetDirectories(
System.Environment.CurrentDirectory))
System.Console.WriteLine(s);
C#
if(pointB == true)
//Error CS1023:
int radius = 5;
C#
if (b == true)
{
// OK:
System.DateTime d = System.DateTime.Now;
System.Console.WriteLine(d.ToLongDateString());
}
C#
Unreachable statements
If the compiler determines that the flow of control can never reach a particular
statement under any circumstances, it will produce warning CS0162, as shown in the
following example:
C#
C# language specification
For more information, see the Statements section of the C# language specification.
See also
Statement keywords
C# operators and expressions
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Expression-bodied members (C#
programming guide)
Article • 08/02/2024
C#
Expression body definitions can be used with the following type members:
Method
Read-only property
Property
Constructor
Finalizer
Indexer
Methods
An expression-bodied method consists of a single expression that returns a value whose
type matches the method's return type, or, for methods that return void , that performs
some operation. For example, types that override the ToString method typically include
a single expression that returns the string representation of the current object.
The following example defines a Person class that overrides the ToString method with
an expression body definition. It also defines a DisplayName method that displays a
name to the console. The return keyword is not used in the ToString expression body
definition.
C#
using System;
namespace ExpressionBodiedMembers;
public class Person
{
public Person(string firstName, string lastName)
{
fname = firstName;
lname = lastName;
}
class Example
{
static void Main()
{
Person p = new Person("Mandy", "Dejesus");
Console.WriteLine(p);
p.DisplayName();
}
}
Read-only properties
You can use expression body definition to implement a read-only property. To do that,
use the following syntax:
C#
The following example defines a Location class whose read-only Name property is
implemented as an expression body definition that returns the value of the private
locationName field:
C#
For more information about properties, see Properties (C# Programming Guide).
Properties
You can use expression body definitions to implement property get and set accessors.
The following example demonstrates how to do that:
C#
For more information about properties, see Properties (C# Programming Guide).
Events
Similarly, event add and remove accessors can be expression-bodied:
C#
For more information about events, see Events (C# Programming Guide).
Constructors
An expression body definition for a constructor typically consists of a single assignment
expression or a method call that handles the constructor's arguments or initializes
instance state.
The following example defines a Location class whose constructor has a single string
parameter named name. The expression body definition assigns the argument to the
Name property.
C#
Finalizers
An expression body definition for a finalizer typically contains cleanup statements, such
as statements that release unmanaged resources.
The following example defines a finalizer that uses an expression body definition to
indicate that the finalizer has been called.
C#
Indexers
Like with properties, indexer get and set accessors consist of expression body
definitions if the get accessor consists of a single expression that returns a value or the
set accessor performs a simple assignment.
The following example defines a class named Sports that includes an internal String
array that contains the names of some sports. Both the indexer get and set accessors
are implemented as expression body definitions.
C#
using System;
using System.Collections.Generic;
namespace SportsExample;
See also
.NET code style rules for expression-bodied-members
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Equality comparisons (C# Programming
Guide)
Article • 03/12/2024
It is sometimes necessary to compare two values for equality. In some cases, you are
testing for value equality, also known as equivalence, which means that the values that
are contained by the two variables are equal. In other cases, you have to determine
whether two variables refer to the same underlying object in memory. This type of
equality is called reference equality, or identity. This topic describes these two kinds of
equality and provides links to other topics for more information.
Reference equality
Reference equality means that two object references refer to the same underlying
object. This can occur through simple assignment, as shown in the following example.
C#
using System;
class Test
{
public int Num { get; set; }
public string Str { get; set; }
// Assign b to a.
b = a;
In this code, two objects are created, but after the assignment statement, both
references refer to the same object. Therefore they have reference equality. Use the
ReferenceEquals method to determine whether two references refer to the same object.
The concept of reference equality applies only to reference types. Value type objects
cannot have reference equality because when an instance of a value type is assigned to
a variable, a copy of the value is made. Therefore you can never have two unboxed
structs that refer to the same location in memory. Furthermore, if you use
ReferenceEquals to compare two value types, the result will always be false , even if the
values that are contained in the objects are all identical. This is because each variable is
boxed into a separate object instance. For more information, see How to test for
reference equality (Identity).
Value equality
Value equality means that two objects contain the same value or values. For primitive
value types such as int or bool, tests for value equality are straightforward. You can use
the == operator, as shown in the following example.
C#
int a = GetOriginalValue();
int b = GetCurrentValue();
For most other types, testing for value equality is more complex because it requires that
you understand how the type defines it. For classes and structs that have multiple fields
or properties, value equality is often defined to mean that all fields or properties have
the same value. For example, two Point objects might be defined to be equivalent if
pointA.X is equal to pointB.X and pointA.Y is equal to pointB.Y. For records, value
equality means that two variables of a record type are equal if the types match and all
property and field values match.
However, there is no requirement that equivalence be based on all the fields in a type. It
can be based on a subset. When you compare types that you do not own, you should
make sure to understand specifically how equivalence is defined for that type. For more
information about how to define value equality in your own classes and structs, see How
to define value equality for a type.
Related topics
ノ Expand table
Title Description
How to test for reference Describes how to determine whether two variables have
equality (Identity) reference equality.
How to define value equality for Describes how to provide a custom definition of value
a type equality for a type.
Records Provides information about record types, which test for value
equality by default.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to define value equality for a class
or struct (C# Programming Guide)
Article • 06/21/2022
When you define a class or struct, you decide whether it makes sense to create a custom
definition of value equality (or equivalence) for the type. Typically, you implement value
equality when you expect to add objects of the type to a collection, or when their
primary purpose is to store a set of fields or properties. You can base your definition of
value equality on a comparison of all the fields and properties in the type, or you can
base the definition on a subset.
In either case, and in both classes and structs, your implementation should follow the
five guarantees of equivalence (for the following rules, assume that x , y and z are not
null):
4. Successive invocations of x.Equals(y) return the same value as long as the objects
referenced by x and y aren't modified.
5. Any non-null value isn't equal to null. However, x.Equals(y) throws an exception
when x is null. That breaks rules 1 or 2, depending on the argument to Equals .
Any struct that you define already has a default implementation of value equality that it
inherits from the System.ValueType override of the Object.Equals(Object) method. This
implementation uses reflection to examine all the fields and properties in the type.
Although this implementation produces correct results, it is relatively slow compared to
a custom implementation that you write specifically for the type.
The implementation details for value equality are different for classes and structs.
However, both classes and structs require the same basic steps for implementing
equality:
1. Override the virtual Object.Equals(Object) method. In most cases, your
implementation of bool Equals( object obj ) should just call into the type-
specific Equals method that is the implementation of the System.IEquatable<T>
interface. (See step 2.)
example, you might decide to define equality by comparing only one or two fields
in your type. Don't throw exceptions from Equals . For classes that are related by
inheritance:
This method should examine only fields that are declared in the class. It
should call base.Equals to examine fields that are in the base class. (Don't
call base.Equals if the type inherits directly from Object, because the Object
implementation of Object.Equals(Object) performs a reference equality
check.)
Two variables should be deemed equal only if the run-time types of the
variables being compared are the same. Also, make sure that the IEquatable
implementation of the Equals method for the run-time type is used if the
run-time and compile-time types of a variable are different. One strategy for
making sure run-time types are always compared correctly is to implement
IEquatable only in sealed classes. For more information, see the class
4. Override Object.GetHashCode so that two objects that have value equality produce
the same hash code.
5. Optional: To support definitions for "greater than" or "less than," implement the
IComparable<T> interface for your type, and also overload the <= and >=
operators.
7 Note
You can use records to get value equality semantics without any unnecessary
boilerplate code.
Class example
The following example shows how to implement value equality in a class (reference
type).
C#
namespace ValueEqualityClass;
public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs) => !(lhs ==
rhs);
}
class Program
{
static void Main(string[] args)
{
ThreeDPoint pointA = new ThreeDPoint(3, 4, 5);
ThreeDPoint pointB = new ThreeDPoint(3, 4, 5);
ThreeDPoint pointC = null;
int i = 5;
Console.WriteLine("pointA.Equals(pointB) = {0}",
pointA.Equals(pointB));
Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
Console.WriteLine("null comparison = {0}", pointA.Equals(pointC));
Console.WriteLine("Compare to some other type = {0}",
pointA.Equals(i));
/* Output:
pointA.Equals(pointB) = True
pointA == pointB = True
null comparison = False
Compare to some other type = False
Two null TwoDPoints are equal: True
(pointE == pointA) = False
(pointA == pointE) = False
(pointA != pointE) = True
pointE.Equals(list[0]): False
*/
The == and != operators can be used with classes even if the class does not overload
them. However, the default behavior is to perform a reference equality check. In a class,
if you overload the Equals method, you should overload the == and != operators, but
it is not required.
) Important
The preceding example code may not handle every inheritance scenario the way
you expect. Consider the following code:
C#
The built-in value equality of record types handles scenarios like this correctly. If
TwoDPoint and ThreeDPoint were record types, the result of p1.Equals(p2) would
Struct example
The following example shows how to implement value equality in a struct (value type):
C#
namespace ValueEqualityStruct
{
struct TwoDPoint : IEquatable<TwoDPoint>
{
public int X { get; private set; }
public int Y { get; private set; }
class Program
{
static void Main(string[] args)
{
TwoDPoint pointA = new TwoDPoint(3, 4);
TwoDPoint pointB = new TwoDPoint(3, 4);
int i = 5;
// True:
Console.WriteLine("pointA.Equals(pointB) = {0}",
pointA.Equals(pointB));
// True:
Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
// True:
Console.WriteLine("object.Equals(pointA, pointB) = {0}",
object.Equals(pointA, pointB));
// False:
Console.WriteLine("pointA.Equals(null) = {0}",
pointA.Equals(null));
// False:
Console.WriteLine("(pointA == null) = {0}", pointA == null);
// True:
Console.WriteLine("(pointA != null) = {0}", pointA != null);
// False:
Console.WriteLine("pointA.Equals(i) = {0}", pointA.Equals(i));
// CS0019:
// Console.WriteLine("pointA == i = {0}", pointA == i);
pointD = temp;
// True:
Console.WriteLine("pointD == (pointC = 3,4) = {0}", pointD ==
pointC);
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
pointA.Equals(pointB) = True
pointA == pointB = True
Object.Equals(pointA, pointB) = True
pointA.Equals(null) = False
(pointA == null) = False
(pointA != null) = True
pointA.Equals(i) = False
pointE.Equals(list[0]): True
pointA == (pointC = null) = False
pointC == pointD = True
pointA == (pointC = 3,4) = True
pointD == (pointC = 3,4) = True
*/
}
performing the value equality check and optionally to base the comparison on some
subset of the struct's fields or properties.
The == and != operators can't operate on a struct unless the struct explicitly overloads
them.
See also
Equality comparisons
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to test for reference equality
(Identity) (C# Programming Guide)
Article • 11/05/2021
You do not have to implement any custom logic to support reference equality
comparisons in your types. This functionality is provided for all types by the static
Object.ReferenceEquals method.
The following example shows how to determine whether two variables have reference
equality, which means that they refer to the same object in memory.
The example also shows why Object.ReferenceEquals always returns false for value
types and why you should not use ReferenceEquals to determine string equality.
Example
C#
using System.Text;
namespace TestReferenceEquality
{
struct TestStruct
{
public int Num { get; private set; }
public string Name { get; private set; }
class TestClass
{
public int Num { get; set; }
public string? Name { get; set; }
}
class Program
{
static void Main()
{
// Demonstrate reference equality with reference types.
#region ReferenceTypes
// Create two reference type instances that have identical
values.
TestClass tcA = new TestClass() { Num = 1, Name = "New
TestClass" };
TestClass tcB = new TestClass() { Num = 1, Name = "New
TestClass" };
#region stringRefEquality
// Constant strings within the same assembly are always interned
by the runtime.
// This means they are stored in the same location in memory.
Therefore,
// the two strings have reference equality although no
assignment takes place.
string strA = "Hello world!";
string strB = "Hello world!";
Console.WriteLine("ReferenceEquals(strA, strB) = {0}",
Object.ReferenceEquals(strA, strB)); // true
#endregion
/* Output:
ReferenceEquals(tcA, tcB) = False
After assignment: ReferenceEquals(tcA, tcB) = True
tcB.Name = TestClass 42 tcB.Num: 42
After assignment: ReferenceEquals(tsC, tsD) = False
ReferenceEquals(strA, strB) = True
strA = "Goodbye world!" strB = "Hello world!"
After strA changes, ReferenceEquals(strA, strB) = False
ReferenceEquals(stringC, strB) = False
stringC == strB = True
*/
The implementation of Equals in the System.Object universal base class also performs a
reference equality check, but it is best not to use this because, if a class happens to
override the method, the results might not be what you expect. The same is true for the
== and != operators. When they are operating on reference types, the default behavior
Constant strings within the same assembly are always interned by the runtime. That is,
only one instance of each unique literal string is maintained. However, the runtime does
not guarantee that strings created at run time are interned, nor does it guarantee that
two equal constant strings in different assemblies are interned.
See also
Equality Comparisons
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Casting and type conversions (C#
Programming Guide)
Article • 03/19/2024
C#
int i;
However, you might sometimes need to copy a value into a variable or method
parameter of another type. For example, you might have an integer variable that you
need to pass to a method whose parameter is typed as double . Or you might need to
assign a class variable to a variable of an interface type. These kinds of operations are
called type conversions. In C#, you can perform the following kinds of conversions:
Implicit conversions
For built-in numeric types, an implicit conversion can be made when the value to be
stored can fit into the variable without being truncated or rounded off. For integral
types, this means the range of the source type is a proper subset of the range for the
target type. For example, a variable of type long (64-bit integer) can store any value that
an int (32-bit integer) can store. In the following example, the compiler implicitly
converts the value of num on the right to a type long before assigning it to bigNum .
C#
For a complete list of all implicit numeric conversions, see the Implicit numeric
conversions section of the Built-in numeric conversions article.
For reference types, an implicit conversion always exists from a class to any one of its
direct or indirect base classes or interfaces. No special syntax is necessary because a
derived class always contains all the members of a base class.
C#
// Always OK.
Base b = d;
Explicit conversions
However, if a conversion can't be made without a risk of losing information, the
compiler requires that you perform an explicit conversion, which is called a cast. A cast is
a way of explicitly informing the compiler that you intend to make the conversion and
that you're aware data loss might occur, or the cast may fail at run time. To perform a
cast, specify the type that you're casting to in parentheses in front of the value or
variable to be converted. The following program casts a double to an int. The program
won't compile without the cast.
C#
class Test
{
static void Main()
{
double x = 1234.7;
int a;
// Cast double to int.
a = (int)x;
System.Console.WriteLine(a);
}
}
// Output: 1234
For a complete list of supported explicit numeric conversions, see the Explicit numeric
conversions section of the Built-in numeric conversions article.
For reference types, an explicit cast is required if you need to convert from a base type
to a derived type:
C#
A cast operation between reference types doesn't change the run-time type of the
underlying object; it only changes the type of the value that is being used as a reference
to that object. For more information, see Polymorphism.
C#
class Animal
{
public void Eat() => System.Console.WriteLine("Eating.");
class UnSafeCast
{
static void Main()
{
Test(new Mammal());
The Test method has an Animal parameter, thus explicitly casting the argument a to a
Reptile makes a dangerous assumption. It's safer to not make assumptions, but rather
check the type. C# provides the is operator to enable you to test for compatibility
before actually performing a cast. For more information, see How to safely cast using
pattern matching and the as and is operators.
C# language specification
For more information, see the Conversions section of the C# language specification.
See also
Types
Cast expression
User-defined conversion operators
Generalized Type Conversion
How to convert a string to a number
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Boxing and Unboxing (C# Programming
Guide)
Article • 09/15/2021
Boxing is the process of converting a value type to the type object or to any interface
type implemented by this value type. When the common language runtime (CLR) boxes
a value type, it wraps the value inside a System.Object instance and stores it on the
managed heap. Unboxing extracts the value type from the object. Boxing is implicit;
unboxing is explicit. The concept of boxing and unboxing underlies the C# unified view
of the type system in which a value of any type can be treated as an object.
In the following example, the integer variable i is boxed and assigned to object o .
C#
int i = 123;
// The following line boxes i.
object o = i;
C#
o = 123;
i = (int)o; // unboxing
C#
// String.Concat example.
// String.Concat has many versions. Rest the mouse pointer on
// Concat in the following statement to verify that the version
// that is used here takes three object arguments. Both 42 and
// true must be boxed.
Console.WriteLine(String.Concat("Answer", 42, true));
// List example.
// Create a list of objects to hold a heterogeneous collection
// of elements.
List<object> mixedList = new List<object>();
// The following loop sums the squares of the first group of boxed
// integers in mixedList. The list elements are objects, and cannot
// be multiplied or added to the sum until they are unboxed. The
// unboxing must be done explicitly.
var sum = 0;
for (var j = 1; j < 5; j++)
{
// The following statement causes a compiler error: Operator
// '*' cannot be applied to operands of type 'object' and
// 'object'.
//sum += mixedList[j] * mixedList[j];
// Output:
// Answer42True
// First Group:
// 1
// 2
// 3
// 4
// Second Group:
// 5
// 6
// 7
// 8
// 9
// Sum: 30
Performance
In relation to simple assignments, boxing and unboxing are computationally expensive
processes. When a value type is boxed, a new object must be allocated and constructed.
To a lesser degree, the cast required for unboxing is also expensive computationally. For
more information, see Performance.
Boxing
Boxing is used to store value types in the garbage-collected heap. Boxing is an implicit
conversion of a value type to the type object or to any interface type implemented by
this value type. Boxing a value type allocates an object instance on the heap and copies
the value into the new object.
C#
int i = 123;
The following statement implicitly applies the boxing operation on the variable i :
C#
The result of this statement is creating an object reference o , on the stack, that
references a value of the type int , on the heap. This value is a copy of the value-type
value assigned to the variable i . The difference between the two variables, i and o , is
illustrated in the following image of boxing conversion:
It is also possible to perform the boxing explicitly as in the following example, but
explicit boxing is never required:
C#
int i = 123;
object o = (object)i; // explicit boxing
Example
This example converts an integer variable i to an object o by using boxing. Then, the
value stored in the variable i is changed from 123 to 456 . The example shows that the
original value type and the boxed object use separate memory locations, and therefore
can store different values.
C#
class TestBoxing
{
static void Main()
{
int i = 123;
Checking the object instance to make sure that it is a boxed value of the given
value type.
Copying the value from the instance into the value-type variable.
C#
For the unboxing of value types to succeed at run time, the item being unboxed must
be a reference to an object that was previously created by boxing an instance of that
value type. Attempting to unbox null causes a NullReferenceException. Attempting to
unbox a reference to an incompatible value type causes an InvalidCastException.
Example
The following example demonstrates a case of invalid unboxing and the resulting
InvalidCastException . Using try and catch , an error message is displayed when the
error occurs.
C#
class TestUnboxing
{
static void Main()
{
int i = 123;
object o = i; // implicit boxing
try
{
int j = (short)o; // attempt to unbox
System.Console.WriteLine("Unboxing OK.");
}
catch (System.InvalidCastException e)
{
System.Console.WriteLine("{0} Error: Incorrect unboxing.",
e.Message);
}
}
}
C#
int j = (short)o;
to:
C#
int j = (int)o;
the conversion will be performed, and you will get the output:
Unboxing OK.
C# language specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
See also
Reference types
Value types
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to convert a byte array to an int
(C# Programming Guide)
Article • 09/23/2021
This example shows you how to use the BitConverter class to convert an array of bytes
to an int and back to an array of bytes. You may have to convert from bytes to a built-in
data type after you read bytes off the network, for example. In addition to the
ToInt32(Byte[], Int32) method in the example, the following table lists methods in the
BitConverter class that convert bytes (from an array of bytes) to other built-in types.
ノ Expand table
Examples
This example initializes an array of bytes, reverses the array if the computer architecture
is little-endian (that is, the least significant byte is stored first), and then calls the
ToInt32(Byte[], Int32) method to convert four bytes in the array to an int . The second
argument to ToInt32(Byte[], Int32) specifies the start index of the array of bytes.
7 Note
The output may differ depending on the endianness of your computer's
architecture.
C#
In this example, the GetBytes(Int32) method of the BitConverter class is called to convert
an int to an array of bytes.
7 Note
C#
See also
BitConverter
IsLittleEndian
Types
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
issues and pull requests. For Open a documentation issue
more information, see our
contributor guide. Provide product feedback
How to convert a string to a number
(C# Programming Guide)
Article • 03/15/2024
You convert a string to a number by calling the Parse or TryParse method found on
numeric types ( int , long , double , and so on), or by using methods in the
System.Convert class.
It's slightly more efficient and straightforward to call a TryParse method (for example,
int.TryParse("11", out number)) or Parse method (for example, var number =
int.Parse("11")). Using a Convert method is more useful for general objects that
implement IConvertible.
You use Parse or TryParse methods on the numeric type you expect the string contains,
such as the System.Int32 type. The Convert.ToInt32 method uses Parse internally. The
Parse method returns the converted number; the TryParse method returns a boolean
value that indicates whether the conversion succeeded, and returns the converted
number in an out parameter. If the string isn't in a valid format, Parse throws an
exception, but TryParse returns false . When calling a Parse method, you should
always use exception handling to catch a FormatException when the parse operation
fails.
10 from "10X", "1 0" (note the embedded space), "10 .3" (note the embedded space),
"10e1" ( float.TryParse works here), and so on. A string whose value is null or
String.Empty fails to parse successfully. You can check for a null or empty string before
attempting to parse it by calling the String.IsNullOrEmpty method.
The following example demonstrates both successful and unsuccessful calls to Parse
and TryParse .
C#
using System;
try
{
int numVal = Int32.Parse("-105");
Console.WriteLine(numVal);
}
catch (FormatException e)
{
Console.WriteLine(e.Message);
}
// Output: -105
try
{
int m = Int32.Parse("abc");
}
catch (FormatException e)
{
Console.WriteLine(e.Message);
}
// Output: Input string was not in a correct format.
The following example illustrates one approach to parsing a string expected to include
leading numeric characters (including hexadecimal characters) and trailing non-numeric
characters. It assigns valid characters from the beginning of a string to a new string
before calling the TryParse method. Because the strings to be parsed contain a few
characters, the example calls the String.Concat method to assign valid characters to a
new string. For a larger string, the StringBuilder class can be used instead.
C#
using System;
if (int.TryParse(numericString,
System.Globalization.NumberStyles.HexNumber, null, out int i))
{
Console.WriteLine($"'{str}' --> '{numericString}' --> {i}");
}
// Output: ' 10FFxxx' --> ' 10FF' --> 4351
ノ Expand table
decimal ToDecimal(String)
float ToSingle(String)
double ToDouble(String)
short ToInt16(String)
int ToInt32(String)
long ToInt64(String)
ushort ToUInt16(String)
uint ToUInt32(String)
ulong ToUInt64(String)
C#
using System;
while (repeat)
{
Console.Write("Enter a number between −2,147,483,648 and
+2,147,483,647 (inclusive): ");
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to convert between hexadecimal
strings and numeric types (C#
Programming Guide)
Article • 10/12/2021
Examples
This example outputs the hexadecimal value of each character in a string . First it parses
the string to an array of characters. Then it calls ToInt32(Char) on each character to
obtain its numeric value. Finally, it formats the number as its hexadecimal representation
in a string .
C#
This example parses a string of hexadecimal values and outputs the character
corresponding to each hexadecimal value. First it calls the Split(Char[]) method to obtain
each hexadecimal value as an individual string in an array. Then it calls ToInt32(String,
Int32) to convert the hexadecimal value to a decimal value represented as an int. It
shows two different ways to obtain the character corresponding to that character code.
The first technique uses ConvertFromUtf32(Int32), which returns the character
corresponding to the integer argument as a string . The second technique explicitly
casts the int to a char.
C#
C#
string hexString = "8E2";
int num = Int32.Parse(hexString,
System.Globalization.NumberStyles.HexNumber);
Console.WriteLine(num);
//Output: 2274
The following example shows how to convert a hexadecimal string to a float by using
the System.BitConverter class and the UInt32.Parse method.
C#
// Output: 200.0056
The following example shows how to convert a byte array to a hexadecimal string by
using the System.BitConverter class.
C#
/*Output:
01-AA-B1-DC-10-DD
01AAB1DC10DD
*/
The following example shows how to convert a byte array to a hexadecimal string by
calling the Convert.ToHexString method introduced in .NET 5.0.
C#
/*Output:
646F74636574
*/
See also
Standard Numeric Format Strings
Types
How to determine whether a string represents a numeric value
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Versioning with the Override and New
Keywords (C# Programming Guide)
Article • 10/27/2021
The C# language is designed so that versioning between base and derived classes in
different libraries can evolve and maintain backward compatibility. This means, for
example, that the introduction of a new member in a base class with the same name as
a member in a derived class is completely supported by C# and does not lead to
unexpected behavior. It also means that a class must explicitly state whether a method is
intended to override an inherited method, or whether a method is a new method that
hides a similarly named inherited method.
In C#, derived classes can contain methods with the same name as base class methods.
If the method in the derived class is not preceded by new or override keywords,
the compiler will issue a warning and the method will behave as if the new
keyword were present.
If the method in the derived class is preceded with the new keyword, the method is
defined as being independent of the method in the base class.
If the method in the derived class is preceded with the override keyword, objects
of the derived class will call that method instead of the base class method.
In order to apply the override keyword to the method in the derived class, the
base class method must be defined virtual.
The base class method can be called from within the derived class using the base
keyword.
The override , virtual , and new keywords can also be applied to properties,
indexers, and events.
By default, C# methods are not virtual. If a method is declared as virtual, any class
inheriting the method can implement its own version. To make a method virtual, the
virtual modifier is used in the method declaration of the base class. The derived class
can then override the base virtual method by using the override keyword or hide the
virtual method in the base class by using the new keyword. If neither the override
keyword nor the new keyword is specified, the compiler will issue a warning and the
method in the derived class will hide the method in the base class.
To demonstrate this in practice, assume for a moment that Company A has created a
class named GraphicsClass , which your program uses. The following is GraphicsClass :
C#
class GraphicsClass
{
public virtual void DrawLine() { }
public virtual void DrawPoint() { }
}
Your company uses this class, and you use it to derive your own class, adding a new
method:
C#
Your application is used without problems, until Company A releases a new version of
GraphicsClass , which resembles the following code:
C#
class GraphicsClass
{
public virtual void DrawLine() { }
public virtual void DrawPoint() { }
public virtual void DrawRectangle() { }
}
However, as soon as you recompile your application by using the new version of
GraphicsClass , you will receive a warning from the compiler, CS0108. This warning
informs you that you have to consider how you want your DrawRectangle method to
behave in your application.
If you want your method to override the new base class method, use the override
keyword:
C#
The override keyword makes sure that any objects derived from
YourDerivedGraphicsClass will use the derived class version of DrawRectangle . Objects
derived from YourDerivedGraphicsClass can still access the base class version of
DrawRectangle by using the base keyword:
C#
base.DrawRectangle();
If you do not want your method to override the new base class method, the following
considerations apply. To avoid confusion between the two methods, you can rename
your method. This can be time-consuming and error-prone, and just not practical in
some cases. However, if your project is relatively small, you can use Visual Studio's
Refactoring options to rename the method. For more information, see Refactoring
Classes and Types (Class Designer).
Alternatively, you can prevent the warning by using the keyword new in your derived
class definition:
C#
Using the new keyword tells the compiler that your definition hides the definition that is
contained in the base class. This is the default behavior.
C#
When DoWork is called on an instance of Derived , the C# compiler will first try to make
the call compatible with the versions of DoWork declared originally on Derived . Override
methods are not considered as declared on a class, they are new implementations of a
method declared on a base class. Only if the C# compiler cannot match the method call
to an original method on Derived , it will try to match the call to an overridden method
with the same name and compatible parameters. For example:
C#
int val = 5;
Derived d = new Derived();
d.DoWork(val); // Calls DoWork(double).
Because the variable val can be converted to a double implicitly, the C# compiler calls
DoWork(double) instead of DoWork(int) . There are two ways to avoid this. First, avoid
declaring new methods with the same name as virtual methods. Second, you can
instruct the C# compiler to call the virtual method by making it search the base class
method list by casting the instance of Derived to Base . Because the method is virtual,
the implementation of DoWork(int) on Derived will be called. For example:
C#
For more examples of new and override , see Knowing When to Use Override and New
Keywords.
See also
The C# type system
Methods
Inheritance
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Knowing When to Use Override and
New Keywords (C# Programming Guide)
Article • 10/27/2021
In C#, a method in a derived class can have the same name as a method in the base
class. You can specify how the methods interact by using the new and override
keywords. The override modifier extends the base class virtual method, and the new
modifier hides an accessible base class method. The difference is illustrated in the
examples in this topic.
In a console application, declare the following two classes, BaseClass and DerivedClass .
DerivedClass inherits from BaseClass .
C#
class BaseClass
{
public void Method1()
{
Console.WriteLine("Base - Method1");
}
}
bcdc is of type BaseClass , and its value is of type DerivedClass . This is the variable
Because bc and bcdc have type BaseClass , they can only directly access Method1 , unless
you use casting. Variable dc can access both Method1 and Method2 . These relationships
are shown in the following code.
C#
class Program
{
static void Main(string[] args)
{
BaseClass bc = new BaseClass();
DerivedClass dc = new DerivedClass();
BaseClass bcdc = new DerivedClass();
bc.Method1();
dc.Method1();
dc.Method2();
bcdc.Method1();
}
// Output:
// Base - Method1
// Base - Method1
// Derived - Method2
// Base - Method1
}
Next, add the following Method2 method to BaseClass . The signature of this method
matches the signature of the Method2 method in DerivedClass .
C#
Because BaseClass now has a Method2 method, a second calling statement can be
added for BaseClass variables bc and bcdc , as shown in the following code.
C#
bc.Method1();
bc.Method2();
dc.Method1();
dc.Method2();
bcdc.Method1();
bcdc.Method2();
When you build the project, you see that the addition of the Method2 method in
BaseClass causes a warning. The warning says that the Method2 method in
DerivedClass hides the Method2 method in BaseClass . You are advised to use the new
keyword in the Method2 definition if you intend to cause that result. Alternatively, you
could rename one of the Method2 methods to resolve the warning, but that is not always
practical.
Before adding new , run the program to see the output produced by the additional
calling statements. The following results are displayed.
C#
// Output:
// Base - Method1
// Base - Method2
// Base - Method1
// Derived - Method2
// Base - Method1
// Base - Method2
The new keyword preserves the relationships that produce that output, but it suppresses
the warning. The variables that have type BaseClass continue to access the members of
BaseClass , and the variable that has type DerivedClass continues to access members in
To suppress the warning, add the new modifier to the definition of Method2 in
DerivedClass , as shown in the following code. The modifier can be added before or
after public .
C#
Run the program again to verify that the output has not changed. Also verify that the
warning no longer appears. By using new , you are asserting that you are aware that the
member that it modifies hides a member that is inherited from the base class. For more
information about name hiding through inheritance, see new Modifier.
To contrast this behavior to the effects of using override , add the following method to
DerivedClass . The override modifier can be added before or after public .
C#
Add the virtual modifier to the definition of Method1 in BaseClass . The virtual
modifier can be added before or after public .
C#
Run the project again. Notice especially the last two lines of the following output.
C#
// Output:
// Base - Method1
// Base - Method2
// Derived - Method1
// Derived - Method2
// Derived - Method1
// Base - Method2
The use of the override modifier enables bcdc to access the Method1 method that is
defined in DerivedClass . Typically, that is the desired behavior in inheritance hierarchies.
You want objects that have values that are created from the derived class to use the
methods that are defined in the derived class. You achieve that behavior by using
override to extend the base class method.
C#
using System;
using System.Text;
namespace OverrideAndNew
{
class Program
{
static void Main(string[] args)
{
BaseClass bc = new BaseClass();
DerivedClass dc = new DerivedClass();
BaseClass bcdc = new DerivedClass();
// The following two calls do what you would expect. They call
// the methods that are defined in BaseClass.
bc.Method1();
bc.Method2();
// Output:
// Base - Method1
// Base - Method2
// The following two calls do what you would expect. They call
// the methods that are defined in DerivedClass.
dc.Method1();
dc.Method2();
// Output:
// Derived - Method1
// Derived - Method2
class BaseClass
{
public virtual void Method1()
{
Console.WriteLine("Base - Method1");
}
method displays a basic description of a car, and then calls ShowDetails to provide
additional information. Each of the three classes defines a ShowDetails method. The new
modifier is used to define ShowDetails in the ConvertibleCar class. The override
modifier is used to define ShowDetails in the Minivan class.
C#
// Define the base class, Car. The class defines two methods,
// DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each
derived
// class also defines a ShowDetails method. The example tests which version
of
// ShowDetails is selected, the base class method or the derived class
method.
class Car
{
public void DescribeCar()
{
System.Console.WriteLine("Four wheels and an engine.");
ShowDetails();
}
The example tests which version of ShowDetails is called. The following method,
TestCars1 , declares an instance of each class, and then calls DescribeCar on each
instance.
C#
// Notice the output from this test case. The new modifier is
// used in the definition of ShowDetails in the ConvertibleCar
// class.
TestCars1 produces the following output. Notice especially the results for car2 , which
probably are not what you expected. The type of the object is ConvertibleCar , but
DescribeCar does not access the version of ShowDetails that is defined in the
ConvertibleCar class because that method is declared with the new modifier, not the
override modifier. As a result, a ConvertibleCar object displays the same description as
a Car object. Contrast the results for car3 , which is a Minivan object. In this case, the
ShowDetails method that is declared in the Minivan class overrides the ShowDetails
method that is declared in the Car class, and the description that is displayed describes
a minivan.
C#
// TestCars1
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Carries seven people.
// ----------
TestCars2 creates a list of objects that have type Car . The values of the objects are
instantiated from the Car , ConvertibleCar , and Minivan classes. DescribeCar is called
on each element of the list. The following code shows the definition of TestCars2 .
C#
The following output is displayed. Notice that it is the same as the output that is
displayed by TestCars1 . The ShowDetails method of the ConvertibleCar class is not
called, regardless of whether the type of the object is ConvertibleCar , as in TestCars1 ,
or Car , as in TestCars2 . Conversely, car3 calls the ShowDetails method from the
Minivan class in both cases, whether it has type Minivan or type Car .
C#
// TestCars2
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Standard transportation.
// ----------
// Four wheels and an engine.
// Carries seven people.
// ----------
Methods TestCars3 and TestCars4 complete the example. These methods call
ShowDetails directly, first from objects declared to have type ConvertibleCar and
Minivan ( TestCars3 ), then from objects declared to have type Car ( TestCars4 ). The
C#
The methods produce the following output, which corresponds to the results from the
first example in this topic.
C#
// TestCars3
// ----------
// A roof that opens up.
// Carries seven people.
// TestCars4
// ----------
// Standard transportation.
// Carries seven people.
The following code shows the complete project and its output.
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OverrideAndNew2
{
class Program
{
static void Main(string[] args)
{
// Declare objects of the derived classes and test which version
// of ShowDetails is run, base or derived.
TestCars1();
// Notice the output from this test case. The new modifier is
// used in the definition of ShowDetails in the ConvertibleCar
// class.
ConvertibleCar car2 = new ConvertibleCar();
car2.DescribeCar();
System.Console.WriteLine("----------");
// Define the base class, Car. The class defines two virtual methods,
// DescribeCar and ShowDetails. DescribeCar calls ShowDetails, and each
derived
// class also defines a ShowDetails method. The example tests which
version of
// ShowDetails is used, the base class method or the derived class
method.
class Car
{
public virtual void DescribeCar()
{
System.Console.WriteLine("Four wheels and an engine.");
ShowDetails();
}
}
See also
The C# type system
Versioning with the Override and New Keywords
base
abstract
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to override the ToString method
(C# Programming Guide)
Article • 10/27/2021
Every class or struct in C# implicitly inherits the Object class. Therefore, every object in
C# gets the ToString method, which returns a string representation of that object. For
example, all variables of type int have a ToString method, which enables them to
return their contents as a string:
C#
int x = 42;
string strx = x.ToString();
Console.WriteLine(strx);
// Output:
// 42
When you create a custom class or struct, you should override the ToString method in
order to provide information about your type to client code.
For information about how to use format strings and other types of custom formatting
with the ToString method, see Formatting Types.
) Important
When you decide what information to provide through this method, consider
whether your class or struct will ever be used by untrusted code. Be careful to
ensure that you do not provide any information that could be exploited by
malicious code.
1. Declare a ToString method with the following modifiers and return type:
C#
C#
class Person
{
public string Name { get; set; }
public int Age { get; set; }
You can test the ToString method as shown in the following code example:
C#
See also
IFormattable
The C# type system
Strings
string
override
virtual
Formatting Types
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Members (C# Programming Guide)
Article • 09/17/2021
Classes and structs have members that represent their data and behavior. A class's
members include all the members declared in the class, along with all members (except
constructors and finalizers) declared in all classes in its inheritance hierarchy. Private
members in base classes are inherited but are not accessible from derived classes.
The following table lists the kinds of members a class or struct may contain:
ノ Expand table
Member Description
Fields Fields are variables declared at class scope. A field may be a built-in numeric type
or an instance of another class. For example, a calendar class may have a field that
contains the current date.
Constants Constants are fields whose value is set at compile time and cannot be changed.
Properties Properties are methods on a class that are accessed as if they were fields on that
class. A property can provide protection for a class field to keep it from being
changed without the knowledge of the object.
Methods Methods define the actions that a class can perform. Methods can take
parameters that provide input data, and can return output data through
parameters. Methods can also return a value directly, without using a parameter.
Events Events provide notifications about occurrences, such as button clicks or the
successful completion of a method, to other objects. Events are defined and
triggered by using delegates.
Operators Overloaded operators are considered type members. When you overload an
operator, you define it as a public static method in a type. For more information,
see Operator overloading.
Constructors Constructors are methods that are called when the object is first created. They are
often used to initialize the data of an object.
Finalizers Finalizers are used very rarely in C#. They are methods that are called by the
runtime execution engine when the object is about to be removed from memory.
They are generally used to make sure that any resources which must be released
are handled appropriately.
Nested Nested types are types declared within another type. Nested types are often used
Types to describe objects that are used only by the types that contain them.
See also
Classes
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Abstract and Sealed Classes and Class
Members (C# Programming Guide)
Article • 10/27/2021
The abstract keyword enables you to create classes and class members that are
incomplete and must be implemented in a derived class.
The sealed keyword enables you to prevent the inheritance of a class or certain class
members that were previously marked virtual.
C#
Abstract classes may also define abstract methods. This is accomplished by adding the
keyword abstract before the return type of the method. For example:
C#
C#
public class F : E
{
public override void DoWork(int i)
{
// New implementation.
}
}
If a virtual method is declared abstract , it is still virtual to any class inheriting from
the abstract class. A class inheriting an abstract method cannot access the original
implementation of the method—in the previous example, DoWork on class F cannot call
DoWork on class D. In this way, an abstract class can force derived classes to provide new
C#
A sealed class cannot be used as a base class. For this reason, it cannot also be an
abstract class. Sealed classes prevent derivation. Because they can never be used as a
base class, some run-time optimizations can make calling sealed class members slightly
faster.
example:
C#
public class D : C
{
public sealed override void DoWork() { }
}
See also
The C# type system
Inheritance
Methods
Fields
How to define abstract properties
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Static Classes and Static Class Members
(C# Programming Guide)
Article • 03/19/2024
A static class is basically the same as a non-static class, but there's one difference: a
static class can't be instantiated. In other words, you can't use the new operator to
create a variable of the class type. Because there's no instance variable, you access the
members of a static class by using the class name itself. For example, if you have a static
class that is named UtilityClass that has a public static method named MethodA , you
call the method as shown in the following example:
C#
UtilityClass.MethodA();
A static class can be used as a convenient container for sets of methods that just
operate on input parameters and don't have to get or set any internal instance fields.
For example, in the .NET Class Library, the static System.Math class contains methods
that perform mathematical operations, without any requirement to store or retrieve data
that is unique to a particular instance of the Math class. That is, you apply the members
of the class by specifying the class name and the method name, as shown in the
following example.
C#
// Output:
// 3.14
// -4
// 3
As is the case with all class types, the .NET runtime loads the type information for a
static class when the program that references the class is loaded. The program can't
specify exactly when the class is loaded. However, it's guaranteed to load and have its
fields initialized and its static constructor called before the class is referenced for the
first time in your program. A static constructor is only called one time, and a static class
remains in memory for the lifetime of the application domain in which your program
resides.
7 Note
To create a non-static class that allows only one instance of itself to be created, see
Implementing Singleton in C#.
Can't be instantiated.
Is sealed.
Creating a static class is therefore basically the same as creating a class that contains
only static members and a private constructor. A private constructor prevents the class
from being instantiated. The advantage of using a static class is that the compiler can
check to make sure that no instance members are accidentally added. The compiler
guarantees that instances of this class can't be created.
Static classes are sealed and therefore can't be inherited. They can't inherit from any
class or interface except Object. Static classes can't contain an instance constructor.
However, they can contain a static constructor. Non-static classes should also define a
static constructor if the class contains static members that require non-trivial
initialization. For more information, see Static Constructors.
Example
Here's an example of a static class that contains two methods that convert temperature
from Celsius to Fahrenheit and from Fahrenheit to Celsius:
C#
return fahrenheit;
}
return celsius;
}
}
class TestTemperatureConverter
{
static void Main()
{
Console.WriteLine("Please select the convertor direction");
Console.WriteLine("1. From Celsius to Fahrenheit.");
Console.WriteLine("2. From Fahrenheit to Celsius.");
Console.Write(":");
switch (selection)
{
case "1":
Console.Write("Please enter the Celsius temperature: ");
F =
TemperatureConverter.CelsiusToFahrenheit(Console.ReadLine() ?? "0");
Console.WriteLine("Temperature in Fahrenheit: {0:F2}", F);
break;
case "2":
Console.Write("Please enter the Fahrenheit temperature: ");
C =
TemperatureConverter.FahrenheitToCelsius(Console.ReadLine() ?? "0");
Console.WriteLine("Temperature in Celsius: {0:F2}", C);
break;
default:
Console.WriteLine("Please select a convertor.");
break;
}
Static Members
A non-static class can contain static methods, fields, properties, or events. The static
member is callable on a class even when no instance of the class exists. The static
member is always accessed by the class name, not the instance name. Only one copy of
a static member exists, regardless of how many instances of the class are created. Static
methods and properties can't access non-static fields and events in their containing
type, and they can't access an instance variable of any object unless it's explicitly passed
in a method parameter.
It's more typical to declare a non-static class with some static members, than to declare
an entire class as static. Two common uses of static fields are to keep a count of the
number of objects that are instantiated, or to store a value that must be shared among
all instances.
Static methods can be overloaded but not overridden, because they belong to the class,
and not to any instance of the class.
Although a field can't be declared as static const , a const field is essentially static in its
behavior. It belongs to the type, not to instances of the type. Therefore, const fields can
be accessed by using the same ClassName.MemberName notation used for static fields. No
object instance is required.
C# doesn't support static local variables (that is, variables that are declared in method
scope).
You declare static class members by using the static keyword before the return type of
the member, as shown in the following example:
C#
Static members are initialized before the static member is accessed for the first time and
before the static constructor, if there's one, is called. To access a static class member, use
the name of the class instead of a variable name to specify the location of the member,
as shown in the following example:
C#
Automobile.Drive();
int i = Automobile.NumberOfWheels;
If your class contains static fields, provide a static constructor that initializes them when
the class is loaded.
C# Language Specification
For more information, see Static classes, Static and instance members and Static
constructors in the C# Language Specification. The language specification is the
definitive source for C# syntax and usage.
See also
static
Classes
class
Static Constructors
Instance Constructors
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Access Modifiers (C# Programming
Guide)
Article • 04/11/2024
All types and type members have an accessibility level. The accessibility level controls
whether they can be used from other code in your assembly or other assemblies. An
assembly is a .dll or .exe created by compiling one or more .cs files in a single
compilation. Use the following access modifiers to specify the accessibility of a type or
member when you declare it:
public: Code in any assembly can access this type or member. The accessibility
level of the containing type controls the accessibility level of public members of
the type.
private: Only code declared in the same class or struct can access this member.
protected: Only code in the same class or in a derived class can access this type
or member.
internal: Only code in the same assembly can access this type or member.
protected internal: Only code in the same assembly or in a derived class in another
assembly can access this type or member.
private protected: Only code in the same assembly and in the same class or a
derived class can access the type or member.
file: Only code in the same file can access the type or member.
The record modifier on a type causes the compiler to synthesize extra members. The
record modifier doesn't affect the default accessibility for either a record class or a
record struct .
Summary table
ノ Expand table
Derived class ✔️ ✔️ ✔️ ✔️ ✔️ ❌ ❌
(same
assembly)
Caller's public protected protected internal private private file
location internal protected
Non-derived ✔️ ✔️ ❌ ✔️ ❌ ❌ ❌
class (same
assembly)
Derived class ✔️ ✔️ ✔️ ❌ ❌ ❌ ❌
(different
assembly)
Non-derived ✔️ ❌ ❌ ❌ ❌ ❌ ❌
class (different
assembly)
The following examples demonstrate how to specify access modifiers on a type and
member:
C#
Not all access modifiers are valid for all types or members in all contexts. In some cases,
the accessibility of the containing type constrains the accessibility of its members.
When one declaration of a partial class or partial method doesn't declare its
accessibility, it has the accessibility of the other declaration. The compiler generates an
error if multiple declarations for the partial class or method declare different
accessibilities.
Struct members, including nested classes and structs, can be declared public , internal ,
or private . Class members, including nested classes and structs, can be public ,
protected internal , protected , internal , private protected , or private . Class and
struct members, including nested classes and structs, have private access by default.
Derived classes can't have greater accessibility than their base types. You can't declare a
public class B that derives from an internal class A . If allowed, it would have the effect
of making A public, because all protected or internal members of A are accessible
from the derived class.
You can enable specific other assemblies to access your internal types by using the
InternalsVisibleToAttribute . For more information, see Friend Assemblies.
Other types
Interfaces declared directly within a namespace can be public or internal and, just like
classes and structs, interfaces default to internal access. Interface members are public
by default because the purpose of an interface is to enable other types to access a class
or struct. Interface member declarations might include any access modifier. You use
access modifiers on interface members to provide a common implementation needed
by all implementors of an interface.
For more information about access modifiers, see the Accessibility Levels page.
Member accessibility
Members of a class or struct (including nested classes and structs) can be declared
with any of the six types of access. Struct members can't be declared as protected ,
protected internal , or private protected because structs don't support inheritance.
Normally, the accessibility of a member isn't greater than the accessibility of the type
that contains it. However, a public member of an internal class might be accessible
from outside the assembly if the member implements interface methods or overrides
virtual methods that are defined in a public base class.
The type of any member field, property, or event must be at least as accessible as the
member itself. Similarly, the return type and the parameter types of any method,
indexer, or delegate must be at least as accessible as the member itself. For example,
you can't have a public method M that returns a class C unless C is also public .
Likewise, you can't have a protected property of type A if A is declared as private .
User-defined operators must always be declared as public and static . For more
information, see Operator overloading.
To set the access level for a class or struct member, add the appropriate keyword to
the member declaration, as shown in the following example.
C#
// public class:
public class Tricycle
{
// protected method:
protected void Pedal() { }
// private field:
private int _wheels = 3;
Finalizers can't have accessibility modifiers. Members of an enum type are always public ,
and no access modifiers can be applied.
The file access modifier is allowed only on top-level (non-nested) type declarations.
C# language specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
See also
Specify modifier order (style rule IDE0036)
The C# type system
Interfaces
Accessibility Levels
private
public
internal
protected
protected internal
private protected
sealed
class
struct
interface
Anonymous types
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Fields (C# Programming Guide)
Article • 05/26/2023
A field is a variable of any type that is declared directly in a class or struct. Fields are
members of their containing type.
A class or struct may have instance fields, static fields, or both. Instance fields are
specific to an instance of a type. If you have a class T , with an instance field F , you can
create two objects of type T , and modify the value of F in each object without affecting
the value in the other object. By contrast, a static field belongs to the type itself, and is
shared among all instances of that type. You can access the static field only by using the
type name. If you access the static field by an instance name, you get CS0176 compile-
time error.
Generally, you should declare private or protected accessibility for fields. Data that
your type exposes to client code should be provided through methods, properties, and
indexers. By using these constructs for indirect access to internal fields, you can guard
against invalid input values. A private field that stores the data exposed by a public
property is called a backing store or backing field. You can declare public fields, but
then you can't prevent code that uses your type from setting that field to an invalid
value or otherwise changing an object's data.
Fields typically store the data that must be accessible to more than one type method
and must be stored for longer than the lifetime of any single method. For example, a
type that represents a calendar date might have three integer fields: one for the month,
one for the day, and one for the year. Variables that aren't used outside the scope of a
single method should be declared as local variables within the method body itself.
Fields are declared in the class or struct block by specifying the access level, followed by
the type, followed by the name of the field. For example:
C#
To access a field in an instance, add a period after the instance name, followed by the
name of the field, as in instancename._fieldName . For example:
C#
A field can be given an initial value by using the assignment operator when the field is
declared. To automatically assign the Day field to "Monday" , for example, you would
declare Day as in the following example:
C#
Fields are initialized immediately before the constructor for the object instance is called.
If the constructor assigns the value of a field, it overwrites any value given during field
declaration. For more information, see Using Constructors.
7 Note
Fields can be marked as public, private, protected, internal, protected internal, or private
protected. These access modifiers define how users of the type can access the fields. For
more information, see Access Modifiers.
A field can optionally be declared static. Static fields are available to callers at any time,
even if no instance of the type exists. For more information, see Static Classes and Static
Class Members.
A field can be declared readonly. A read-only field can only be assigned a value during
initialization or in a constructor. A static readonly field is similar to a constant, except
that the C# compiler doesn't have access to the value of a static read-only field at
compile time, only at run time. For more information, see Constants.
A field can be declared required. A required field must be initialized by the constructor,
or by an object initializers when an object is created. You add the
System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute attribute to any
constructor declaration that initializes all required members.
The required modifier can't be combined with the readonly modifier on the same field.
However, property can be required and init only.
C# language specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
See also
The C# type system
Using Constructors
Inheritance
Access Modifiers
Abstract and Sealed Classes and Class Members
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Constants (C# Programming Guide)
Article • 03/12/2024
Constants are immutable values which are known at compile time and do not change
for the life of the program. Constants are declared with the const modifier. Only the C#
built-in types may be declared as const . Reference type constants other than String can
only be initialized with a null value. User-defined types, including classes, structs, and
arrays, cannot be const . Use the readonly modifier to create a class, struct, or array that
is initialized one time at run time (for example in a constructor) and thereafter cannot be
changed.
The enum type enables you to define named constants for integral built-in types (for
example int , uint , long , and so on). For more information, see enum.
C#
class Calendar1
{
public const int Months = 12;
}
In this example, the constant Months is always 12, and it cannot be changed even by the
class itself. In fact, when the compiler encounters a constant identifier in C# source code
(for example, Months ), it substitutes the literal value directly into the intermediate
language (IL) code that it produces. Because there is no variable address associated with
a constant at run time, const fields cannot be passed by reference and cannot appear
as an l-value in an expression.
7 Note
Use caution when you refer to constant values defined in other code such as DLLs.
If a new version of the DLL defines a new value for the constant, your program will
still hold the old literal value until it is recompiled against the new version.
Multiple constants of the same type can be declared at the same time, for example:
C#
class Calendar2
{
public const int Months = 12, Weeks = 52, Days = 365;
}
The expression that is used to initialize a constant can refer to another constant if it
does not create a circular reference. For example:
C#
class Calendar3
{
public const int Months = 12;
public const int Weeks = 52;
public const int Days = 365;
Constants are accessed as if they were static fields because the value of the constant is
the same for all instances of the type. You do not use the static keyword to declare
them. Expressions that are not in the class that defines the constant must use the class
name, a period, and the name of the constant to access the constant. For example:
C#
C# Language Specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
See also
Properties
Types
readonly
Immutability in C# Part One: Kinds of Immutability
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to define abstract properties (C#
Programming Guide)
Article • 03/12/2024
The following example shows how to define abstract properties. An abstract property
declaration does not provide an implementation of the property accessors -- it declares
that the class supports properties, but leaves the accessor implementation to derived
classes. The following example demonstrates how to implement the abstract properties
inherited from a base class.
This sample consists of three files, each of which is compiled individually and its
resulting assembly is referenced by the next compilation:
shapetest.cs: A test program to display the areas of some Shape -derived objects.
Examples
This file declares the Shape class that contains the Area property of the type double .
C#
public Shape(string s)
{
// calling the set accessor of the Id property.
Id = s;
}
public string Id
{
get
{
return name;
}
set
{
name = value;
}
}
Modifiers on the property are placed on the property declaration itself. For
example:
C#
When declaring an abstract property (such as Area in this example), you simply
indicate what property accessors are available, but do not implement them. In this
example, only a get accessor is available, so the property is read-only.
The following code shows three subclasses of Shape and how they override the Area
property to provide their own implementation.
C#
The following code shows a test program that creates a number of Shape -derived
objects and prints out their areas.
C#
System.Console.WriteLine("Shapes Collection");
foreach (Shape s in shapes)
{
System.Console.WriteLine(s);
}
}
}
/* Output:
Shapes Collection
Square #1 Area = 25.00
Circle #1 Area = 28.27
Rectangle #1 Area = 20.00
*/
See also
The C# type system
Abstract and Sealed Classes and Class Members
Properties
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to define constants in C#
Article • 10/27/2021
Constants are fields whose values are set at compile time and can never be changed.
Use constants to provide meaningful names instead of numeric literals ("magic
numbers") for special values.
7 Note
To define constant values of integral types ( int , byte , and so on) use an enumerated
type. For more information, see enum.
To define non-integral constants, one approach is to group them in a single static class
named Constants . This will require that all references to the constants be prefaced with
the class name, as shown in the following example.
Example
C#
class Program
{
static void Main()
{
double radius = 5.3;
double area = Constants.Pi * (radius * radius);
int secsFromSun = 149476000 / Constants.SpeedOfLight; // in km
Console.WriteLine(secsFromSun);
}
}
The use of the class name qualifier helps ensure that you and others who use the
constant understand that it is constant and cannot be modified.
See also
The C# type system
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Properties (C# Programming Guide)
Article • 03/15/2024
Properties overview
Properties enable a class to expose a public way of getting and setting values,
while hiding implementation or verification code.
A get property accessor is used to return the property value, and a set property
accessor is used to assign a new value. An init property accessor is used to assign a
new value only during object construction. These accessors can have different
access levels. For more information, see Restricting Accessor Accessibility.
The value keyword is used to define the value the set or init accessor is
assigning.
Properties can be read-write (they have both a get and a set accessor), read-only
(they have a get accessor but no set accessor), or write-only (they have a set
accessor, but no get accessor). Write-only properties are rare and are most
commonly used to restrict access to sensitive data.
Simple properties that require no custom accessor code can be implemented
either as expression body definitions or as auto-implemented properties.
The following example illustrates this pattern. In this example, the TimePeriod class
represents an interval of time. Internally, the class stores the time interval in seconds in a
private field named _seconds . A read-write property named Hours allows the customer
to specify the time interval in hours. Both the get and the set accessors perform the
necessary conversion between hours and seconds. In addition, the set accessor
validates the data and throws an ArgumentOutOfRangeException if the number of
hours is invalid.
C#
You could access properties to get and set the value as shown in the following example:
C#
Both the get and the set accessor can be implemented as expression-bodied
members. In this case, the get and set keywords must be present. The following
example illustrates the use of expression body definitions for both accessors. The
return keyword isn't used with the get accessor.
C#
Auto-implemented properties
In some cases, property get and set accessors just assign a value to or retrieve a value
from a backing field without including any extra logic. By using auto-implemented
properties, you can simplify your code while having the C# compiler transparently
provide the backing field for you.
If a property has both a get and a set (or a get and an init ) accessor, both must be
auto-implemented. You define an auto-implemented property by using the get and
set keywords without providing any implementation. The following example repeats
the previous one, except that Name and Price are auto-implemented properties. The
example also removes the parameterized constructor, so that SaleItem objects are now
initialized with a call to the parameterless constructor and an object initializer.
C#
Auto-implemented properties can declare different accessibilities for the get and set
accessors. You commonly declare a public get accessor and a private set accessor. You
can learn more in the article on restricting accessor accessibility.
Required properties
Beginning with C# 11, you can add the required member to force client code to
initialize any property or field:
C#
C#
Related sections
Using Properties
Interface Properties
Comparison Between Properties and Indexers
Restricting Accessor Accessibility
Auto-Implemented Properties
C# Language Specification
For more information, see Properties in the C# Language Specification. The language
specification is the definitive source for C# syntax and usage.
See also
Indexers
init keyword
get keyword
set keyword
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Using Properties (C# Programming
Guide)
Article • 03/19/2024
Properties combine aspects of both fields and methods. To the user of an object, a
property appears to be a field; accessing the property requires the same syntax. To the
implementer of a class, a property is one or two code blocks, representing a get
accessor and/or a set or init accessor. The code block for the get accessor is executed
when the property is read; the code block for the set or init accessor is executed
when the property is assigned a value. A property without a set accessor is considered
read-only. A property without a get accessor is considered write-only. A property that
has both accessors is read-write. You can use an init accessor instead of a set
accessor to enable the property to be set as part of object initialization but otherwise
make it read-only.
Unlike fields, properties aren't classified as variables. Therefore, you can't pass a
property as a ref or out parameter.
Properties are declared in the class block by specifying the access level of the field,
followed by the type of the property, followed by the name of the property, and
followed by a code block that declares a get -accessor and/or a set accessor. For
example:
C#
In this example, Month is declared as a property so that the set accessor can make sure
that the Month value is set between 1 and 12. The Month property uses a private field to
track the actual value. The real location of a property's data is often referred to as the
property's "backing store." It's common for properties to use private fields as a backing
store. The field is marked private in order to make sure that it can only be changed by
calling the property. For more information about public and private access restrictions,
see Access Modifiers. Auto-implemented properties provide simplified syntax for simple
property declarations. For more information, see Auto-Implemented Properties.
C#
class Employee
{
private string _name; // the name field
public string Name => _name; // the Name property
}
When you reference the property, except as the target of an assignment, the get
accessor is invoked to read the value of the property. For example:
C#
2 Warning
It's a bad programming style to change the state of the object by using the get
accessor.
The get accessor can be used to return the field value or to compute it and return it. For
example:
C#
class Manager
{
private string _name;
public string Name => _name != null ? _name : "NA";
}
In the previous example, if you don't assign a value to the Name property, it returns the
value NA .
C#
class Student
{
private string _name; // the name field
public string Name // the Name property
{
get => _name;
set => _name = value;
}
}
When you assign a value to the property, the set accessor is invoked by using an
argument that provides the new value. For example:
C#
It's an error to use the implicit parameter name, value , for a local variable declaration in
a set accessor.
Remarks
Properties can be marked as public , private , protected , internal , protected
internal , or private protected . These access modifiers define how users of the class
can access the property. The get and set accessors for the same property can have
different access modifiers. For example, the get might be public to allow read-only
access from outside the type, and the set can be private or protected . For more
information, see Access Modifiers.
A property can be declared as a static property by using the static keyword. Static
properties are available to callers at any time, even if no instance of the class exists. For
more information, see Static Classes and Static Class Members.
A property can be marked as a virtual property by using the virtual keyword. Virtual
properties enable derived classes to override the property behavior by using the
override keyword. For more information about these options, see Inheritance.
A property overriding a virtual property can also be sealed, specifying that for derived
classes it's no longer virtual. Lastly, a property can be declared abstract. Abstract
properties don't define an implementation in the class, and derived classes must write
their own implementation. For more information about these options, see Abstract and
Sealed Classes and Class Members.
7 Note
Examples
This example demonstrates instance, static, and read-only properties. It accepts the
name of the employee from the keyboard, increments NumberOfEmployees by 1, and
displays the Employee name and number.
C#
// A Constructor:
public Employee() => _counter = ++NumberOfEmployees; // Calculate the
employee's number:
}
C#
public class Employee
{
private string _name;
public string Name
{
get => _name;
set => _name = value;
}
}
class TestHiding
{
public static void Test()
{
Manager m1 = new Manager();
The property Name in the derived class hides the property Name in the base class. In
such a case, the new modifier is used in the declaration of the property in the
derived class:
C#
The cast (Employee) is used to access the hidden property in the base class:
C#
((Employee)m1).Name = "Mary";
For more information about hiding members, see the new Modifier.
C#
//constructor
public Square(double s) => side = s;
class TestShapes
{
static void Main()
{
// Input the side:
System.Console.Write("Enter the side: ");
double side = double.Parse(System.Console.ReadLine());
See also
Properties
Interface Properties
Auto-Implemented Properties
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Interface Properties (C# Programming
Guide)
Article • 03/12/2024
C#
Interface properties typically don't have a body. The accessors indicate whether the
property is read-write, read-only, or write-only. Unlike in classes and structs, declaring
the accessors without a body doesn't declare an auto-implemented property. An
interface may define a default implementation for members, including properties.
Defining a default implementation for a property in an interface is rare because
interfaces may not define instance data fields.
Example
In this example, the interface IEmployee has a read-write property, Name , and a read-
only property, Counter . The class Employee implements the IEmployee interface and
uses these two properties. The program reads the name of a new employee and the
current number of employees and displays the employee name and the computed
employee number.
You could use the fully qualified name of the property, which references the interface in
which the member is declared. For example:
C#
string IEmployee.Name
{
get { return "Employee Name"; }
set { }
}
C#
string IEmployee.Name
{
get { return "Employee Name"; }
set { }
}
implements the Name property on the IEmployee interface, while the following
declaration:
C#
string ICitizen.Name
{
get { return "Citizen Name"; }
set { }
}
C#
interface IEmployee
{
string Name
{
get;
set;
}
int Counter
{
get;
}
}
// constructor
public Employee() => _counter = ++numberOfEmployees;
}
C#
Sample output
Console
See also
Properties
Using Properties
Comparison Between Properties and Indexers
Indexers
Interfaces
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Restricting Accessor Accessibility (C#
Programming Guide)
Article • 07/30/2022
The get and set portions of a property or indexer are called accessors. By default these
accessors have the same visibility or access level of the property or indexer to which
they belong. For more information, see accessibility levels. However, it's sometimes
useful to restrict access to one of these accessors. Typically, you restrict the accessibility
of the set accessor, while keeping the get accessor publicly accessible. For example:
C#
In this example, a property called Name defines a get and set accessor. The get
accessor receives the accessibility level of the property itself, public in this case, while
the set accessor is explicitly restricted by applying the protected access modifier to the
accessor itself.
7 Note
accessors.
If the property or indexer has an override modifier, the accessor modifier must
match the accessor of the overridden accessor, if any.
The accessibility level on the accessor must be more restrictive than the
accessibility level on the property or indexer itself.
C#
Implementing Interfaces
When you use an accessor to implement an interface, the accessor may not have an
access modifier. However, if you implement the interface using one accessor, such as
get , the other accessor can have an access modifier, as in the following example:
C#
If you didn't use an access modifier on the accessor, the accessibility domain of the
accessor is determined by the accessibility level of the property or indexer.
Example
The following example contains three classes, BaseClass , DerivedClass , and MainClass .
There are two properties on the BaseClass , Name and Id on both classes. The example
demonstrates how the property Id on DerivedClass can be hidden by the property Id
on BaseClass when you use a restrictive access modifier such as protected or private.
Therefore, when you assign values to this property, the property on the BaseClass class
is called instead. Replacing the access modifier by public will make the property
accessible.
The example also demonstrates that a restrictive access modifier, such as private or
protected , on the set accessor of the Name property in DerivedClass prevents access to
the accessor in the derived class. It generates an error when you assign to it, or accesses
the base class property of the same name, if it's accessible.
C#
public string Id
{
get { return _id; }
set { }
}
}
class MainClass
{
static void Main()
{
BaseClass b1 = new BaseClass();
DerivedClass d1 = new DerivedClass();
b1.Name = "Mary";
d1.Name = "John";
b1.Id = "Mary123";
d1.Id = "John123"; // The BaseClass.Id property is called.
Comments
Notice that if you replace the declaration new private string Id by new public string
Id , you get the output:
Name and ID in the base class: Name-BaseClass, ID-BaseClass Name and ID in the
derived class: John, John123
See also
Properties
Indexers
Access Modifiers
Init only properties
Required properties
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to declare and use read write
properties (C# Programming Guide)
Article • 07/30/2022
Properties provide the convenience of public data members without the risks that come
with unprotected, uncontrolled, and unverified access to an object's data. Properties
declare accessors: special methods that assign and retrieve values from the underlying
data member. The set accessor enables data members to be assigned, and the get
accessor retrieves data member values.
This sample shows a Person class that has two properties: Name (string) and Age (int).
Both properties provide get and set accessors, so they're considered read/write
properties.
Example
C#
class Person
{
private string _name = "N/A";
private int _age = 0;
set
{
_age = value;
}
}
class TestPerson
{
static void Main()
{
// Create a new Person object:
Person person = new Person();
// Print out the name and the age associated with the person:
Console.WriteLine("Person details - {0}", person);
Robust Programming
In the previous example, the Name and Age properties are public and include both a get
and a set accessor. Public accessors allow any object to read and write these properties.
It's sometimes desirable, however, to exclude one of the accessors. You can omit the
set accessor to make the property read-only:
C#
Alternatively, you can expose one accessor publicly but make the other private or
protected. For more information, see Asymmetric Accessor Accessibility.
Once the properties are declared, they can be used as fields of the class. Properties
allow for a natural syntax when both getting and setting the value of a property, as in
the following statements:
C#
person.Name = "Joe";
person.Age = 99;
In a property set method a special value variable is available. This variable contains the
value that the user specified, for example:
C#
_name = value;
Notice the clean syntax for incrementing the Age property on a Person object:
C#
person.Age += 1;
If separate set and get methods were used to model properties, the equivalent code
might look like this:
C#
person.SetAge(person.GetAge() + 1);
C#
Notice that ToString isn't explicitly used in the program. It's invoked by default by the
WriteLine calls.
See also
Properties
The C# type system
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Auto-Implemented Properties (C#
Programming Guide)
Article • 09/29/2022
Example
The following example shows a simple class that has some auto-implemented
properties:
C#
// Constructor
public Customer(double purchases, string name, int id)
{
TotalPurchases = purchases;
Name = name;
CustomerId = id;
}
// Methods
public string GetContactInfo() { return "ContactInfo"; }
public string GetTransactionHistory() { return "History"; }
class Program
{
static void Main()
{
// Initialize a new object.
Customer cust1 = new Customer(4987.63, "Northwind", 90108);
// Modify a property.
cust1.TotalPurchases += 499.99;
}
}
C#
The class that is shown in the previous example is mutable. Client code can change the
values in objects after creation. In complex classes that contain significant behavior
(methods) as well as data, it's often necessary to have public properties. However, for
small classes or structs that just encapsulate a set of values (data) and have little or no
behaviors, you should use one of the following options for making the objects
immutable:
For more information, see How to implement a lightweight class with auto-implemented
properties.
See also
Use auto-implemented properties (style rule IDE0032)
Properties
Modifiers
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to implement a lightweight class
with auto-implemented properties (C#
Programming Guide)
Article • 07/30/2022
This example shows how to create an immutable lightweight class that serves only to
encapsulate a set of auto-implemented properties. Use this kind of construct instead of
a struct when you must use reference type semantics.
Declare only the get accessor, which makes the property immutable everywhere
except in the type's constructor.
Declare an init accessor instead of a set accessor, which makes the property
settable only in the constructor or by using an object initializer.
Declare the set accessor to be private. The property is settable within the type, but
it's immutable to consumers.
You can add the required modifier to the property declaration to force callers to set the
property as part of initializing a new object.
The following example shows how a property with only get accessor differs than one
with get and private set.
C#
class Contact
{
public string Name { get; }
public string Address { get; private set; }
C#
// Public constructor.
public Contact(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
}
// Read-only property.
public string Address { get; }
// Private constructor.
private Contact2(string contactName, string contactAddress)
{
Name = contactName;
Address = contactAddress;
}
/* Output:
Terry Adams, 123 Main St.
Fadi Fakhouri, 345 Cypress Ave.
Hanying Feng, 678 1st Ave
Cesar Garcia, 12 108th St.
Debra Garcia, 89 E. 42nd St.
*/
The compiler creates backing fields for each auto-implemented property. The fields
aren't accessible directly from source code.
See also
Properties
struct
Object and Collection Initializers
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Methods (C# Programming Guide)
Article • 06/20/2023
A method is a code block that contains a series of statements. A program causes the
statements to be executed by calling the method and specifying any required method
arguments. In C#, every executed instruction is performed in the context of a method.
The Main method is the entry point for every C# application and it's called by the
common language runtime (CLR) when the program is started. In an application that
uses top-level statements, the Main method is generated by the compiler and contains
all top-level statements.
7 Note
This article discusses named methods. For information about anonymous functions,
see Lambda expressions.
Method signatures
Methods are declared in a class, struct, or interface by specifying the access level such as
public or private , optional modifiers such as abstract or sealed , the return value, the
name of the method, and any method parameters. These parts together are the
signature of the method.
) Important
A return type of a method is not part of the signature of the method for the
purposes of method overloading. However, it is part of the signature of the method
when determining the compatibility between a delegate and the method that it
points to.
Method parameters are enclosed in parentheses and are separated by commas. Empty
parentheses indicate that the method requires no parameters. This class contains four
methods:
C#
Method access
Calling a method on an object is like accessing a field. After the object name, add a
period, the name of the method, and parentheses. Arguments are listed within the
parentheses, and are separated by commas. The methods of the Motorcycle class can
therefore be called as in the following example:
C#
moto.StartEngine();
moto.AddGas(15);
moto.Drive(5, 20);
double speed = moto.GetTopSpeed();
Console.WriteLine("My top speed is {0}", speed);
}
}
C#
int Square(int i)
{
// Store input argument in a local variable.
int input = i;
return input * input;
}
You create a reference type by using the class keyword, as the following example
shows:
C#
public class SampleRefType
{
public int value;
}
Now, if you pass an object that is based on this type to a method, a reference to the
object is passed. The following example passes an object of type SampleRefType to
method ModifyObject :
C#
The example does essentially the same thing as the previous example in that it passes
an argument by value to a method. But, because a reference type is used, the result is
different. The modification that is made in ModifyObject to the value field of the
parameter, obj , also changes the value field of the argument, rt , in the TestRefType
method. The TestRefType method displays 33 as the output.
For more information about how to pass reference types by reference and by value, see
Passing Reference-Type Parameters and Reference Types.
Return values
Methods can return a value to the caller. If the return type (the type listed before the
method name) is not void , the method can return the value by using the return
statement. A statement with the return keyword followed by a value that matches the
return type will return that value to the method caller.
The value can be returned to the caller by value or by reference. Values are returned to
the caller by reference if the ref keyword is used in the method signature and it follows
each return keyword. For example, the following method signature and return
statement indicate that the method returns a variable named estDistance by reference
to the caller.
C#
The return keyword also stops the execution of the method. If the return type is void , a
return statement without a value is still useful to stop the execution of the method.
Without the return keyword, the method will stop executing when it reaches the end of
the code block. Methods with a non-void return type are required to use the return
keyword to return a value. For example, these two methods use the return keyword to
return integers:
C#
class SimpleMath
{
public int AddTwoNumbers(int number1, int number2)
{
return number1 + number2;
}
To use a value returned from a method, the calling method can use the method call
itself anywhere a value of the same type would be sufficient. You can also assign the
return value to a variable. For example, the following two code examples accomplish the
same goal:
C#
C#
result = obj.SquareANumber(obj.AddTwoNumbers(1, 2));
// The result is 9.
Console.WriteLine(result);
Using a local variable, in this case, result , to store a value is optional. It may help the
readability of the code, or it may be necessary if you need to store the original value of
the argument for the entire scope of the method.
To use a value returned by reference from a method, you must declare a ref local
variable if you intend to modify its value. For example, if the
Planet.GetEstimatedDistance method returns a Double value by reference, you can
C#
C#
If you mark a method with the async modifier, you can use the await operator in the
method. When control reaches an await expression in the async method, control returns
to the caller, and progress in the method is suspended until the awaited task completes.
When the task is complete, execution can resume in the method.
7 Note
An async method returns to the caller when either it encounters the first awaited
object that's not yet complete or it gets to the end of the async method, whichever
occurs first.
In the following example, DelayAsync is an async method that has a return type of
Task<TResult>. DelayAsync has a return statement that returns an integer. Therefore
the method declaration of DelayAsync must have a return type of Task<int> . Because
the return type is Task<int> , the evaluation of the await expression in
DoSomethingAsync produces an integer as the following statement demonstrates: int
The Main method is an example of an async method that has a return type of Task. It
goes to the DoSomethingAsync method, and because it is expressed with a single line, it
can omit the async and await keywords. Because DoSomethingAsync is an async method,
the task for the call to DoSomethingAsync must be awaited, as the following statement
shows: await DoSomethingAsync(); .
C#
class Program
{
static Task Main() => DoSomethingAsync();
static async Task DoSomethingAsync()
{
Task<int> delayTask = DelayAsync();
int result = await delayTask;
Console.WriteLine($"Result: {result}");
}
An async method can't declare any ref or out parameters, but it can call methods that
have such parameters.
For more information about async methods, see Asynchronous programming with async
and await and Async return types.
C#
public Point Move(int dx, int dy) => new Point(x + dx, y + dy);
public void Print() => Console.WriteLine(First + " " + Last);
// Works with operators, properties, and indexers too.
public static Complex operator +(Complex a, Complex b) => a.Add(b);
public string Name => First + " " + Last;
public Customer this[long id] => store.LookupCustomer(id);
If the method returns void or is an async method, then the body of the method must
be a statement expression (same as with lambdas). For properties and indexers, they
must be read only, and you don't use the get accessor keyword.
Iterators
An iterator performs a custom iteration over a collection, such as a list or an array. An
iterator uses the yield return statement to return each element one at a time. When a
yield return statement is reached, the current location in code is remembered.
Execution is restarted from that location when the iterator is called the next time.
C# language specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
See also
The C# type system
Access Modifiers
Static Classes and Static Class Members
Inheritance
Abstract and Sealed Classes and Class Members
params
out
ref
Method Parameters
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Local functions (C# Programming
Guide)
Article • 05/27/2023
Local functions are methods of a type that are nested in another member. They can only
be called from their containing member. Local functions can be declared in and called
from:
7 Note
In some cases, you can use a lambda expression to implement functionality also
supported by a local function. For a comparison, see Local functions vs. lambda
expressions.
Local functions make the intent of your code clear. Anyone reading your code can see
that the method is not callable except by the containing method. For team projects, they
also make it impossible for another developer to mistakenly call the method directly
from elsewhere in the class or struct.
C#
async
unsafe
static A static local function can't capture local variables or instance state.
extern An external local function must be static .
All local variables that are defined in the containing member, including its method
parameters, are accessible in a non-static local function.
Unlike a method definition, a local function definition cannot include the member access
modifier. Because all local functions are private, including an access modifier, such as
the private keyword, generates compiler error CS0106, "The modifier 'private' is not
valid for this item."
C#
You can apply attributes to a local function, its parameters and type parameters, as the
following example shows:
C#
#nullable enable
private static void Process(string?[] lines, string mark)
{
foreach (var line in lines)
{
if (IsValid(line))
{
// Processing logic...
}
}
The preceding example uses a special attribute to assist the compiler in static analysis in
a nullable context.
The following example defines an OddSequence method that enumerates odd numbers
in a specified range. Because it passes a number greater than 100 to the OddSequence
enumerator method, the method throws an ArgumentOutOfRangeException. As the
output from the example shows, the exception surfaces only when you iterate the
numbers, and not when you retrieve the enumerator.
C#
If you put iterator logic into a local function, argument validation exceptions are thrown
when you retrieve the enumerator, as the following example shows:
C#
return GetOddSequenceEnumerator();
IEnumerable<int> GetOddSequenceEnumerator()
{
for (int i = start; i <= end; i++)
{
if (i % 2 == 1)
yield return i;
}
}
}
}
// The example displays the output like this:
//
// Unhandled exception. System.ArgumentOutOfRangeException: end must be
less than or equal to 100. (Parameter 'end')
// at IteratorWithLocalExample.OddSequence(Int32 start, Int32 end) in
IteratorWithLocal.cs:line 22
// at IteratorWithLocalExample.Main() in IteratorWithLocal.cs:line 8
Let's examine the differences between the local function and lambda expression
implementations of the factorial algorithm. Here's the version using a local function:
C#
C#
public static int LambdaFactorial(int n)
{
Func<int, int> nthFactorial = default(Func<int, int>);
return nthFactorial(n);
}
Naming
Local functions are explicitly named like methods. Lambda expressions are anonymous
methods and need to be assigned to variables of a delegate type, typically either
Action or Func types. When you declare a local function, the process is like writing a
Beginning with C# 10, some lambda expressions have a natural type, which enables the
compiler to infer the return type and parameter types of the lambda expression.
Definite assignment
Lambda expressions are objects that are declared and assigned at run time. In order for
a lambda expression to be used, it needs to be definitely assigned: the Action / Func
variable that it will be assigned to must be declared and the lambda expression assigned
to it. Notice that LambdaFactorial must declare and initialize the lambda expression
nthFactorial before defining it. Not doing so results in a compile time error for
Local functions are defined at compile time. As they're not assigned to variables, they
can be referenced from any code location where it is in scope; in our first example
LocalFunctionFactorial , we could declare our local function either above or below the
Implementation as a delegate
Lambda expressions are converted to delegates when they're declared. Local functions
are more flexible in that they can be written like a traditional method or as a delegate.
Local functions are only converted to delegates when used as a delegate.
If you declare a local function and only reference it by calling it like a method, it will not
be converted to a delegate.
Variable capture
The rules of definite assignment also affect any variables that are captured by the local
function or lambda expression. The compiler can perform static analysis that enables
local functions to definitely assign captured variables in the enclosing scope. Consider
this example:
C#
int M()
{
int y;
LocalFunction();
return y;
The compiler can determine that LocalFunction definitely assigns y when called.
Because LocalFunction is called before the return statement, y is definitely assigned at
the return statement.
Note that when a local function captures variables in the enclosing scope, the local
function is implemented using a closure, like delegate types are.
Heap allocations
Depending on their use, local functions can avoid heap allocations that are always
necessary for lambda expressions. If a local function is never converted to a delegate,
and none of the variables captured by the local function are captured by other lambdas
or local functions that are converted to delegates, the compiler can avoid heap
allocations.
C#
The closure for this lambda expression contains the address , index and name variables.
In the case of local functions, the object that implements the closure may be a struct
type. That struct type would be passed by reference to the local function. This difference
in implementation would save on an allocation.
The instantiation necessary for lambda expressions means extra memory allocations,
which may be a performance factor in time-critical code paths. Local functions do not
incur this overhead. In the example above, the local functions version has two fewer
allocations than the lambda expression version.
If you know that your local function won't be converted to a delegate and none of the
variables captured by it are captured by other lambdas or local functions that are
converted to delegates, you can guarantee that your local function avoids being
allocated on the heap by declaring it as a static local function.
Tip
Enable .NET code style rule IDE0062 to ensure that local functions are always
marked static .
7 Note
The local function equivalent of this method also uses a class for the closure.
Whether the closure for a local function is implemented as a class or a struct is
an implementation detail. A local function may use a struct whereas a lambda will
always use a class .
C#
return LowercaseIterator();
IEnumerable<string> LowercaseIterator()
{
foreach (var output in input.Select(item => item.ToLower()))
{
yield return output;
}
}
}
The yield return statement is not allowed in lambda expressions. For more
information, see compiler error CS1621.
While local functions may seem redundant to lambda expressions, they actually serve
different purposes and have different uses. Local functions are more efficient for the
case when you want to write a function that is called only from the context of another
method.
C# language specification
For more information, see the Local function declarations section of the C# language
specification.
See also
Use local function instead of lambda (style rule IDE0039)
Methods
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review Open a documentation issue
issues and pull requests. For
more information, see our Provide product feedback
contributor guide.
Implicitly typed local variables (C#
Programming Guide)
Article • 03/14/2023
Local variables can be declared without giving an explicit type. The var keyword
instructs the compiler to infer the type of the variable from the expression on the right
side of the initialization statement. The inferred type may be a built-in type, an
anonymous type, a user-defined type, or a type defined in the .NET class library. For
more information about how to initialize arrays with var , see Implicitly Typed Arrays.
The following examples show various ways in which local variables can be declared with
var :
C#
// i is compiled as an int
var i = 5;
// s is compiled as a string
var s = "Hello";
// a is compiled as int[]
var a = new[] { 0, 1, 2 };
It is important to understand that the var keyword does not mean "variant" and does
not indicate that the variable is loosely typed, or late-bound. It just means that the
compiler determines and assigns the most appropriate type.
C#
C#
In a using statement.
C#
For more information, see How to use implicitly typed local variables and arrays in a
query expression.
scenario in LINQ query expressions. For more information, see Anonymous Types.
From the perspective of your source code, an anonymous type has no name. Therefore,
if a query variable has been initialized with var , then the only way to access the
properties in the returned sequence of objects is to use var as the type of the iteration
variable in the foreach statement.
C#
class ImplicitlyTypedLocals2
{
static void Main()
{
string[] words = { "aPPLE", "BlUeBeRrY", "cHeRry" };
Remarks
The following restrictions apply to implicitly-typed variable declarations:
var can only be used when a local variable is declared and initialized in the same
statement; the variable cannot be initialized to null, or to a method group or an
anonymous function.
If a type named var is in scope, then the var keyword will resolve to that type
name and will not be treated as part of an implicitly typed local variable
declaration.
Implicit typing with the var keyword can only be applied to variables at local method
scope. Implicit typing is not available for class fields as the C# compiler would encounter
a logical paradox as it processed the code: the compiler needs to know the type of the
field, but it cannot determine the type until the assignment expression is analyzed, and
the expression cannot be evaluated without knowing the type. Consider the following
code:
C#
private var bookTitles;
bookTitles is a class field given the type var . As the field has no expression to evaluate,
it is impossible for the compiler to infer what type bookTitles is supposed to be. In
addition, adding an expression to the field (like you would for a local variable) is also
insufficient:
C#
When the compiler encounters fields during code compilation, it records each field's
type before processing any expressions associated with it. The compiler encounters the
same paradox trying to parse bookTitles : it needs to know the type of the field, but the
compiler would normally determine var 's type by analyzing the expression, which isn't
possible without knowing the type beforehand.
You may find that var can also be useful with query expressions in which the exact
constructed type of the query variable is difficult to determine. This can occur with
grouping and ordering operations.
The var keyword can also be useful when the specific type of the variable is tedious to
type on the keyboard, or is obvious, or does not add to the readability of the code. One
example where var is helpful in this manner is with nested generic types such as those
used with group operations. In the following query, the type of the query variable is
IEnumerable<IGrouping<string, Student>> . As long as you and others who must
maintain your code understand this, there is no problem with using implicit typing for
convenience and brevity.
C#
// Same as previous example except we use the entire last name as a key.
// Query variable is an IEnumerable<IGrouping<string, Student>>
var studentQuery3 =
from student in students
group student by student.Last;
The use of var helps simplify your code, but its use should be restricted to cases where
it is required, or when it makes your code easier to read. For more information about
when to use var properly, see the Implicitly typed local variables section on the C#
Coding Guidelines article.
See also
C# Reference
Implicitly Typed Arrays
How to use implicitly typed local variables and arrays in a query expression
Anonymous Types
Object and Collection Initializers
var
LINQ in C#
LINQ (Language-Integrated Query)
Iteration statements
using statement
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to use implicitly typed local
variables and arrays in a query
expression (C# Programming Guide)
Article • 09/21/2022
You can use implicitly typed local variables whenever you want the compiler to
determine the type of a local variable. You must use implicitly typed local variables to
store anonymous types, which are often used in query expressions. The following
examples illustrate both optional and required uses of implicitly typed local variables in
queries.
Implicitly typed local variables are declared by using the var contextual keyword. For
more information, see Implicitly Typed Local Variables and Implicitly Typed Arrays.
Examples
The following example shows a common scenario in which the var keyword is required:
a query expression that produces a sequence of anonymous types. In this scenario, both
the query variable and the iteration variable in the foreach statement must be implicitly
typed by using var because you do not have access to a type name for the anonymous
type. For more information about anonymous types, see Anonymous Types.
C#
for convenience. In the example, the iteration variable in the foreach statement is
explicitly typed as a string, but it could instead be declared by using var . Because the
type of the iteration variable is not an anonymous type, the use of var is an option, not
a requirement. Remember, var itself is not a type, but an instruction to the compiler to
infer and assign the type.
C#
See also
Extension Methods
LINQ (Language-Integrated Query)
LINQ in C#
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Extension Methods (C# Programming
Guide)
Article • 03/15/2024
Extension methods enable you to "add" methods to existing types without creating a
new derived type, recompiling, or otherwise modifying the original type. Extension
methods are static methods, but they're called as if they were instance methods on the
extended type. For client code written in C#, F# and Visual Basic, there's no apparent
difference between calling an extension method and the methods defined in a type.
The most common extension methods are the LINQ standard query operators that add
query functionality to the existing System.Collections.IEnumerable and
System.Collections.Generic.IEnumerable<T> types. To use the standard query operators,
first bring them into scope with a using System.Linq directive. Then any type that
implements IEnumerable<T> appears to have instance methods such as GroupBy,
OrderBy, Average, and so on. You can see these additional methods in IntelliSense
statement completion when you type "dot" after an instance of an IEnumerable<T> type
such as List<T> or Array.
OrderBy Example
The following example shows how to call the standard query operator OrderBy method
on an array of integers. The expression in parentheses is a lambda expression. Many
standard query operators take lambda expressions as parameters, but this isn't a
requirement for extension methods. For more information, see Lambda Expressions.
C#
class ExtensionMethods2
{
The following example shows an extension method defined for the System.String class.
It's defined inside a non-nested, non-generic static class:
C#
namespace ExtensionMethods
{
public static class MyExtensions
{
public static int WordCount(this string str)
{
return str.Split(new char[] { ' ', '.', '?' },
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}
The WordCount extension method can be brought into scope with this using directive:
C#
using ExtensionMethods;
C#
You invoke the extension method in your code with instance method syntax. The
intermediate language (IL) generated by the compiler translates your code into a call on
the static method. The principle of encapsulation isn't really being violated. Extension
methods can't access private variables in the type they're extending.
Both the MyExtensions class and the WordCount method are static , and it can be
accessed like all other static members. The WordCount method can be invoked like
other static methods as follows:
C#
string s = "Hello Extension Methods";
int i = MyExtensions.WordCount(s);
Declares and assigns a new string named s with a value of "Hello Extension
Methods" .
For more information, see How to implement and call a custom extension method.
In general, you'll probably be calling extension methods far more often than
implementing your own. Because extension methods are called by using instance
method syntax, no special knowledge is required to use them from client code. To
enable extension methods for a particular type, just add a using directive for the
namespace in which the methods are defined. For example, to use the standard query
operators, add this using directive to your code:
C#
using System.Linq;
(You may also have to add a reference to System.Core.dll.) You'll notice that the standard
query operators now appear in IntelliSense as additional methods available for most
IEnumerable<T> types.
Example
The following example demonstrates the rules that the C# compiler follows in
determining whether to bind a method call to an instance method on the type, or to an
extension method. The static class Extensions contains extension methods defined for
any type that implements IMyInterface . Classes A , B , and C all implement the
interface.
The MethodB extension method is never called because its name and signature exactly
match methods already implemented by the classes.
When the compiler can't find an instance method with a matching signature, it will bind
to a matching extension method if one exists.
C#
// Define three classes that implement IMyInterface, and then use them to
test
// the extension methods.
namespace ExtensionMethodsDemo1
{
using System;
using Extensions;
using DefineIMyInterface;
class A : IMyInterface
{
public void MethodB() { Console.WriteLine("A.MethodB()"); }
}
class B : IMyInterface
{
public void MethodB() { Console.WriteLine("B.MethodB()"); }
public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)");
}
}
class C : IMyInterface
{
public void MethodB() { Console.WriteLine("C.MethodB()"); }
public void MethodA(object obj)
{
Console.WriteLine("C.MethodA(object obj)");
}
}
class ExtMethodDemo
{
static void Main(string[] args)
{
// Declare an instance of class A, class B, and class C.
A a = new A();
B b = new B();
C c = new C();
Collection Functionality
In the past, it was common to create "Collection Classes" that implemented the
System.Collections.Generic.IEnumerable<T> interface for a given type and contained
functionality that acted on collections of that type. While there's nothing wrong with
creating this type of collection object, the same functionality can be achieved by using
an extension on the System.Collections.Generic.IEnumerable<T>. Extensions have the
advantage of allowing the functionality to be called from any collection such as an
System.Array or System.Collections.Generic.List<T> that implements
System.Collections.Generic.IEnumerable<T> on that type. An example of this using an
Array of Int32 can be found earlier in this article.
Layer-Specific Functionality
When using an Onion Architecture or other layered application design, it's common to
have a set of Domain Entities or Data Transfer Objects that can be used to communicate
across application boundaries. These objects generally contain no functionality, or only
minimal functionality that applies to all layers of the application. Extension methods can
be used to add functionality that is specific to each application layer without loading the
object down with methods not needed or wanted in other layers.
C#
can appear before or after the this keyword without any semantic differences. Adding
the ref modifier indicates that the first argument is passed by reference. This enables
you to write extension methods that change the state of the struct being extended (note
that private members aren't accessible). Only value types or generic types constrained to
struct (see struct constraint for more information) are allowed as the first parameter of a
ref extension method. The following example shows how to use a ref extension
method to directly modify a built-in type without the need to reassign the result or pass
it through a function with the ref keyword:
C#
This next example demonstrates ref extension methods for user-defined struct types:
C#
public struct Account
{
public uint id;
public float balance;
account.Deposit(50f);
Console.WriteLine($"I have ${account.balance}"); // I have $150
}
}
General Guidelines
While it's still considered preferable to add functionality by modifying an object's code
or deriving a new type whenever it's reasonable and possible to do so, extension
methods have become a crucial option for creating reusable functionality throughout
the .NET ecosystem. For those occasions when the original source isn't under your
control, when a derived object is inappropriate or impossible, or when the functionality
shouldn't be exposed beyond its applicable scope, Extension methods are an excellent
choice.
If you do implement extension methods for a given type, remember the following
points:
An extension method is not called if it has the same signature as a method defined
in the type.
Extension methods are brought into scope at the namespace level. For example, if
you have multiple static classes that contain extension methods in a single
namespace named Extensions , they'll all be brought into scope by the using
Extensions; directive.
For a class library that you implemented, you shouldn't use extension methods to avoid
incrementing the version number of an assembly. If you want to add significant
functionality to a library for which you own the source code, follow the .NET guidelines
for assembly versioning. For more information, see Assembly Versioning.
See also
Parallel Programming Samples (these include many example extension methods)
Lambda Expressions
Standard Query Operators Overview
Conversion rules for Instance parameters and their impact
Extension methods Interoperability between languages
Extension methods and Curried Delegates
Extension method Binding and Error reporting
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to implement and call a custom
extension method (C# Programming
Guide)
Article • 03/12/2024
This topic shows how to implement your own extension methods for any .NET type.
Client code can use your extension methods by adding a reference to the DLL that
contains them, and adding a using directive that specifies the namespace in which the
extension methods are defined.
The class must be visible to client code. For more information about accessibility
rules, see Access Modifiers.
2. Implement the extension method as a static method with at least the same
visibility as the containing class.
3. The first parameter of the method specifies the type that the method operates on;
it must be preceded with the this modifier.
4. In the calling code, add a using directive to specify the namespace that contains
the extension method class.
Note that the first parameter is not specified by calling code because it represents
the type on which the operator is being applied, and the compiler already knows
the type of your object. You only have to provide arguments for parameters 2
through n .
Example
The following example implements an extension method named WordCount in the
CustomExtensions.StringExtension class. The method operates on the String class, which
namespace CustomExtensions
{
// Extension methods must be defined in a static class.
public static class StringExtension
{
// This is the extension method.
// The first parameter takes the "this" modifier
// and specifies the type for which the method is defined.
public static int WordCount(this string str)
{
return str.Split(new char[] {' ', '.','?'},
StringSplitOptions.RemoveEmptyEntries).Length;
}
}
}
namespace Extension_Methods_Simple
{
// Import the extension method namespace.
using CustomExtensions;
class Program
{
static void Main(string[] args)
{
string s = "The quick brown fox jumped over the lazy dog.";
// Call the method as if it were an
// instance method on the type. Note that the first
// parameter is not specified by the calling code.
int i = s.WordCount();
System.Console.WriteLine("Word count of s is {0}", i);
}
}
}
.NET Security
Extension methods present no specific security vulnerabilities. They can never be used to
impersonate existing methods on a type, because all name collisions are resolved in
favor of the instance or static method defined by the type itself. Extension methods
cannot access any private data in the extended class.
See also
Extension Methods
LINQ (Language-Integrated Query)
Static Classes and Static Class Members
protected
internal
public
this
namespace
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to create a new method for an
enumeration (C# Programming Guide)
Article • 03/12/2024
You can use extension methods to add functionality specific to a particular enum type.
Example
In the following example, the Grades enumeration represents the possible letter grades
that a student may receive in a class. An extension method named Passing is added to
the Grades type so that each instance of that type now "knows" whether it represents a
passing grade or not.
C#
using System;
namespace EnumExtension
{
// Define an extension method in a non-nested static class.
public static class Extensions
{
public static Grades minPassing = Grades.D;
public static bool Passing(this Grades grade)
{
return grade >= minPassing;
}
}
Extensions.minPassing = Grades.C;
Console.WriteLine("\r\nRaising the bar!\r\n");
Console.WriteLine("First {0} a passing grade.", g1.Passing() ?
"is" : "is not");
Console.WriteLine("Second {0} a passing grade.", g2.Passing() ?
"is" : "is not");
}
}
}
/* Output:
First is a passing grade.
Second is not a passing grade.
Note that the Extensions class also contains a static variable that is updated
dynamically and that the return value of the extension method reflects the current value
of that variable. This demonstrates that, behind the scenes, extension methods are
invoked directly on the static class in which they are defined.
See also
Extension Methods
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Named and Optional Arguments (C#
Programming Guide)
Article • 03/19/2024
Named arguments enable you to specify an argument for a parameter by matching the
argument with its name rather than with its position in the parameter list. Optional
arguments enable you to omit arguments for some parameters. Both techniques can be
used with methods, indexers, constructors, and delegates.
When you use named and optional arguments, the arguments are evaluated in the
order in which they appear in the argument list, not the parameter list.
Named and optional parameters enable you to supply arguments for selected
parameters. This capability greatly eases calls to COM interfaces such as the Microsoft
Office Automation APIs.
Named arguments
Named arguments free you from matching the order of arguments to the order of
parameters in the parameter lists of called methods. The argument for each parameter
can be specified by parameter name. For example, a function that prints order details
(such as, seller name, order number & product name) can be called by sending
arguments by position, in the order defined by the function.
C#
If you don't remember the order of the parameters but know their names, you can send
the arguments in any order.
C#
Named arguments also improve the readability of your code by identifying what each
argument represents. In the example method below, the sellerName can't be null or
white space. As both sellerName and productName are string types, instead of sending
arguments by position, it makes sense to use named arguments to disambiguate the
two and reduce confusion for anyone reading the code.
Named arguments, when used with positional arguments, are valid as long as
C#
they're used in the correct position. In the example below, the parameter orderNum
is in the correct position but isn't explicitly named.
C#
Positional arguments that follow any out-of-order named arguments are invalid.
C#
Example
The following code implements the examples from this section along with some
additional ones.
C#
class NamedExample
{
static void Main(string[] args)
{
// The method can be called in the normal way, by using positional
arguments.
PrintOrderDetails("Gift Shop", 31, "Red Mug");
Optional arguments
The definition of a method, constructor, indexer, or delegate can specify its parameters
are required or optional. Any call must provide arguments for all required parameters,
but can omit arguments for optional parameters.
Each optional parameter has a default value as part of its definition. If no argument is
sent for that parameter, the default value is used. A default value must be one of the
following types of expressions:
a constant expression;
an expression of the form new ValType() , where ValType is a value type, such as an
enum or a struct;
an expression of the form default(ValType), where ValType is a value type.
Optional parameters are defined at the end of the parameter list, after any required
parameters. If the caller provides an argument for any one of a succession of optional
parameters, it must provide arguments for all preceding optional parameters. Comma-
separated gaps in the argument list aren't supported. For example, in the following
code, instance method ExampleMethod is defined with one required and two optional
parameters.
C#
C#
//anExample.ExampleMethod(3, ,4);
However, if you know the name of the third parameter, you can use a named argument
to accomplish the task.
C#
7 Note
You can also declare optional parameters by using the .NET OptionalAttribute
class. OptionalAttribute parameters do not require a default value. However, if a
default value is desired, take a look at DefaultParameterValueAttribute class.
Example
In the following example, the constructor for ExampleClass has one parameter, which is
optional. Instance method ExampleMethod has one required parameter, required , and
two optional parameters, optionalstr and optionalint . The code in Main shows the
different ways in which the constructor and method can be invoked.
C#
namespace OptionalNamespace
{
class OptionalExample
{
static void Main(string[] args)
{
// Instance anExample does not send an argument for the
constructor's
// optional parameter.
ExampleClass anExample = new ExampleClass();
anExample.ExampleMethod(1, "One", 1);
anExample.ExampleMethod(2, "Two");
anExample.ExampleMethod(3);
class ExampleClass
{
private string _name;
The preceding code shows a number of examples where optional parameters aren't
applied correctly. The first illustrates that an argument must be supplied for the first
parameter, which is required.
These attributes are optional parameters with default values provided by the compiler.
The caller should not explicitly provide a value for these parameters.
COM interfaces
Named and optional arguments, along with support for dynamic objects, greatly
improve interoperability with COM APIs, such as Office Automation APIs.
For example, the AutoFormat method in the Microsoft Office Excel Range interface has
seven parameters, all of which are optional. These parameters are shown in the
following illustration:
However, you can greatly simplify the call to AutoFormat by using named and optional
arguments. Named and optional arguments enable you to omit the argument for an
optional parameter if you don't want to change the parameter's default value. In the
following call, a value is specified for only one of the seven parameters.
C#
var myFormat =
Microsoft.Office.Interop.Excel.XlRangeAutoFormat.xlRangeAutoFormatAccounting
1;
For more information and examples, see How to use named and optional arguments in
Office programming and How to access Office interop objects by using C# features.
Overload resolution
Use of named and optional arguments affects overload resolution in the following ways:
C# language specification
For more information, see the C# Language Specification. The language specification is
the definitive source for C# syntax and usage.
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Constructors (C# programming guide)
Article • 04/10/2023
There are several actions that are part of initializing a new instance. Those actions take
place in the following order:
The preceding actions take place when a new instance is initialized. If a new instance of
a struct is set to its default value, all instance fields are set to 0.
If the static constructor hasn't run, the static constructor runs before any of the instance
constructor actions take place.
Constructor syntax
A constructor is a method whose name is the same as the name of its type. Its method
signature includes only an optional access modifier, the method name and its parameter
list; it does not include a return type. The following example shows the constructor for a
class named Person .
C#
C#
Static constructors
The previous examples have all shown instance constructors, which create a new object.
A class or struct can also have a static constructor, which initializes static members of
the type. Static constructors are parameterless. If you don't provide a static constructor
to initialize static fields, the C# compiler initializes static fields to their default value as
listed in the Default values of C# types article.
C#
You can also define a static constructor with an expression body definition, as the
following example shows.
C#
In This Section
Using constructors
Instance constructors
Private constructors
Static constructors
How to write a copy constructor
See also
The C# type system
Finalizers
static
Why Do Initializers Run In The Opposite Order As Constructors? Part One
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Using Constructors (C# Programming
Guide)
Article • 05/26/2023
When a class or struct is instantiated, its constructor is called. Constructors have the
same name as the class or struct, and they usually initialize the data members of the
new object.
In the following example, a class named Taxi is defined by using a simple constructor.
This class is then instantiated with the new operator. The Taxi constructor is invoked by
the new operator immediately after memory is allocated for the new object.
C#
public Taxi()
{
IsInitialized = true;
}
}
class TestTaxi
{
static void Main()
{
Taxi t = new Taxi();
Console.WriteLine(t.IsInitialized);
}
}
Unless the class is static, classes without constructors are given a public parameterless
constructor by the C# compiler in order to enable class instantiation. For more
information, see Static Classes and Static Class Members.
You can prevent a class from being instantiated by making the constructor private, as
follows:
C#
class NLog
{
// Private Constructor:
private NLog() { }
Constructors for struct types resemble class constructors. When a struct type is
instantiated with new , a constructor is invoked. When a struct is set to its default
value, the runtime initializes all memory in the struct to 0. Prior to C# 10, structs can't
contain an explicit parameterless constructor because one is provided automatically by
the compiler. For more information, see the Struct initialization and default values
section of the Structure types article.
The following code uses the parameterless constructor for Int32, so that you're assured
that the integer is initialized:
C#
The following code, however, causes a compiler error because it doesn't use new , and
because it tries to use an object that hasn't been initialized:
C#
int i;
Console.WriteLine(i);
Alternatively, objects based on structs (including all built-in numeric types) can be
initialized or assigned and then used as in the following example:
C#
Both classes and structs can define constructors that take parameters, including primary
constructors. Constructors that take parameters must be called through a new statement
or a base statement. Classes and structs can also define multiple constructors, and
neither is required to define a parameterless constructor. For example:
C#
public Employee() { }
C#
A constructor can use the base keyword to call the constructor of a base class. For
example:
C#
In a derived class, if a base-class constructor isn't called explicitly by using the base
keyword, the parameterless constructor, if there's one, is called implicitly. The following
constructor declarations are effectively the same:
C#
C#
If a base class doesn't offer a parameterless constructor, the derived class must make an
explicit call to a base constructor by using base .
A constructor can invoke another constructor in the same object by using the this
keyword. Like base , this can be used with or without parameters, and any parameters
in the constructor are available as parameters to this , or as part of an expression. For
example, the second constructor in the previous example can be rewritten using this :
C#
The use of the this keyword in the previous example causes this constructor to be
called:
C#
public Employee(int annualSalary)
{
Salary = annualSalary;
}
A constructor can be declared static by using the static keyword. Static constructors are
called automatically, immediately before any static fields are accessed, and are used to
initialize static class members. For more information, see Static Constructors.
C# Language Specification
For more information, see Instance constructors and Static constructors in the C#
Language Specification. The language specification is the definitive source for C# syntax
and usage.
See also
The C# type system
Constructors
Finalizers
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Instance constructors (C# programming
guide)
Article • 05/31/2024
You declare an instance constructor to specify the code that is executed when you
create a new instance of a type with the new expression. To initialize a static class or
static variables in a nonstatic class, you can define a static constructor.
As the following example shows, you can declare several instance constructors in one
type:
C#
class Coords
{
public Coords()
: this(0, 0)
{ }
class Example
{
static void Main()
{
var p1 = new Coords();
Console.WriteLine($"Coords #1 at {p1}");
// Output: Coords #1 at (0,0)
In the preceding example, the first, parameterless, constructor calls the second
constructor with both arguments equal 0 . To do that, use the this keyword.
When you declare an instance constructor in a derived class, you can call a constructor
of a base class. To do that, use the base keyword, as the following example shows:
C#
class Example
{
static void Main()
{
double radius = 2.5;
double height = 3.0;
Parameterless constructors
If a class has no explicit instance constructors, C# provides a parameterless constructor
that you can use to instantiate an instance of that class, as the following example shows:
C#
class Example
{
static void Main()
{
var person = new Person();
Console.WriteLine($"Name: {person.name}, Age: {person.age}");
// Output: Name: unknown, Age: 0
}
}
That constructor initializes instance fields and properties according to the corresponding
initializers. If a field or property has no initializer, its value is set to the default value of
the field's or property's type. If you declare at least one instance constructor in a class,
C# doesn't provide a parameterless constructor.
Primary constructors
Beginning in C# 12, you can declare a primary constructor in classes and structs. You
place any parameters in parentheses following the type name:
C#
public class NamedItem(string name)
{
public string Name => name;
}
The parameters to a primary constructor are in scope in the entire body of the declaring
type. They can initialize properties or fields. They can be used as variables in methods or
local functions. They can be passed to a base constructor.
A primary constructor indicates that these parameters are necessary for any instance of
the type. Any explicitly written constructor must use the this(...) initializer syntax to
invoke the primary constructor. That ensures that the primary constructor parameters
are definitely assigned by all constructors. For any class type, including record class
types, the implicit parameterless constructor isn't emitted when a primary constructor is
present. For any struct type, including record struct types, the implicit parameterless
constructor is always emitted, and always initializes all fields, including primary
constructor parameters, to the 0-bit pattern. If you write an explicit parameterless
constructor, it must invoke the primary constructor. In that case, you can specify a
different value for the primary constructor parameters. The following code shows
examples of primary constructors.
C#
You can add attributes to the synthesized primary constructor method by specifying the
method: target on the attribute:
C#
[method: MyAttribute]
public class TaggedWidget(string name)
{
// details elided
}
If you don't specify the method target, the attribute is placed on the class rather than the
method.
In class and struct types, primary constructor parameters are available anywhere in
the body of the type. The parameter can be implemented as a captured private field. If
the only references to a parameter are initializers and constructor calls, that parameter
isn't captured in a private field. Uses in other members of the type cause the compiler to
capture the parameter in a private field.
If the type includes the record modifier, the compiler instead synthesizes a public
property with the same name as the primary constructor parameter. For record class
types, if a primary constructor parameter uses the same name as a base primary
constructor, that property is a public property of the base record class type. It isn't
duplicated in the derived record class type. These properties aren't generated for non-
record types.
See also
Classes, structs, and records
Constructors
Finalizers
base
this
Primary constructors feature spec
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Private Constructors (C# Programming
Guide)
Article • 01/12/2022
C#
class NLog
{
// Private Constructor:
private NLog() { }
Private constructors are used to prevent creating instances of a class when there are no
instance fields or methods, such as the Math class, or when a method is called to obtain
an instance of a class. If all the methods in the class are static, consider making the
complete class static. For more information see Static Classes and Static Class Members.
Example
The following is an example of a class using a private constructor.
C#
class TestCounter
{
static void Main()
{
// If you uncomment the following statement, it will generate
// an error because the constructor is inaccessible:
// Counter aCounter = new Counter(); // Error
Counter.currentCount = 100;
Counter.IncrementCount();
Console.WriteLine("New count: {0}", Counter.currentCount);
Notice that if you uncomment the following statement from the example, it will
generate an error because the constructor is inaccessible because of its protection level:
C#
See also
The C# type system
Constructors
Finalizers
private
public
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Static Constructors (C# Programming
Guide)
Article • 08/02/2024
A static constructor is used to initialize any static data, or to perform a particular action
that needs to be performed only once. It's called automatically before the first instance
is created or any static members are referenced. A static constructor is called at most
once.
C#
class SimpleClass
{
// Static variable that must be initialized at run time.
static readonly long baseline;
There are several actions that are part of static initialization. Those actions take place in
the following order:
1. Static fields are set to 0. The runtime typically does this initialization.
2. Static field initializers run. The static field initializers in the most derived type run.
3. Base type static field initializers run. Static field initializers starting with the direct
base through each base type to System.Object.
4. Any static constructor runs. Any static constructors, from the ultimate base class of
Object.Object through each base class through the type run. The order of static
constructor execution isn't specified. However, all static constructors in the
hierarchy run before any instances are created.
) Important
There is one important exception to the rule that a static constructor runs before
any instance is created. If a static field initializer creates an instance of the type, that
initializer runs (including any call to an instance constructor) before the static
constructor runs. This is most common in the singleton pattern as shown in the
following example:
C#
private Singleton()
{
Console.WriteLine("Executes before static constructor.");
}
static Singleton()
{
Console.WriteLine("Executes after instance constructor.");
}
Remarks
Static constructors have the following properties:
7 Note
Though not directly accessible, the presence of an explicit static constructor should
be documented to assist with troubleshooting initialization exceptions.
Usage
A typical use of static constructors is when the class is using a log file and the
constructor is used to write entries to this file.
Static constructors are also useful when creating wrapper classes for unmanaged
code, when the constructor can call the LoadLibrary method.
Static constructors are also a convenient place to enforce run-time checks on the
type parameter that can't be checked at compile time via type-parameter
constraints.
Example
In this example, class Bus has a static constructor. When the first instance of Bus is
created ( bus1 ), the static constructor is invoked to initialize the class. The sample output
verifies that the static constructor runs only one time, even though two instances of Bus
are created, and that it runs before the instance constructor runs.
C#
// Instance constructor.
public Bus(int routeNum)
{
RouteNumber = routeNum;
Console.WriteLine("Bus #{0} is created.", RouteNumber);
}
// Instance method.
public void Drive()
{
TimeSpan elapsedTime = DateTime.Now - globalStartTime;
class TestBus
{
static void Main()
{
// The creation of this instance activates the static constructor.
Bus bus1 = new Bus(71);
// Create a second bus.
Bus bus2 = new Bus(72);
C# language specification
For more information, see the Static constructors section of the C# language
specification.
See also
The C# type system
Constructors
Static Classes and Static Class Members
Finalizers
Constructor Design Guidelines
Security Warning - CA2121: Static constructors should be private
Module initializers
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
be found on GitHub, where you Select a link to provide feedback:
can also create and review
issues and pull requests. For Open a documentation issue
more information, see our
contributor guide. Provide product feedback
How to write a copy constructor (C#
Programming Guide)
Article • 03/12/2024
C# records provide a copy constructor for objects, but for classes you have to write one
yourself.
) Important
Writing copy constructors that work for all derived types in a class hierarchy can be
difficult. If your class isn't sealed , you should strongly consider creating a hierarchy
of record class types to use the compiler-synthesized copy constructor.
Example
In the following example, the Person class defines a copy constructor that takes, as its
argument, an instance of Person . The values of the properties of the argument are
assigned to the properties of the new instance of Person . The code contains an
alternative copy constructor that sends the Name and Age properties of the instance that
you want to copy to the instance constructor of the class. The Person class is sealed , so
no derived types can be declared that could introduce errors by copying only the base
class.
C#
// Instance constructor.
public Person(string name, int age)
{
Name = name;
Age = age;
}
class TestPerson
{
static void Main()
{
// Create a Person object by using the instance constructor.
Person person1 = new Person("George", 40);
// Show details to verify that the name and age fields are distinct.
Console.WriteLine(person1.Details());
Console.WriteLine(person2.Details());
See also
ICloneable
Records
The C# type system
Constructors
Finalizers
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Finalizers (C# Programming Guide)
Article • 03/14/2023
Finalizers (historically referred to as destructors) are used to perform any necessary final
clean-up when a class instance is being collected by the garbage collector. In most
cases, you can avoid writing a finalizer by using the
System.Runtime.InteropServices.SafeHandle or derived classes to wrap any unmanaged
handle.
Remarks
Finalizers cannot be defined in structs. They are only used with classes.
A class can only have one finalizer.
Finalizers cannot be inherited or overloaded.
Finalizers cannot be called. They are invoked automatically.
A finalizer does not take modifiers or have parameters.
For example, the following is a declaration of a finalizer for the Car class.
C#
class Car
{
~Car() // finalizer
{
// cleanup statements...
}
}
C#
C#
This design means that the Finalize method is called recursively for all instances in the
inheritance chain, from the most-derived to the least-derived.
7 Note
Empty finalizers should not be used. When a class contains a finalizer, an entry is
created in the Finalize queue. This queue is processed by the garbage collector.
When the GC processes the queue, it calls each finalizer. Unnecessary finalizers,
including empty finalizers, finalizers that only call the base class finalizer, or
finalizers that only call conditionally emitted methods, cause a needless loss of
performance.
The programmer has no control over when the finalizer is called; the garbage collector
decides when to call it. The garbage collector checks for objects that are no longer
being used by the application. If it considers an object eligible for finalization, it calls the
finalizer (if any) and reclaims the memory used to store the object. It's possible to force
garbage collection by calling Collect, but most of the time, this call should be avoided
because it may create performance issues.
7 Note
If you need to perform cleanup reliably when an application exits, register a handler for
the System.AppDomain.ProcessExit event. That handler would ensure
IDisposable.Dispose() (or, IAsyncDisposable.DisposeAsync()) has been called for all
objects that require cleanup before application exit. Because you can't call Finalize
directly, and you can't guarantee the garbage collector calls all finalizers before exit, you
must use Dispose or DisposeAsync to ensure resources are freed.
For more information about cleaning up resources, see the following articles:
Example
The following example creates three classes that make a chain of inheritance. The class
First is the base class, Second is derived from First , and Third is derived from
Second . All three have finalizers. In Main , an instance of the most-derived class is
created. The output from this code depends on which implementation of .NET the
application targets:
.NET Framework: The output shows that the finalizers for the three classes are
called automatically when the application terminates, in order from the most-
derived to the least-derived.
.NET 5 (including .NET Core) or a later version: There's no output, because this
implementation of .NET doesn't call finalizers when the application terminates.
C#
class First
{
~First()
{
System.Diagnostics.Trace.WriteLine("First's finalizer is called.");
}
}
/*
Test with code like the following:
Third t = new Third();
t = null;
See also
IDisposable
Constructors
Garbage Collection
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Object and Collection Initializers (C#
Programming Guide)
Article • 05/21/2024
Object initializers
Object initializers let you assign values to any accessible fields or properties of an object
at creation time without having to invoke a constructor followed by lines of assignment
statements. The object initializer syntax enables you to specify arguments for a
constructor or omit the arguments (and parentheses syntax). The following example
shows how to use an object initializer with a named type, Cat and how to invoke the
parameterless constructor. Note the use of autoimplemented properties in the Cat
class. For more information, see Auto-Implemented Properties.
C#
public Cat()
{
}
C#
The object initializers syntax allows you to create an instance, and after that it assigns
the newly created object, with its assigned properties, to the variable in the assignment.
Object initializers can set indexers, in addition to assigning fields and properties.
Consider this basic Matrix class:
C#
You could initialize the identity matrix with the following code:
C#
[1, 0] = 0.0,
[1, 1] = 1.0,
[1, 2] = 0.0,
[2, 0] = 0.0,
[2, 1] = 0.0,
[2, 2] = 1.0,
};
Any accessible indexer that contains an accessible setter can be used as one of the
expressions in an object initializer, regardless of the number or types of arguments. The
index arguments form the left side of the assignment, and the value is the right side of
the expression. For example, the following initializers are all valid if IndexersExample has
the appropriate indexers:
C#
For the preceding code to compile, the IndexersExample type must have the following
members:
C#
C#
Anonymous types enable the select clause in a LINQ query expression to transform
objects of the original sequence into objects whose value and shape can differ from the
original. You might want to store only a part of the information from each object in a
sequence. In the following example, assume that a product object ( p ) contains many
fields and methods, and that you're only interested in creating a sequence of objects
that contain the product name and the unit price.
C#
var productInfos =
from p in products
select new { p.ProductName, p.UnitPrice };
When this query is executed, the productInfos variable contains a sequence of objects
that can be accessed in a foreach statement as shown in this example:
C#
foreach(var p in productInfos){...}
Each object in the new anonymous type has two public properties that receive the same
names as the properties or fields in the original object. You can also rename a field when
you're creating an anonymous type; the following example renames the UnitPrice field
to Price .
C#
C#
// Compiler error:
// Error CS9035 Required member 'Pet.Age' must be set in the object
initializer or attribute constructor.
// var pet = new Pet();
It's a typical practice to guarantee that your object is properly initialized, especially when
you have multiple fields or properties to manage and don't want to include them all in
the constructor.
C#
// Compiler error:
// Error CS8852 Init - only property or indexer 'Person.LastName' can only
be assigned in an object initializer,
// or on 'this' or 'base' in an instance constructor or an
'init' accessor.
// pet.LastName = "Kowalski";
Required init-only properties support immutable structures while allowing natural syntax
for users of the type.
C#
public EmbeddedClassTypeA()
{
Console.WriteLine($"Entering EmbeddedClassTypeA constructor.
Values are: {this}");
I = 3;
B = true;
S = "abc";
ClassB = new() { BB = true, BI = 43 };
Console.WriteLine($"Exiting EmbeddedClassTypeA constructor.
Values are: {this})");
}
}
public EmbeddedClassTypeB()
{
Console.WriteLine($"Entering EmbeddedClassTypeB constructor.
Values are: {this}");
BI = 23;
BB = false;
BS = "BBBabc";
Console.WriteLine($"Exiting EmbeddedClassTypeB constructor.
Values are: {this})");
}
}
// Output:
//Entering EmbeddedClassTypeA constructor Values are: 0|False||||
//Entering EmbeddedClassTypeB constructor Values are: 0|False|
//Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
//Exiting EmbeddedClassTypeA constructor Values are:
3|True|abc|||43|True|BBBabc)
//After initializing EmbeddedClassTypeA:
103|False|abc|||100003|True|BBBabc
//Entering EmbeddedClassTypeA constructor Values are: 0|False||||
//Entering EmbeddedClassTypeB constructor Values are: 0|False|
//Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
//Exiting EmbeddedClassTypeA constructor Values are:
3|True|abc|||43|True|BBBabc)
//Entering EmbeddedClassTypeB constructor Values are: 0|False|
//Exiting EmbeddedClassTypeB constructor Values are: 23|False|BBBabc)
//After initializing EmbeddedClassTypeA a2:
103|False|abc|||100003|False|BBBabc
}
The following example shows how, for ClassB, the initialization process involves
updating specific values while retaining others from the original instance. The Initializer
reuses current instance: ClassB's values are: 100003 (new value we assign here), true
(kept from EmbeddedClassTypeA's initialization), BBBabc (unchanged default from
EmbeddedClassTypeB).
Collection initializers
Collection initializers let you specify one or more element initializers when you initialize
a collection type that implements IEnumerable and has Add with the appropriate
signature as an instance method or an extension method. The element initializers can be
a value, an expression, or an object initializer. By using a collection initializer, you don't
have to specify multiple calls; the compiler adds the calls automatically.
C#
The following collection initializer uses object initializers to initialize objects of the Cat
class defined in a previous example. The individual object initializers are enclosed in
braces and separated by commas.
C#
You can specify null as an element in a collection initializer if the collection's Add
method allows it.
C#
You can specify indexed elements if the collection supports read / write indexing.
C#
The preceding sample generates code that calls the Item[TKey] to set the values. You
could also initialize dictionaries and other associative containers using the following
syntax. Notice that instead of indexer syntax, with parentheses and an assignment, it
uses an object with multiple values:
C#
This initializer example calls Add(TKey, TValue) to add the three items into the dictionary.
These two different ways to initialize associative collections have slightly different
behavior because of the method calls the compiler generates. Both variants work with
the Dictionary class. Other types might only support one or the other based on their
public API.
Object Initializers with collection read-only
property initialization
Some classes might have collection properties where the property is read-only, like the
Cats property of CatOwner in the following case:
C#
You can't use collection initializer syntax discussed so far since the property can't be
assigned a new list:
C#
However, new entries can be added to Cats nonetheless using the initialization syntax
by omitting the list creation ( new List<Cat> ), as shown next:
C#
The set of entries to be added appear surrounded by braces. The preceding code is
identical to writing:
C#
Examples
The following example combines the concepts of object and collection initializers.
C#
public Cat() { }
// Display results.
System.Console.WriteLine(cat.Name);
foreach (Cat c in cats)
{
System.Console.WriteLine(c.Name);
}
The following example shows an object that implements IEnumerable and contains an
Add method with multiple parameters. It uses a collection initializer with multiple
elements per item in the list that correspond to the signature of the Add method.
C#
System.Collections.IEnumerator
System.Collections.IEnumerable.GetEnumerator() =>
internalList.GetEnumerator();
Console.WriteLine("Address Entries:");
/*
* Prints:
Address Entries:
John Doe
123 Street
Topeka, KS 00000
Jane Smith
456 Street
Topeka, KS 00000
*/
}
Add methods can use the params keyword to take a variable number of arguments, as
shown in the following example. This example also demonstrates the custom
implementation of an indexer to initialize a collection using indexes. Beginning with C#
13, the params parameter isn't restricted to an array. It can be a collection type or
interface.
C#
/*
* Prints:
See also
Use object initializers (style rule IDE0017)
Use collection initializers (style rule IDE0028)
LINQ in C#
Anonymous Types
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
How to initialize objects by using an
object initializer (C# Programming
Guide)
Article • 05/14/2024
You can use object initializers to initialize type objects in a declarative manner without
explicitly invoking a constructor for the type.
The following examples show how to use object initializers with named objects. The
compiler processes object initializers by first accessing the parameterless instance
constructor and then processing the member initializations. Therefore, if the
parameterless constructor is declared as private in the class, object initializers that
require public access will fail.
You must use an object initializer if you're defining an anonymous type. For more
information, see How to return subsets of element properties in a query.
Example
The following example shows how to initialize a new StudentName type by using object
initializers. This example sets properties in the StudentName type:
C#
Console.WriteLine(student1.ToString());
Console.WriteLine(student2.ToString());
Console.WriteLine(student3.ToString());
Console.WriteLine(student4.ToString());
}
// Output:
// Craig 0
// Craig 0
// 183
// Craig 116
// Properties.
public string? FirstName { get; set; }
public string? LastName { get; set; }
public int ID { get; set; }
Object initializers can be used to set indexers in an object. The following example
defines a BaseballTeam class that uses an indexer to get and set players at different
positions. The initializer can assign players, based on the abbreviation for the position,
or the number used for each position baseball scorecards:
C#
Console.WriteLine(team["2B"]);
}
}
The next example shows the order of execution of constructor and member
initializations using constructor with and without parameter:
C#
set
{
Console.WriteLine("Hello from setter of Dog's required
property 'Name'");
name = value;
}
}
}
public Person()
{
Console.WriteLine("Hello from Person's parameterless
constructor");
}
init
{
Console.WriteLine("Hello from setter of Person's init
property 'LastName'");
lastName = value;
}
}
set
{
Console.WriteLine("Hello from setter of Person's property
'City'");
city = value;
}
}
}
// Output:
// Hello from Person's parameterless constructor
// Hello from setter of Person's required property 'FirstName'
// Hello from setter of Person's init property 'LastName'
// Hello from setter of Person's property 'City'
// Hello from Dog's non-parameterless constructor
// Hello from setter of Dog's required property 'Name'
}
See also
Object and Collection Initializers
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to initialize a dictionary with a
collection initializer (C# Programming
Guide)
Article • 03/15/2024
7 Note
The major difference between these two ways of initializing the collection is that in
case of having duplicated keys, for example:
C#
Add method will throw ArgumentException: 'An item with the same key has
already been added. Key: 111' , while the second part of example, the public read /
write indexer method, will quietly overwrite the already existing entry with the same
key.
Example
In the following code example, a Dictionary<TKey,TValue> is initialized with instances of
type StudentName . The first initialization uses the Add method with two arguments. The
compiler generates a call to Add for each of the pairs of int keys and StudentName
values. The second uses a public read / write indexer method of the Dictionary class:
C#
public class HowToDictionaryInitializer
{
class StudentName
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
public int ID { get; set; }
}
Note the two pairs of braces in each element of the collection in the first declaration.
The innermost braces enclose the object initializer for the StudentName , and the
outermost braces enclose the initializer for the key/value pair to be added to the
students Dictionary<TKey,TValue>. Finally, the whole collection initializer for the
dictionary is enclosed in braces. In the second initialization, the left side of the
assignment is the key and the right side is the value, using an object initializer for
StudentName .
See also
Object and Collection Initializers
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Nested Types (C# Programming Guide)
Article • 03/12/2024
A type defined within a class, struct, or interface is called a nested type. For example
C#
Regardless of whether the outer type is a class, interface, or struct, nested types default
to private; they are accessible only from their containing type. In the previous example,
the Nested class is inaccessible to external types.
You can also specify an access modifier to define the accessibility of a nested type, as
follows:
Also be aware that making a nested type externally visible violates the code quality
rule CA1034 "Nested types should not be visible".
C#
C#
public Nested()
{
}
public Nested(Container parent)
{
this.parent = parent;
}
}
}
A nested type has access to all of the members that are accessible to its containing type.
It can access private and protected members of the containing type, including any
inherited protected members.
In the previous declaration, the full name of class Nested is Container.Nested . This is the
name used to create a new instance of the nested class, as follows:
C#
See also
The C# type system
Access Modifiers
Constructors
CA1034 rule
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
be found on GitHub, where you Select a link to provide feedback:
can also create and review
issues and pull requests. For Open a documentation issue
more information, see our
contributor guide. Provide product feedback
Partial Classes and Methods (C#
Programming Guide)
Article • 03/14/2024
It's possible to split the definition of a class, a struct, an interface, or a method over two
or more source files. Each source file contains a section of the type or method definition,
and all parts are combined when the application is compiled.
Partial Classes
There are several situations when splitting a class definition is desirable:
To split a class definition, use the partial keyword modifier, as shown here:
C#
The partial keyword indicates that other parts of the class, struct, or interface can be
defined in the namespace. All the parts must use the partial keyword. All the parts
must be available at compile time to form the final type. All the parts must have the
same accessibility, such as public , private , and so on.
If any part is declared abstract, then the whole type is considered abstract. If any part is
declared sealed, then the whole type is considered sealed. If any part declares a base
type, then the whole type inherits that class.
All the parts that specify a base class must agree, but parts that omit a base class still
inherit the base type. Parts can specify different base interfaces, and the final type
implements all the interfaces listed by all the partial declarations. Any class, struct, or
interface members declared in a partial definition are available to all the other parts. The
final type is the combination of all the parts at compile time.
7 Note
The following example shows that nested types can be partial, even if the type they're
nested within isn't partial itself.
C#
class Container
{
partial class Nested
{
void Test() { }
}
At compile time, attributes of partial-type definitions are merged. For example, consider
the following declarations:
C#
[SerializableAttribute]
partial class Moon { }
[ObsoleteAttribute]
partial class Moon { }
They're equivalent to the following declarations:
C#
[SerializableAttribute]
[ObsoleteAttribute]
class Moon { }
XML comments
interfaces
generic-type parameter attributes
class attributes
members
C#
C#
Restrictions
There are several rules to follow when you're working with partial class definitions:
All partial-type definitions meant to be parts of the same type must be modified
with partial . For example, the following class declarations generate an error:
C#
The partial modifier can only appear immediately before the keyword class ,
struct , or interface .
Nested partial types are allowed in partial-type definitions as illustrated in the
following example:
C#
All partial-type definitions meant to be parts of the same type must be defined in
the same assembly and the same module (.exe or .dll file). Partial definitions can't
span multiple modules.
The class name and generic-type parameters must match on all partial-type
definitions. Generic types can be partial. Each partial declaration must use the
same parameter names in the same order.
The following keywords on a partial-type definition are optional, but if present on
one partial-type definition, can't conflict with the keywords specified on another
partial definition for the same type:
public
private
protected
internal
abstract
sealed
base class
new modifier (nested parts)
generic constraints
Examples
In the following example, the fields and the constructor of the class, Coords , are
declared in one partial class definition, and the member, PrintCoords , is declared in
another partial class definition.
C#
public partial class Coords
{
private int x;
private int y;
class TestCoords
{
static void Main()
{
Coords myCoords = new Coords(10, 15);
myCoords.PrintCoords();
The following example shows that you can also develop partial structs and interfaces.
C#
partial struct S1
{
void Struct_Test() { }
}
partial struct S1
{
void Struct_Test2() { }
}
Partial Methods
A partial class or struct can contain a partial method. One part of the class contains the
signature of the method. An implementation can be defined in the same part or another
part.
An implementation isn't required for a partial method when the signature obeys the
following rules:
The declaration doesn't include any access modifiers. The method has private
access by default.
The return type is void.
None of the parameters have the out modifier.
The method declaration can't include any of the following modifiers:
virtual
override
sealed
new
extern
The method and all calls to the method are removed at compile time when there's no
implementation.
Any method that doesn't conform to all those restrictions (for example, public virtual
partial void method), must provide an implementation. That implementation might be
Partial methods enable the implementer of one part of a class to declare a method. The
implementer of another part of the class can define that method. There are two
scenarios where this separation is useful: templates that generate boilerplate code, and
source generators.
Template code: The template reserves a method name and signature so that
generated code can call the method. These methods follow the restrictions that
enable a developer to decide whether to implement the method. If the method
isn't implemented, then the compiler removes the method signature and all calls
to the method. The calls to the method, including any results that would occur
from evaluation of arguments in the calls, have no effect at run time. Therefore,
any code in the partial class can freely use a partial method, even if the
implementation isn't supplied. No compile-time or run-time errors result if the
method is called but not implemented.
Source generators: Source generators provide an implementation for methods.
The human developer can add the method declaration (often with attributes read
by the source generator). The developer can write code that calls these methods.
The source generator runs during compilation and provides the implementation. In
this scenario, the restrictions for partial methods that might not be implemented
often aren't followed.
C#
// Definition in file1.cs
partial void OnNameChanged();
// Implementation in file2.cs
partial void OnNameChanged()
{
// method body
}
Partial method declarations must begin with the contextual keyword partial.
Partial method signatures in both parts of the partial type must match.
Partial methods can have static and unsafe modifiers.
Partial methods can be generic. Constraints must be the same on the defining and
implementing method declaration. Parameter and type parameter names don't
have to be the same in the implementing declaration as in the defining one.
You can make a delegate to a partial method defined and implemented, but not to
a partial method that doesn't have an implementation.
C# Language Specification
For more information, see Partial types and Partial methods in the C# Language
Specification. The language specification is the definitive source for C# syntax and
usage. The additional features for partial methods are defined in the feature
specification.
See also
Classes
Structure types
Interfaces
partial (Type)
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to return subsets of element
properties in a query (C# Programming
Guide)
Article • 03/12/2024
Use an anonymous type in a query expression when both of these conditions apply:
You want to return only some of the properties of each source element.
You do not have to store the query results outside the scope of the method in
which the query is executed.
If you only want to return one property or field from each source element, then you can
just use the dot operator in the select clause. For example, to return only the ID of
each student , write the select clause as follows:
C#
select student.ID;
Example
The following example shows how to use an anonymous type to return only a subset of
the properties of each source element that matches the specified condition.
C#
Note that the anonymous type uses the source element's names for its properties if no
names are specified. To give new names to the properties in the anonymous type, write
the select statement as follows:
C#
If you try this in the previous example, then the Console.WriteLine statement must also
change:
C#
See also
Anonymous Types
LINQ in C#
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Explicit Interface Implementation (C#
Programming Guide)
Article • 09/29/2022
If a class implements two interfaces that contain a member with the same signature,
then implementing that member on the class will cause both interfaces to use that
member as their implementation. In the following example, all the calls to Paint invoke
the same method. This first sample defines the types:
C#
C#
// Output:
// Paint method in SampleClass
// Paint method in SampleClass
// Paint method in SampleClass
But you might not want the same implementation to be called for both interfaces. To
call a different implementation depending on which interface is in use, you can
implement an interface member explicitly. An explicit interface implementation is a class
member that is only called through the specified interface. Name the class member by
prefixing it with the name of the interface and a period. For example:
C#
The class member IControl.Paint is only available through the IControl interface, and
ISurface.Paint is only available through ISurface . Both method implementations are
separate, and neither are available directly on the class. For example:
C#
// Output:
// IControl.Paint
// ISurface.Paint
Explicit implementation is also used to resolve cases where two interfaces each declare
different members of the same name such as a property and a method. To implement
both interfaces, a class has to use explicit implementation either for the property P , or
the method P , or both, to avoid a compiler error. For example:
C#
interface ILeft
{
int P { get;}
}
interface IRight
{
int P();
}
C#
C#
See also
Object oriented programming
Interfaces
Inheritance
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to explicitly implement interface
members (C# Programming Guide)
Article • 03/12/2024
This example declares an interface, IDimensions , and a class, Box , which explicitly
implements the interface members GetLength and GetWidth . The members are accessed
through the interface instance dimensions .
Example
C#
interface IDimensions
{
float GetLength();
float GetWidth();
}
Robust Programming
Notice that the following lines, in the Main method, are commented out because
they would produce compilation errors. An interface member that is explicitly
implemented cannot be accessed from a class instance:
C#
Notice also that the following lines, in the Main method, successfully print out the
dimensions of the box because the methods are being called from an instance of
the interface:
C#
See also
Object oriented programming
Interfaces
How to explicitly implement members of two interfaces
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to explicitly implement members
of two interfaces (C# Programming
Guide)
Article • 03/12/2024
Example
C#
Robust Programming
If you want to make the default measurements in English units, implement the methods
Length and Width normally, and explicitly implement the Length and Width methods
from the IMetricDimensions interface:
C#
// Normal implementation:
public float Length() => lengthInches;
public float Width() => widthInches;
// Explicit implementation:
float IMetricDimensions.Length() => lengthInches * 2.54f;
float IMetricDimensions.Width() => widthInches * 2.54f;
In this case, you can access the English units from the class instance and access the
metric units from the interface instance:
C#
See also
Object oriented programming
Interfaces
How to explicitly implement interface members
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Delegates (C# Programming Guide)
Article • 09/29/2022
Delegates are used to pass methods as arguments to other methods. Event handlers are
nothing more than methods that are invoked through delegates. You create a custom
method, and a class such as a windows control can call your method when a certain
event occurs. The following example shows a delegate declaration:
C#
Any method from any accessible class or struct that matches the delegate type can be
assigned to the delegate. The method can be either static or an instance method. This
flexibility means you can programmatically change method calls, or plug new code into
existing classes.
7 Note
In the context of method overloading, the signature of a method does not include
the return value. But in the context of delegates, the signature does include the
return value. In other words, a method must have the same return type as the
delegate.
This ability to refer to a method as a parameter makes delegates ideal for defining
callback methods. You can write a method that compares two objects in your
application. That method can be used in a delegate for a sort algorithm. Because the
comparison code is separate from the library, the sort method can be more general.
Function pointers support similar scenarios, where you need more control over the
calling convention. The code associated with a delegate is invoked using a virtual
method added to a delegate type. Using function pointers, you can specify different
conventions.
Delegates Overview
Delegates have the following properties:
Delegates are similar to C++ function pointers, but delegates are fully object-
oriented, and unlike C++ pointers to member functions, delegates encapsulate
both an object instance and a method.
Delegates allow methods to be passed as parameters.
Delegates can be used to define callback methods.
Delegates can be chained together; for example, multiple methods can be called
on a single event.
Methods don't have to match the delegate type exactly. For more information, see
Using Variance in Delegates.
Lambda expressions are a more concise way of writing inline code blocks. Lambda
expressions (in certain contexts) are compiled to delegate types. For more
information about lambda expressions, see Lambda expressions.
In This Section
Using Delegates
When to Use Delegates Instead of Interfaces (C# Programming Guide)
Delegates with Named vs. Anonymous Methods
Using Variance in Delegates
How to combine delegates (Multicast Delegates)
How to declare, instantiate, and use a delegate
C# Language Specification
For more information, see Delegates in the C# Language Specification. The language
specification is the definitive source for C# syntax and usage.
See also
Delegate
Events
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review Open a documentation issue
issues and pull requests. For
more information, see our Provide product feedback
contributor guide.
Using Delegates (C# Programming
Guide)
Article • 07/31/2023
C#
A delegate object is normally constructed by providing the name of the method the
delegate will wrap, or with a lambda expression. Once a delegate is instantiated in this
manner it can be invoked. Invoking a delegate calls the method attached to the
delegate instance. The parameters passed to the delegate by the caller are passed to the
method, and the return value, if any, from the method is returned to the caller by the
delegate. For example:
C#
C#
Delegate types are derived from the Delegate class in .NET. Delegate types are sealed—
they cannot be derived from— and it is not possible to derive custom classes from
Delegate. Because the instantiated delegate is an object, it can be passed as an
argument, or assigned to a property. This allows a method to accept a delegate as a
parameter, and call the delegate at some later time. This is known as an asynchronous
callback, and is a common method of notifying a caller when a long process has
completed. When a delegate is used in this fashion, the code using the delegate does
not need any knowledge of the implementation of the method being used. The
functionality is similar to the encapsulation interfaces provide.
Another common use of callbacks is defining a custom comparison method and passing
that delegate to a sort method. It allows the caller's code to become part of the sort
algorithm. The following example method uses the Del type as a parameter:
C#
You can then pass the delegate created above to that method:
C#
MethodWithCallback(1, 2, handler);
Console
Using the delegate as an abstraction, MethodWithCallback does not need to call the
console directly—it does not have to be designed with a console in mind. What
MethodWithCallback does is simply prepare a string and pass the string to another
method. This is especially powerful since a delegated method can use any number of
parameters.
C#
public class MethodClass
{
public void Method1(string message) { }
public void Method2(string message) { }
}
Along with the static DelegateMethod shown previously, we now have three methods
that can be wrapped by a Del instance.
A delegate can call more than one method when invoked. This is referred to as
multicasting. To add an extra method to the delegate's list of methods—the invocation
list—simply requires adding two delegates using the addition or addition assignment
operators ('+' or '+='). For example:
C#
At this point allMethodsDelegate contains three methods in its invocation list— Method1 ,
Method2 , and DelegateMethod . The original three delegates, d1 , d2 , and d3 , remain
unchanged. When allMethodsDelegate is invoked, all three methods are called in order.
If the delegate uses reference parameters, the reference is passed sequentially to each
of the three methods in turn, and any changes by one method are visible to the next
method. When any of the methods throws an exception that is not caught within the
method, that exception is passed to the caller of the delegate and no subsequent
methods in the invocation list are called. If the delegate has a return value and/or out
parameters, it returns the return value and parameters of the last method invoked. To
remove a method from the invocation list, use the subtraction or subtraction
assignment operators ( - or -= ). For example:
C#
//remove Method1
allMethodsDelegate -= d1;
C#
Delegates with more than one method in their invocation list derive from
MulticastDelegate, which is a subclass of System.Delegate . The above code works in
either case because both classes support GetInvocationList .
Multicast delegates are used extensively in event handling. Event source objects send
event notifications to recipient objects that have registered to receive that event. To
register for an event, the recipient creates a method designed to handle the event, then
creates a delegate for that method and passes the delegate to the event source. The
source calls the delegate when the event occurs. The delegate then calls the event
handling method on the recipient, delivering the event data. The delegate type for a
given event is defined by the event source. For more, see Events.
C#
See also
Delegates
Using Variance in Delegates
Variance in Delegates
Using Variance for Func and Action Generic Delegates
Events
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Delegates with Named vs. Anonymous
Methods (C# Programming Guide)
Article • 03/12/2024
A delegate can be associated with a named method. When you instantiate a delegate by
using a named method, the method is passed as a parameter, for example:
C#
// Declare a delegate.
delegate void WorkCallback(int x);
This is called using a named method. Delegates constructed with a named method can
encapsulate either a static method or an instance method. Named methods are the only
way to instantiate a delegate in earlier versions of C#. However, in a situation where
creating a new method is unwanted overhead, C# enables you to instantiate a delegate
and immediately specify a code block that the delegate will process when it is called.
The block can contain either a lambda expression or an anonymous method.
The method that you pass as a delegate parameter must have the same signature as the
delegate declaration. A delegate instance may encapsulate either static or instance
method.
7 Note
Although the delegate can use an out parameter, we do not recommend its use
with multicast event delegates because you cannot know which delegate will be
called.
Beginning with C# 10, method groups with a single overload have a natural type. This
means the compiler can infer the return type and parameter types for the delegate type:
C#
Examples
The following is a simple example of declaring and using a delegate. Notice that both
the delegate, Del , and the associated method, MultiplyNumbers , have the same
signature
C#
// Declare a delegate
delegate void MultiplyCallback(int i, double j);
class MathClass
{
static void Main()
{
MathClass m = new MathClass();
In the following example, one delegate is mapped to both static and instance methods
and returns specific information from each.
C#
// Declare a delegate
delegate void Callback();
class SampleClass
{
public void InstanceMethod()
{
Console.WriteLine("A message from the instance method.");
}
class TestSampleClass
{
static void Main()
{
var sc = new SampleClass();
See also
Delegates
How to combine delegates (Multicast Delegates)
Events
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review Open a documentation issue
issues and pull requests. For
more information, see our Provide product feedback
contributor guide.
How to combine delegates (Multicast
Delegates) (C# Programming Guide)
Article • 03/12/2024
Example
C#
using System;
// Define a custom delegate that has a string parameter and returns void.
delegate void CustomCallback(string s);
class TestClass
{
// Define two methods that have the same signature as CustomCallback.
static void Hello(string s)
{
Console.WriteLine($" Hello, {s}!");
}
See also
MulticastDelegate
Events
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
How to declare, instantiate, and use a
Delegate (C# Programming Guide)
Article • 09/29/2022
C#
// Declare a delegate.
delegate void NotifyCallback(string str);
C#
C#
C#
C#
// Instantiate NotifyCallback by using a lambda expression.
NotifyCallback del4 = name => { Console.WriteLine($"Notification received
for: {name}"); };
The following example illustrates declaring, instantiating, and using a delegate. The
BookDB class encapsulates a bookstore database that maintains a database of books. It
The use of delegates promotes good separation of functionality between the bookstore
database and the client code. The client code has no knowledge of how the books are
stored or how the bookstore code finds paperback books. The bookstore code has no
knowledge of what processing is performed on the paperback books after it finds them.
Example
C#
C#
Each delegate type describes the number and types of the arguments, and the
type of the return value of methods that it can encapsulate. Whenever a new set of
argument types or return value type is needed, a new delegate type must be
declared.
Instantiating a delegate.
After a delegate type has been declared, a delegate object must be created and
associated with a particular method. In the previous example, you do this by
passing the PrintTitle method to the ProcessPaperbackBooks method as in the
following example:
C#
bookDB.ProcessPaperbackBooks(PrintTitle);
This creates a new delegate object associated with the static method
Test.PrintTitle . Similarly, the non-static method AddBookToTotal on the object
C#
bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);
Calling a delegate.
After a delegate object is created, the delegate object is typically passed to other
code that will call the delegate. A delegate object is called by using the name of
the delegate object, followed by the parenthesized arguments to be passed to the
delegate. Following is an example of a delegate call:
C#
processBook(b);
See also
Events
Delegates
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Strings and string literals
Article • 07/31/2024
A string is an object of type String whose value is text. Internally, the text is stored as a
sequential read-only collection of Char objects. There's no null-terminating character at
the end of a C# string; therefore a C# string can contain any number of embedded null
characters ('\0'). The Length property of a string represents the number of Char objects
it contains, not the number of Unicode characters. To access the individual Unicode
code points in a string, use the StringInfo object.
C#
// Initialize to null.
string? message2 = null;
You don't use the new operator to create a string object except when initializing the
string with an array of chars.
Initialize a string with the Empty constant value to create a new String object whose
string is of zero length. The string literal representation of a zero-length string is "" . By
initializing strings with the Empty value instead of null, you can reduce the chances of a
NullReferenceException occurring. Use the static IsNullOrEmpty(String) method to verify
the value of a string before you try to access it.
Immutability of strings
String objects are immutable: they can't be changed after they're created. All of the
String methods and C# operators that appear to modify a string actually return the
results in a new string object. In the following example, when the contents of s1 and s2
are concatenated to form a single string, the two original strings are unmodified. The +=
operator creates a new string that contains the combined contents. That new object is
assigned to the variable s1 , and the original object that was assigned to s1 is released
for garbage collection because no other variable holds a reference to it.
C#
System.Console.WriteLine(s1);
// Output: A string is more than the sum of its chars.
Because a string "modification" is actually a new string creation, you must use caution
when you create references to strings. If you create a reference to a string, and then
"modify" the original string, the reference continues to point to the original object
instead of the new object that was created when the string was modified. The following
code illustrates this behavior:
C#
System.Console.WriteLine(str2);
//Output: Hello
For more information about how to create new strings that are based on modifications
such as search and replace operations on the original string, see How to modify string
contents.
C#
C#
Starts and ends with a sequence of at least three double quote characters ( """ ).
You can use more than three consecutive characters to start and end the sequence
to support string literals that contain three (or more) repeated quote characters.
Single line raw string literals require the opening and closing quote characters on
the same line.
Multi-line raw string literals require both opening and closing quote characters on
their own line.
In multi-line raw string literals, any whitespace to the left of the closing quotes is
removed from all lines of the raw string literal.
In multi-line raw string literals, whitespace following the opening quote on the
same line is ignored.
In multi-line raw string literals, whitespace only lines following the opening quote
are included in the string literal.
C#
The following examples demonstrate the compiler errors reported based on these rules:
C#
// CS8999: Line does not start with the same whitespace as the closing line
// of the raw string literal
var noOutdenting = """
A line of text.
Trying to outdent the second line.
""";
The first two examples are invalid because multiline raw string literals require the
opening and closing quote sequence on its own line. The third example is invalid
because the text is outdented from the closing quote sequence.
You should consider raw string literals when you're generating text that includes
characters that require escape sequences when using quoted string literals or verbatim
string literals. Raw string literals are easier for you and others to read because it more
closely resembles the output text. For example, consider the following code that
includes a string of formatted JSON:
C#
ノ Expand table
\\ Backslash 0x005C
\0 Null 0x0000
\a Alert 0x0007
\b Backspace 0x0008
\e Escape 0x001B
2 Warning
When using the \x escape sequence and specifying less than 4 hex digits, if the
characters that immediately follow the escape sequence are valid hex digits (i.e. 0-
9, A-F, and a-f), they will be interpreted as being part of the escape sequence. For
example, \xA1 produces "¡", which is code point U+00A1. However, if the next
character is "A" or "a", then the escape sequence will instead be interpreted as
being \xA1A and produce "ਚ", which is code point U+0A1A. In such cases,
specifying all 4 hex digits (for example, \x00A1 ) prevents any possible
misinterpretation.
7 Note
At compile time, verbatim and raw strings are converted to ordinary strings with all
the same escape sequences. Therefore, if you view a verbatim or raw string in the
debugger watch window, you will see the escape characters that were added by the
compiler, not the verbatim or raw version from your source code. For example, the
verbatim string @"C:\files.txt" will appear in the watch window as
"C:\\files.txt" .
Format strings
A format string is a string whose contents are determined dynamically at run time.
Format strings are created by embedding interpolated expressions or placeholders inside
of braces within a string. Everything inside the braces ( {...} ) is resolved to a value and
output as a formatted string at run time. There are two methods to create format
strings: string interpolation and composite formatting.
String interpolation
You declare Interpolated strings with the $ special character. An interpolated string
includes interpolated expressions in braces. If you're new to string interpolation, see the
String interpolation - C# interactive tutorial for a quick overview.
Use string interpolation to improve the readability and maintainability of your code.
String interpolation achieves the same results as the String.Format method, but
improves ease of use and inline clarity.
C#
// Output:
// Jupiter Hammon was an African American poet born in 1711.
// He was first published in 1761 at the age of 50.
// He'd be over 300 years old today.
Beginning with C# 10, you can use string interpolation to initialize a constant string
when all the expressions used for placeholders are also constant strings.
Beginning with C# 11, you can combine raw string literals with string interpolations. You
start and end the format string with three or more successive double quotes. If your
output string should contain the { or } character, you can use extra $ characters to
specify how many { and } characters start and end an interpolation. Any sequence of
fewer { or } characters is included in the output. The following example shows how
you can use that feature to display the distance of a point from the origin, and place the
point inside braces:
C#
int X = 2;
int Y = 3;
Console.WriteLine(pointMessage);
// Output:
// The point {2, 3} is 3.605551275463989 from the origin.
strings.
C#
// Output:
// Jupiter Hammon
// was an African American poet born in 1711.
// He was first published in 1761
// at the age of 50.
Composite formatting
The String.Format utilizes placeholders in braces to create a format string. This example
results in similar output to the string interpolation method used in the preceding
sample.
C#
// Output:
// Phillis Wheatley was an African American poet born in 1753.
// She was first published in 1773 at the age of 20.
// She'd be over 300 years old today.
For more information on formatting .NET types, see Formatting Types in .NET.
Substrings
A substring is any sequence of characters that is contained in a string. Use the Substring
method to create a new string from a part of the original string. You can search for one
or more occurrences of a substring by using the IndexOf method. Use the Replace
method to replace all occurrences of a specified substring with a new string. Like the
Substring method, Replace actually returns a new string and doesn't modify the original
string. For more information, see How to search strings and How to modify string
contents.
C#
System.Console.WriteLine(s3.Replace("C#", "Basic"));
// Output: "Visual Basic Express"
C#
If the String methods don't provide the functionality that you must have to modify
individual characters in a string, you can use a StringBuilder object to modify the
individual chars "in-place," and then create a new string to store the results by using the
StringBuilder methods. In the following example, assume that you must modify the
original string in a particular way and then store the results for future use:
C#
string question = "hOW DOES mICROSOFT wORD DEAL WITH THE cAPS lOCK KEY?";
System.Text.StringBuilder sb = new System.Text.StringBuilder(question);
C#
string s = String.Empty;
By contrast, a null string doesn't refer to an instance of a System.String object and any
attempt to call a method on a null string causes a NullReferenceException. However, you
can use null strings in concatenation and comparison operations with other strings. The
following examples illustrate some cases in which a reference to a null string does and
doesn't cause an exception to be thrown:
C#
// The null character can be displayed and counted, like other chars.
string s1 = "\x0" + "abc";
string s2 = "abc" + "\x0";
// Output of the following line: * abc*
Console.WriteLine("*" + s1 + "*");
// Output of the following line: *abc *
Console.WriteLine("*" + s2 + "*");
// Output of the following line: 4
Console.WriteLine(s2.Length);
C#
In this example, a StringBuilder object is used to create a string from a set of numeric
types:
C#
Related articles
How to modify string contents: Illustrates techniques to transform strings and
modify the contents of strings.
How to compare strings: Shows how to perform ordinal and culture specific
comparisons of strings.
How to concatenate multiple strings: Demonstrates various ways to join multiple
strings into one.
How to parse strings using String.Split: Contains code examples that illustrate how
to use the String.Split method to parse strings.
How to search strings: Explains how to use search for specific text or patterns in
strings.
How to determine whether a string represents a numeric value: Shows how to
safely parse a string to see whether it has a valid numeric value.
String interpolation: Describes the string interpolation feature that provides a
convenient syntax to format strings.
Basic String Operations: Provides links to articles that use System.String and
System.Text.StringBuilder methods to perform basic string operations.
Parsing Strings: Describes how to convert string representations of .NET base types
to instances of the corresponding types.
Parsing Date and Time Strings in .NET: Shows how to convert a string such as
"01/24/2008" to a System.DateTime object.
Comparing Strings: Includes information about how to compare strings and
provides examples in C# and Visual Basic.
Using the StringBuilder Class: Describes how to create and modify dynamic string
objects by using the StringBuilder class.
LINQ and Strings: Provides information about how to perform various string
operations by using LINQ queries.
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to determine whether a string
represents a numeric value (C#
Programming Guide)
Article • 04/16/2022
C#
int i = 0;
string s = "108";
bool result = int.TryParse(s, out i); //i now = 108
If the string contains nonnumeric characters or the numeric value is too large or too
small for the particular type you have specified, TryParse returns false and sets the out
parameter to zero. Otherwise, it returns true and sets the out parameter to the numeric
value of the string.
7 Note
A string may contain only numeric characters and still not be valid for the type
whose TryParse method that you use. For example, "256" is not a valid value for
byte but it is valid for int . "98.6" is not a valid value for int but it is a valid
decimal .
Example
The following examples show how to use TryParse with string representations of long ,
byte , and decimal values.
C#
byte number2 = 0;
numString = "255"; // A value of 256 will return false
canConvert = byte.TryParse(numString, out number2);
if (canConvert == true)
Console.WriteLine("number2 now = {0}", number2);
else
Console.WriteLine("numString is not a valid byte");
decimal number3 = 0;
numString = "27.3"; //"27" is also a valid decimal
canConvert = decimal.TryParse(numString, out number3);
if (canConvert == true)
Console.WriteLine("number3 now = {0}", number3);
else
Console.WriteLine("number3 is not a valid decimal");
Robust Programming
Primitive numeric types also implement the Parse static method, which throws an
exception if the string is not a valid number. TryParse is generally more efficient
because it just returns false if the number is not valid.
.NET Security
Always use the TryParse or Parse methods to validate user input from controls such as
text boxes and combo boxes.
See also
How to convert a byte array to an int
How to convert a string to a number
How to convert between hexadecimal strings and numeric types
Parsing Numeric Strings
Formatting Types
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
be found on GitHub, where you Select a link to provide feedback:
can also create and review
issues and pull requests. For Open a documentation issue
more information, see our
contributor guide. Provide product feedback
Indexers (C# Programming Guide)
Article • 04/12/2023
Indexers allow instances of a class or struct to be indexed just like arrays. The indexed
value can be set or retrieved without explicitly specifying a type or instance member.
Indexers resemble properties except that their accessors take parameters.
The following example defines a generic class with simple get and set accessor methods
to assign and retrieve values. The Program class creates an instance of this class for
storing strings.
C#
using System;
class SampleCollection<T>
{
// Declare an array to store the data elements.
private T[] arr = new T[100];
class Program
{
static void Main()
{
var stringCollection = new SampleCollection<string>();
stringCollection[0] = "Hello, World";
Console.WriteLine(stringCollection[0]);
}
}
// The example displays the following output:
// Hello, World.
7 Note
C#
using System;
class SampleCollection<T>
{
// Declare an array to store the data elements.
private T[] arr = new T[100];
int nextIndex = 0;
class Program
{
static void Main()
{
var stringCollection = new SampleCollection<string>();
stringCollection.Add("Hello, World");
System.Console.WriteLine(stringCollection[0]);
}
}
// The example displays the following output:
// Hello, World.
Note that => introduces the expression body, and that the get keyword is not used.
Both the get and set accessor can be implemented as expression-bodied members. In
this case, both get and set keywords must be used. For example:
C#
using System;
class SampleCollection<T>
{
// Declare an array to store the data elements.
private T[] arr = new T[100];
class Program
{
static void Main()
{
var stringCollection = new SampleCollection<string>();
stringCollection[0] = "Hello, World.";
Console.WriteLine(stringCollection[0]);
}
}
// The example displays the following output:
// Hello, World.
Indexers Overview
Indexers enable objects to be indexed in a similar manner to arrays.
The value keyword is used to define the value being assigned by the set accessor.
Indexers can have more than one formal parameter, for example, when accessing a
two-dimensional array.
Related Sections
Using Indexers
Indexers in Interfaces
C# Language Specification
For more information, see Indexers in the C# Language Specification. The language
specification is the definitive source for C# syntax and usage.
See also
Properties
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Using indexers (C# Programming Guide)
Article • 09/24/2022
Indexers are a syntactic convenience that enable you to create a class, struct, or interface
that client applications can access as an array. The compiler will generate an Item
property (or an alternatively named property if IndexerNameAttribute is present), and
the appropriate accessor methods. Indexers are most frequently implemented in types
whose primary purpose is to encapsulate an internal collection or array. For example,
suppose you have a class TempRecord that represents the temperature in Fahrenheit as
recorded at 10 different times during a 24-hour period. The class contains a temps array
of type float[] to store the temperature values. By implementing an indexer in this
class, clients can access the temperatures in a TempRecord instance as float temp =
tempRecord[4] instead of as float temp = tempRecord.temps[4] . The indexer notation
not only simplifies the syntax for client applications; it also makes the class, and its
purpose more intuitive for other developers to understand.
To declare an indexer on a class or struct, use the this keyword, as the following example
shows:
C#
// Indexer declaration
public int this[int index]
{
// get and set accessors
}
) Important
Remarks
The type of an indexer and the type of its parameters must be at least as accessible as
the indexer itself. For more information about accessibility levels, see Access Modifiers.
For more information about how to use indexers with an interface, see Interface
Indexers.
The signature of an indexer consists of the number and types of its formal parameters. It
doesn't include the indexer type or the names of the formal parameters. If you declare
more than one indexer in the same class, they must have different signatures.
To provide the indexer with a name that other languages can use, use
System.Runtime.CompilerServices.IndexerNameAttribute, as the following example
shows:
C#
// Indexer declaration
[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this[int index]
{
// get and set accessors
}
This indexer will have the name TheItem , as it is overridden by the indexer name
attribute. By default, the indexer name is Item .
Example 1
The following example shows how to declare a private array field, temps , and an indexer.
The indexer enables direct access to the instance tempRecord[i] . The alternative to using
the indexer is to declare the array as a public member and access its members,
tempRecord.temps[i] , directly.
C#
// Indexer declaration.
// If index is out of range, the temps array will throw the exception.
public float this[int index]
{
get => temps[index];
set => temps[index] = value;
}
}
C#
Example 2
The following example declares a class that stores the days of the week. A get accessor
takes a string, the name of a day, and returns the corresponding integer. For example,
"Sunday" returns 0, "Monday" returns 1, and so on.
C#
// Using a string as an indexer value
class DayCollection
{
string[] days = ["Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat"];
Consuming example 2
C#
try
{
Console.WriteLine(week["Made-up day"]);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine($"Not supported input: {e.Message}");
}
Example 3
The following example declares a class that stores the days of the week using the
System.DayOfWeek enum. A get accessor takes a DayOfWeek , the value of a day, and
returns the corresponding integer. For example, DayOfWeek.Sunday returns 0,
DayOfWeek.Monday returns 1, and so on.
C#
class DayOfWeekCollection
{
Day[] days =
[
Day.Sunday, Day.Monday, Day.Tuesday, Day.Wednesday,
Day.Thursday, Day.Friday, Day.Saturday
];
Consuming example 3
C#
try
{
Console.WriteLine(week[(DayOfWeek)43]);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine($"Not supported input: {e.Message}");
}
Robust programming
There are two main ways in which the security and reliability of indexers can be
improved:
See also
Indexers
Properties
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Indexers in Interfaces (C# Programming
Guide)
Article • 09/15/2021
Indexers can be declared on an interface. Accessors of interface indexers differ from the
accessors of class indexers in the following ways:
The purpose of the accessor is to indicate whether the indexer is read-write, read-only,
or write-only. You may provide an implementation for an indexer defined in an interface,
but this is rare. Indexers typically define an API to access data fields, and data fields
cannot be defined in an interface.
C#
// Indexer declaration:
string this[int index]
{
get;
set;
}
}
The signature of an indexer must differ from the signatures of all other indexers
declared in the same interface.
Example
The following example shows how to implement interface indexers.
C#
// Indexer on an interface:
public interface IIndexInterface
{
// Indexer declaration:
int this[int index]
{
get;
set;
}
}
C#
/* Sample output:
Element #0 = 360877544
Element #1 = 327058047
Element #2 = 1913480832
Element #3 = 1519039937
Element #4 = 601472233
Element #5 = 323352310
Element #6 = 1422639981
Element #7 = 1797892494
Element #8 = 875761049
Element #9 = 393083859
*/
In the preceding example, you could use the explicit interface member implementation
by using the fully qualified name of the interface member. For example
C#
string IIndexInterface.this[int index]
{
}
However, the fully qualified name is only needed to avoid ambiguity when the class is
implementing more than one interface with the same indexer signature. For example, if
an Employee class is implementing two interfaces, ICitizen and IEmployee , and both
interfaces have the same indexer signature, the explicit interface member
implementation is necessary. That is, the following indexer declaration:
C#
implements the indexer on the IEmployee interface, while the following declaration:
C#
See also
Indexers
Properties
Interfaces
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Comparison Between Properties and
Indexers (C# Programming Guide)
Article • 03/12/2024
Indexers are like properties. Except for the differences shown in the following table, all
the rules that are defined for property accessors apply to indexer accessors also.
ノ Expand table
Property Indexer
A get accessor of a property has no A get accessor of an indexer has the same formal
parameters. parameter list as the indexer.
A set accessor of a property contains A set accessor of an indexer has the same formal
the implicit value parameter. parameter list as the indexer, and also to the value
parameter.
Supports shortened syntax with Auto- Supports expression bodied members for get only
Implemented Properties. indexers.
See also
Indexers
Properties
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
Provide product feedback
more information, see our
contributor guide.
Events (C# Programming Guide)
Article • 03/12/2024
Events enable a class or object to notify other classes or objects when something of
interest occurs. The class that sends (or raises) the event is called the publisher and the
classes that receive (or handle) the event are called subscribers.
Events Overview
Events have the following properties:
The publisher determines when an event is raised; the subscribers determine what
action is taken in response to the event.
An event can have multiple subscribers. A subscriber can handle multiple events
from multiple publishers.
Events are typically used to signal user actions such as button clicks or menu
selections in graphical user interfaces.
When an event has multiple subscribers, the event handlers are invoked
synchronously when an event is raised. To invoke events asynchronously, see
Calling Synchronous Methods Asynchronously.
In the .NET class library, events are based on the EventHandler delegate and the
EventArgs base class.
Related Sections
For more information, see:
C# Language Specification
For more information, see Events in the C# Language Specification. The language
specification is the definitive source for C# syntax and usage.
See also
EventHandler
Delegates
Creating Event Handlers in Windows Forms
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to subscribe to and unsubscribe
from events (C# Programming Guide)
Article • 10/12/2021
You subscribe to an event that is published by another class when you want to write
custom code that is called when that event is raised. For example, you might subscribe
to a button's click event in order to make your application do something useful when
the user clicks the button.
3. Double-click the event that you want to create, for example the Load event.
Visual C# creates an empty event handler method and adds it to your code.
Alternatively you can add the code manually in Code view. For example, the
following lines of code declare an event handler method that will be called when
the Form class raises the Load event.
C#
The line of code that is required to subscribe to the event is also automatically
generated in the InitializeComponent method in the Form1.Designer.cs file in your
project. It resembles this:
C#
C#
2. Use the addition assignment operator ( += ) to attach an event handler to the event.
In the following example, assume that an object named publisher has an event
named RaiseCustomEvent . Note that the subscriber class needs a reference to the
publisher class in order to subscribe to its events.
C#
publisher.RaiseCustomEvent += HandleCustomEvent;
C#
public Form1()
{
InitializeComponent();
this.Click += (s,e) =>
{
MessageBox.Show(((MouseEventArgs)e).Location.ToString());
};
}
kind of specialized event information. Note that the subscriber class needs a reference
to publisher in order to subscribe to its events.
C#
publisher.RaiseCustomEvent += (object o, CustomEventArgs e) =>
{
string s = o.ToString() + " " + e.ToString();
Console.WriteLine(s);
};
You cannot easily unsubscribe from an event if you used an anonymous function to
subscribe to it. To unsubscribe in this scenario, go back to the code where you subscribe
to the event, store the anonymous function in a delegate variable, and then add the
delegate to the event. We recommend that you don't use anonymous functions to
subscribe to events if you have to unsubscribe from the event at some later point in
your code. For more information about anonymous functions, see Lambda expressions.
Unsubscribing
To prevent your event handler from being invoked when the event is raised, unsubscribe
from the event. In order to prevent resource leaks, you should unsubscribe from events
before you dispose of a subscriber object. Until you unsubscribe from an event, the
multicast delegate that underlies the event in the publishing object has a reference to
the delegate that encapsulates the subscriber's event handler. As long as the publishing
object holds that reference, garbage collection will not delete your subscriber object.
C#
publisher.RaiseCustomEvent -= HandleCustomEvent;
When all subscribers have unsubscribed from an event, the event instance in the
publisher class is set to null .
See also
Events
event
How to publish events that conform to .NET Guidelines
- and -= operators
+ and += operators
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to publish events that conform to
.NET Guidelines (C# Programming
Guide)
Article • 09/15/2021
The following procedure demonstrates how to add events that follow the standard .NET
pattern to your classes and structs. All events in the .NET class library are based on the
EventHandler delegate, which is defined as follows:
C#
7 Note
Although events in classes that you define can be based on any valid delegate type,
even delegates that return a value, it is generally recommended that you base your
events on the .NET pattern by using EventHandler, as shown in the following example.
The name EventHandler can lead to a bit of confusion as it doesn't actually handle the
event. The EventHandler, and generic EventHandler<TEventArgs> are delegate types. A
method or lambda expression whose signature matches the delegate definition is the
event handler and will be invoked when the event is raised.
C#
public class CustomEventArgs : EventArgs
{
public CustomEventArgs(string message)
{
Message = message;
}
2. (Skip this step if you are using the generic version of EventHandler<TEventArgs>.)
Declare a delegate in your publishing class. Give it a name that ends with
EventHandler . The second parameter specifies your custom EventArgs type.
C#
3. Declare the event in your publishing class by using one of the following steps.
a. If you have no custom EventArgs class, your Event type will be the non-generic
EventHandler delegate. You do not have to declare the delegate because it is
already declared in the System namespace that is included when you create
your C# project. Add the following code to your publisher class.
C#
b. If you are using the non-generic version of EventHandler and you have a
custom class derived from EventArgs, declare your event inside your publishing
class and use your delegate from step 2 as the type.
C#
c. If you are using the generic version, you do not need a custom delegate.
Instead, in your publishing class, you specify your event type as
EventHandler<CustomEventArgs> , substituting the name of your own class
C#
public event EventHandler<CustomEventArgs>? RaiseCustomEvent;
Example
The following example demonstrates the previous steps by using a custom EventArgs
class and EventHandler<TEventArgs> as the event type.
C#
namespace DotNetEvents
{
// Define a class to hold custom event info
public class CustomEventArgs : EventArgs
{
public CustomEventArgs(string message)
{
Message = message;
}
class Program
{
static void Main()
{
var pub = new Publisher();
var sub1 = new Subscriber("sub1", pub);
var sub2 = new Subscriber("sub2", pub);
See also
Delegate
Events
Delegates
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to raise base class events in
derived classes (C# Programming Guide)
Article • 03/12/2024
The following simple example shows the standard way to declare events in a base class
so that they can also be raised from derived classes. This pattern is used extensively in
Windows Forms classes in the .NET class libraries.
When you create a class that can be used as a base class for other classes, you should
consider the fact that events are a special type of delegate that can only be invoked
from within the class that declared them. Derived classes cannot directly invoke events
that are declared within the base class. Although sometimes you may want an event that
can only be raised by the base class, most of the time, you should enable the derived
class to invoke base class events. To do this, you can create a protected invoking
method in the base class that wraps the event. By calling or overriding this invoking
method, derived classes can invoke the event indirectly.
7 Note
Do not declare virtual events in a base class and override them in a derived class.
The C# compiler does not handle these correctly and it is unpredictable whether a
subscriber to the derived event will actually be subscribing to the base class event.
Example
C#
namespace BaseClassEvents
{
// Special EventArgs class to hold info about Shapes.
public class ShapeEventArgs : EventArgs
{
public ShapeEventArgs(double area)
{
NewArea = area;
}
public ShapeContainer()
{
_list = new List<Shape>();
}
class Test
{
static void Main()
{
//Create the event publishers and subscriber
var circle = new Circle(54);
var rectangle = new Rectangle(12, 9);
var container = new ShapeContainer();
See also
Events
Delegates
Access Modifiers
Creating Event Handlers in Windows Forms
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to implement interface events (C#
Programming Guide)
Article • 09/15/2021
An interface can declare an event. The following example shows how to implement
interface events in a class. Basically the rules are the same as when you implement any
interface method or property.
C#
namespace ImplementInterfaceEvents
{
public interface IDrawingObject
{
event EventHandler ShapeChanged;
}
public class MyEventArgs : EventArgs
{
// class members
}
public class Shape : IDrawingObject
{
public event EventHandler ShapeChanged;
void ChangeShape()
{
// Do something here before the event…
OnShapeChanged(new MyEventArgs(/*arguments*/));
Example
The following example shows how to handle the less-common situation in which your
class inherits from two or more interfaces and each interface has an event with the same
name. In this situation, you must provide an explicit interface implementation for at least
one of the events. When you write an explicit interface implementation for an event, you
must also write the add and remove event accessors. Normally these are provided by the
compiler, but in this case the compiler cannot provide them.
By providing your own accessors, you can specify whether the two events are
represented by the same event in your class, or by different events. For example, if the
events should be raised at different times according to the interface specifications, you
can associate each event with a separate implementation in your class. In the following
example, subscribers determine which OnDraw event they will receive by casting the
shape reference to either an IShape or an IDrawingObject .
C#
namespace WrapTwoInterfaceEvents
{
using System;
Console.WriteLine("Drawing a shape.");
See also
Events
Delegates
Explicit Interface Implementation
How to raise base class events in derived classes
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
How to implement custom event
accessors (C# Programming Guide)
Article • 09/15/2021
An event is a special kind of multicast delegate that can only be invoked from within the
class that it is declared in. Client code subscribes to the event by providing a reference
to a method that should be invoked when the event is fired. These methods are added
to the delegate's invocation list through event accessors, which resemble property
accessors, except that event accessors are named add and remove . In most cases, you
do not have to supply custom event accessors. When no custom event accessors are
supplied in your code, the compiler will add them automatically. However, in some cases
you may have to provide custom behavior. One such case is shown in the topic How to
implement interface events.
Example
The following example shows how to implement custom add and remove event
accessors. Although you can substitute any code inside the accessors, we recommend
that you lock the event before you add or remove a new event handler method.
C#
See also
Events
event
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Generic type parameters (C#
Programming Guide)
Article • 03/12/2024
specifying a type argument inside the angle brackets. The type argument for this
particular class can be any type recognized by the compiler. Any number of constructed
type instances can be created, each one using a different type argument, as follows:
C#
You can learn the naming conventions for generic type parameters in the article on
naming conventions.
See also
System.Collections.Generic
Generics
Differences Between C++ Templates and C# Generics
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review Open a documentation issue
issues and pull requests. For
more information, see our Provide product feedback
contributor guide.
Constraints on type parameters (C#
Programming Guide)
Article • 07/30/2024
Constraints inform the compiler about the capabilities a type argument must have.
Without any constraints, the type argument could be any type. The compiler can only
assume the members of System.Object, which is the ultimate base class for any .NET
type. For more information, see Why use constraints. If client code uses a type that
doesn't satisfy a constraint, the compiler issues an error. Constraints are specified by
using the where contextual keyword. The following table lists the various types of
constraints:
ノ Expand table
Constraint Description
where T : The type argument must be a non-nullable value type, which includes record
struct struct types. For information about nullable value types, see Nullable value
types. Because all value types have an accessible parameterless constructor,
either declared or implicit, the struct constraint implies the new() constraint
and can't be combined with the new() constraint. You can't combine the struct
constraint with the unmanaged constraint.
where T : The type argument must be a reference type. This constraint applies also to any
class class, interface, delegate, or array type. In a nullable context, T must be a non-
nullable reference type.
where T : The type argument must be a reference type, either nullable or non-nullable. This
class? constraint applies also to any class, interface, delegate, or array type, including
records.
where T : The type argument must be a non-nullable type. The argument can be a non-
notnull nullable reference type or a non-nullable value type.
where T : The type argument must be a non-nullable unmanaged type. The unmanaged
unmanaged constraint implies the struct constraint and can't be combined with either the
struct or new() constraints.
where T : The type argument must have a public parameterless constructor. When used
new() together with other constraints, the new() constraint must be specified last. The
new() constraint can't be combined with the struct and unmanaged constraints.
where T : The type argument must be or derive from the specified base class. In a nullable
<base class context, T must be a non-nullable reference type derived from the specified base
name> class.
Constraint Description
where T : The type argument must be or derive from the specified base class. In a nullable
<base class context, T can be either a nullable or non-nullable type derived from the
name>? specified base class.
where T : The type argument must be or implement the specified interface. Multiple
<interface interface constraints can be specified. The constraining interface can also be
name> generic. In a nullable context, T must be a non-nullable type that implements the
specified interface.
where T : The type argument must be or implement the specified interface. Multiple
<interface interface constraints can be specified. The constraining interface can also be
name>? generic. In a nullable context, T can be a nullable reference type, a non-nullable
reference type, or a value type. T can't be a nullable value type.
where T : U The type argument supplied for T must be or derive from the argument supplied
for U . In a nullable context, if U is a non-nullable reference type, T must be a
non-nullable reference type. If U is a nullable reference type, T can be either
nullable or non-nullable.
where T : This constraint resolves the ambiguity when you need to specify an
default unconstrained type parameter when you override a method or provide an explicit
interface implementation. The default constraint implies the base method
without either the class or struct constraint. For more information, see the
default constraint spec proposal.
where T : This anti-constraint declares that the type argument for T can be a ref struct
allows ref type. The generic type or method must obey ref safety rules for any instance of T
struct because it might be a ref struct .
Some constraints are mutually exclusive, and some constraints must be in a specified
order:
You can apply at most one of the struct , class , class? , notnull , and unmanaged
constraints. If you supply any of these constraints, it must be the first constraint
specified for that type parameter.
The base class constraint ( where T : Base or where T : Base? ) can't be combined
with any of the constraints struct , class , class? , notnull , or unmanaged .
You can apply at most one base class constraint, in either form. If you want to
support the nullable base type, use Base? .
You can't name both the non-nullable and nullable form of an interface as a
constraint.
The new() constraint can't be combined with the struct or unmanaged constraint.
If you specify the new() constraint, it must be the last constraint for that type
parameter. Anti-constraints, if applicable, can follow the new() constraint.
The default constraint can be applied only on override or explicit interface
implementations. It can't be combined with either the struct or class constraints.
The allows ref struct anti-constraint can't be combined with the class or
class? constraint.
The allows ref struct anti-constraint must follow all constraints for that type
parameter.
C#
public T? FindFirstOccurrence(string s)
{
Node? current = head;
T? t = null;
The constraint enables the generic class to use the Employee.Name property. The
constraint specifies that all items of type T are guaranteed to be either an Employee
object or an object that inherits from Employee .
Multiple constraints can be applied to the same type parameter, and the constraints
themselves can be generic types, as follows:
C#
C#
The compiler only knows that T is a reference type at compile time and must use the
default operators that are valid for all reference types. If you must test for value equality,
apply the where T : IEquatable<T> or where T : IComparable<T> constraint and
implement the interface in any class used to construct the generic class.
C#
class Base { }
class Test<T, U>
where U : struct
where T : Base, new()
{ }
C#
In the previous example, T is a type constraint in the context of the Add method, and an
unbounded type parameter in the context of the List class.
Type parameters can also be used as constraints in generic class definitions. The type
parameter must be declared within the angle brackets together with any other type
parameters:
C#
The usefulness of type parameters as constraints with generic classes is limited because
the compiler can assume nothing about the type parameter except that it derives from
System.Object . Use type parameters as constraints on generic classes in scenarios in
which you want to enforce an inheritance relationship between two type parameters.
notnull constraint
You can use the notnull constraint to specify that the type argument must be a non-
nullable value type or non-nullable reference type. Unlike most other constraints, if a
type argument violates the notnull constraint, the compiler generates a warning
instead of an error.
The notnull constraint has an effect only when used in a nullable context. If you add
the notnull constraint in a nullable oblivious context, the compiler doesn't generate any
warnings or errors for violations of the constraint.
class constraint
The class constraint in a nullable context specifies that the type argument must be a
non-nullable reference type. In a nullable context, when a type argument is a nullable
reference type, the compiler generates a warning.
default constraint
The addition of nullable reference types complicates the use of T? in a generic type or
method. T? can be used with either the struct or class constraint, but one of them
must be present. When the class constraint was used, T? referred to the nullable
reference type for T . T? can be used when neither constraint is applied. In that case, T?
is interpreted as T? for value types and reference types. However, if T is an instance of
Nullable<T>, T? is the same as T . In other words, it doesn't become T?? .
Because T? can now be used without either the class or struct constraint, ambiguities
can arise in overrides or explicit interface implementations. In both those cases, the
override doesn't include the constraints, but inherits them from the base class. When the
base class doesn't apply either the class or struct constraint, derived classes need to
somehow specify an override applies to the base method without either constraint. The
derived method applies the default constraint. The default constraint clarifies neither
the class nor struct constraint.
Unmanaged constraint
You can use the unmanaged constraint to specify that the type parameter must be a non-
nullable unmanaged type. The unmanaged constraint enables you to write reusable
routines to work with types that can be manipulated as blocks of memory, as shown in
the following example:
C#
unsafe public static byte[] ToByteArray<T>(this T argument) where T :
unmanaged
{
var size = sizeof(T);
var result = new Byte[size];
Byte* p = (byte*)&argument;
for (var i = 0; i < size; i++)
result[i] = *p++;
return result;
}
The preceding method must be compiled in an unsafe context because it uses the
sizeof operator on a type not known to be a built-in type. Without the unmanaged
The unmanaged constraint implies the struct constraint and can't be combined with it.
Because the struct constraint implies the new() constraint, the unmanaged constraint
can't be combined with the new() constraint as well.
Delegate constraints
You can use System.Delegate or System.MulticastDelegate as a base class constraint. The
CLR always allowed this constraint, but the C# language disallowed it. The
System.Delegate constraint enables you to write code that works with delegates in a
type-safe manner. The following code defines an extension method that combines two
delegates provided they're the same type:
C#
You can use the preceding method to combine delegates that are the same type:
C#
If you uncomment the last line, it doesn't compile. Both first and test are delegate
types, but they're different delegate types.
Enum constraints
You can also specify the System.Enum type as a base class constraint. The CLR always
allowed this constraint, but the C# language disallowed it. Generics using System.Enum
provide type-safe programming to cache results from using the static methods in
System.Enum . The following sample finds all the valid values for an enum type, and then
C#
You can call EnumNamedValues to build a collection that is cached and reused rather than
repeating the calls that require reflection.
You could use it as shown in the following sample to create an enum and build a
dictionary of its values and names:
C#
enum Rainbow
{
Red,
Orange,
Yellow,
Green,
Blue,
Indigo,
Violet
}
C#
C#
This pattern enables the C# compiler to determine the containing type for the
overloaded operators, or any static virtual or static abstract method. It provides
the syntax so that the addition and subtraction operators can be defined on a
containing type. Without this constraint, the parameters and arguments would be
required to be declared as the interface, rather than the type parameter:
C#
It can't be boxed.
It participates in ref safety rules.
Instances can't be used where a ref struct type isn't allowed, such as static
fields.
Instances can be marked with the scoped modifier.
The allows ref struct clause isn't inherited. In the following code:
C#
The argument for S can't be a ref struct because S doesn't have the allows ref
struct clause.
A type parameter that has the allows ref struct clause can't be used as a type
argument unless the corresponding type parameter also has the allows ref struct
clause. This rule is demonstrated in the following example:
C#
The preceding sample shows that a type argument that might be a ref struct type
can't be substituted for a type parameter that can't be a ref struct type.
See also
System.Collections.Generic
Introduction to Generics
Generic Classes
new Constraint
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Generic Classes (C# Programming
Guide)
Article • 07/09/2022
Generic classes encapsulate operations that are not specific to a particular data type.
The most common use for generic classes is with collections like linked lists, hash tables,
stacks, queues, trees, and so on. Operations such as adding and removing items from
the collection are performed in basically the same way regardless of the type of data
being stored.
For most scenarios that require collection classes, the recommended approach is to use
the ones provided in the .NET class library. For more information about using these
classes, see Generic Collections in .NET.
Typically, you create generic classes by starting with an existing concrete class, and
changing types into type parameters one at a time until you reach the optimal balance
of generalization and usability. When creating your own generic classes, important
considerations include the following:
As a rule, the more types you can parameterize, the more flexible and reusable
your code becomes. However, too much generalization can create code that is
difficult for other developers to read or understand.
What constraints, if any, to apply to the type parameters (See Constraints on Type
Parameters).
A good rule is to apply the maximum constraints possible that will still let you
handle the types you must handle. For example, if you know that your generic class
is intended for use only with reference types, apply the class constraint. That will
prevent unintended use of your class with value types, and will enable you to use
the as operator on T , and check for null values.
Because generic classes can serve as base classes, the same design considerations
apply here as with non-generic classes. See the rules about inheriting from generic
base classes later in this topic.
The rules for type parameters and constraints have several implications for generic class
behavior, especially regarding inheritance and member accessibility. Before proceeding,
you should understand some terms. For a generic class Node<T>, client code can
reference the class either by specifying a type argument - to create a closed constructed
type ( Node<int> ); or by leaving the type parameter unspecified - for example when you
specify a generic base class, to create an open constructed type ( Node<T> ). Generic
classes can inherit from concrete, closed constructed, or open constructed base classes:
C#
class BaseNode { }
class BaseNodeGeneric<T> { }
// concrete type
class NodeConcrete<T> : BaseNode { }
Non-generic, in other words, concrete, classes can inherit from closed constructed base
classes, but not from open constructed classes or from type parameters because there is
no way at run time for client code to supply the type argument required to instantiate
the base class.
C#
//No error
class Node1 : BaseNodeGeneric<int> { }
//Generates an error
//class Node2 : BaseNodeGeneric<T> {}
//Generates an error
//class Node3 : T {}
Generic classes that inherit from open constructed types must supply type arguments
for any base class type parameters that are not shared by the inheriting class, as
demonstrated in the following code:
C#
//No error
class Node4<T> : BaseNodeMultiple<T, int> { }
//No error
class Node5<T, U> : BaseNodeMultiple<T, U> { }
//Generates an error
//class Node6<T> : BaseNodeMultiple<T, U> {}
Generic classes that inherit from open constructed types must specify constraints that
are a superset of, or imply, the constraints on the base type:
C#
Generic types can use multiple type parameters and constraints, as follows:
C#
Open constructed and closed constructed types can be used as method parameters:
C#
List<DerivedClass> .
See also
System.Collections.Generic
Generics
Saving the State of Enumerators
An Inheritance Puzzle, Part One
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Generic Interfaces (C# Programming
Guide)
Article • 03/12/2024
It's often useful to define interfaces either for generic collection classes, or for the
generic classes that represent items in the collection. To avoid boxing and unboxing
operations on value types, it's better to use generic interfaces, such as IComparable<T>,
on generic classes. The .NET class library defines several generic interfaces for use with
the collection classes in the System.Collections.Generic namespace. For more
information about these interfaces, see Generic interfaces.
the generic CompareTo method on list elements. In this example, list elements are a
simple class, Person that implements IComparable<Person> .
C#
do
{
Node previous = null;
Node current = head;
swapped = false;
if (previous == null)
{
head = tmp;
}
else
{
previous.next = tmp;
}
previous = tmp;
swapped = true;
}
else
{
previous = current;
current = current.next;
}
}
} while (swapped);
}
}
int[] ages = [45, 19, 28, 23, 18, 9, 108, 72, 30, 35];
C#
C#
C#
interface IMonth<T> { }
Generic interfaces can inherit from non-generic interfaces if the generic interface is
covariant, which means it only uses its type parameter as a return value. In the .NET class
library, IEnumerable<T> inherits from IEnumerable because IEnumerable<T> only uses
T in the return value of GetEnumerator and in the Current property getter.
C#
interface IBaseInterface<T> { }
class SampleClass : IBaseInterface<string> { }
C#
interface IBaseInterface1<T> { }
interface IBaseInterface2<T, U> { }
The rules that control method overloading are the same for methods within generic
classes, generic structs, or generic interfaces. For more information, see Generic
Methods.
Beginning with C# 11, interfaces may declare static abstract or static virtual
members. Interfaces that declare either static abstract or static virtual members
are almost always generic interfaces. The compiler must resolve calls to static virtual
and static abstract methods at compile time. static virtual and static abstract
methods declared in interfaces don't have a runtime dispatch mechanism analogous to
virtual or abstract methods declared in classes. Instead, the compiler uses type
information available at compile time. These members are typically declared in generic
interfaces. Furthermore, most interfaces that declare static virtual or static abstract
methods declare that one of the type parameters must implement the declared
interface. The compiler then uses the supplied type arguments to resolve the type of the
declared member.
See also
Introduction to Generics
interface
Generics
6 Collaborate with us on
GitHub .NET feedback
The source for this content can .NET is an open source project.
Select a link to provide feedback:
be found on GitHub, where you
can also create and review Open a documentation issue
issues and pull requests. For
more information, see our Provide product feedback
contributor guide.
Generic methods (C# programming
guide)
Article • 03/27/2024
C#
The following code example shows one way to call the method by using int for the
type argument:
C#
You can also omit the type argument and the compiler will infer it. The following call to
Swap is equivalent to the previous call:
C#
The same rules for type inference apply to static methods and instance methods. The
compiler can infer the type parameters based on the method arguments you pass in; it
cannot infer the type parameters only from a constraint or return value. Therefore type
inference does not work with methods that have no parameters. Type inference occurs
at compile time before the compiler tries to resolve overloaded method signatures. The
compiler applies type inference logic to all generic methods that share the same name.
In the overload resolution step, the compiler includes only those generic methods on
which type inference succeeded.
Within a generic class, non-generic methods can access the class-level type parameters,
as follows:
C#
class SampleClass<T>
{
void Swap(ref T lhs, ref T rhs) { }
}
If you define a generic method that takes the same type parameters as the containing
class, the compiler generates warning CS0693 because within the method scope, the
argument supplied for the inner T hides the argument supplied for the outer T . If you
require the flexibility of calling a generic class method with type arguments other than
the ones provided when the class was instantiated, consider providing another identifier
for the type parameter of the method, as shown in GenericList2<T> in the following
example.
C#
class GenericList<T>
{
// CS0693.
void SampleMethod<T>() { }
}
class GenericList2<T>
{
// No warning.
void SampleMethod<U>() { }
}
C#
Generic methods can be overloaded on several type parameters. For example, the
following methods can all be located in the same class:
C#
void DoWork() { }
void DoWork<T>() { }
void DoWork<T, U>() { }
You can also use the type parameter as the return type of a method. The following code
example shows a method that returns an array of type T :
C#
T[] Swap<T>(T a, T b)
{
return [b, a];
}
C# Language Specification
For more information, see the C# Language Specification.
See also
System.Collections.Generic
Introduction to Generics
Methods
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our Provide product feedback
contributor guide.
Generics and Arrays (C# Programming
Guide)
Article • 03/12/2024
The following code example demonstrates how a single generic method that takes an
IList<T> input parameter can iterate through both a list and an array, in this case an
array of integers.
C#
class Program
{
static void Main()
{
int[] arr = [0, 1, 2, 3, 4];
List<int> list = new List<int>();
ProcessItems<int>(arr);
ProcessItems<int>(list);
}
See also
System.Collections.Generic
Generics
Arrays
Generics
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Generic Delegates (C# Programming
Guide)
Article • 03/12/2024
A delegate can define its own type parameters. Code that references the generic
delegate can specify the type argument to create a closed constructed type, just like
when instantiating a generic class or calling a generic method, as shown in the following
example:
C#
C# version 2.0 has a new feature called method group conversion, which applies to
concrete as well as generic delegate types, and enables you to write the previous line
with this simplified syntax:
C#
Del<int> m2 = Notify;
Delegates defined within a generic class can use the generic class type parameters in the
same way that class methods do.
C#
class Stack<T>
{
public delegate void StackDelegate(T[] items);
}
Code that references the delegate must specify the type argument of the containing
class, as follows:
C#
Generic delegates are especially useful in defining events based on the typical design
pattern because the sender argument can be strongly typed and no longer has to be
cast to and from Object.
C#
class Stack<T>
{
public class StackEventArgs : System.EventArgs { }
public event StackEventHandler<Stack<T>, StackEventArgs>? StackEvent;
class SampleClass
{
public void HandleStackChange<T>(Stack<T> stack, Stack<T>.StackEventArgs
args) { }
}
See also
System.Collections.Generic
Introduction to Generics
Generic Methods
Generic Classes
Generic Interfaces
Delegates
Generics
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Differences Between C++ Templates
and C# Generics (C# Programming
Guide)
Article • 03/12/2024
C# Generics and C++ templates are both language features that provide support for
parameterized types. However, there are many differences between the two. At the
syntax level, C# generics are a simpler approach to parameterized types without the
complexity of C++ templates. In addition, C# does not attempt to provide all of the
functionality that C++ templates provide. At the implementation level, the primary
difference is that C# generic type substitutions are performed at run time and generic
type information is thereby preserved for instantiated objects. For more information, see
Generics in the Runtime.
The following are the key differences between C# Generics and C++ templates:
C# generics do not provide the same amount of flexibility as C++ templates. For
example, it is not possible to call arithmetic operators in a C# generic class,
although it is possible to call user defined operators.
C# does not allow non-type template parameters, such as template C<int i> {} .
C# does not allow the type parameter to be used as the base class for the generic
type.
C++ allows code that might not be valid for all type parameters in the template,
which is then checked for the specific type used as the type parameter. C# requires
code in a class to be written in such a way that it will work with any type that
satisfies the constraints. For example, in C++ it is possible to write a function that
uses the arithmetic operators + and - on objects of the type parameter, which will
produce an error at the time of instantiation of the template with a type that does
not support these operators. C# disallows this; the only language constructs
allowed are those that can be deduced from the constraints.
See also
Introduction to Generics
Templates
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
be found on GitHub, where you Select a link to provide feedback:
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
Generics in the runtime (C#
programming guide)
Article • 04/20/2024
When a generic type or method is compiled into common intermediate language (CIL),
it contains metadata that identifies it as having type parameters. How the CIL for a
generic type is used differs based on whether the supplied type parameter is a value
type or reference type.
When a generic type is first constructed with a value type as a parameter, the runtime
creates a specialized generic type with the supplied parameter or parameters
substituted in the appropriate locations in the CIL. Specialized generic types are created
one time for each unique value type that is used as a parameter.
For example, suppose your program code declared a stack that's constructed of
integers:
C#
Stack<int>? stack;
At this point, the runtime generates a specialized version of the Stack<T> class that has
the integer substituted appropriately for its parameter. Now, whenever your program
code uses a stack of integers, the runtime reuses the generated specialized Stack<T>
class. In the following example, two instances of a stack of integers are created, and they
share a single instance of the Stack<int> code:
C#
However, suppose that another Stack<T> class with a different value type such as a
long or a user-defined structure as its parameter is created at another point in your
code. As a result, the runtime generates another version of the generic type and
substitutes a long in the appropriate locations in CIL. Conversions are no longer
necessary because each specialized generic class natively contains the value type.
Generics work somewhat differently for reference types. The first time a generic type is
constructed with any reference type, the runtime creates a specialized generic type with
object references substituted for the parameters in the CIL. Then, every time that a
constructed type is instantiated with a reference type as its parameter, regardless of
what type it is, the runtime reuses the previously created specialized version of the
generic type. This is possible because all references are the same size.
For example, suppose you had two reference types, a Customer class and an Order class,
and also suppose that you created a stack of Customer types:
C#
class Customer { }
class Order { }
C#
Stack<Customer> customers;
At this point, the runtime generates a specialized version of the Stack<T> class that
stores object references that will be filled in later instead of storing data. Suppose the
next line of code creates a stack of another reference type, which is named Order :
C#
Unlike with value types, another specialized version of the Stack<T> class is not created
for the Order type. Instead, an instance of the specialized version of the Stack<T> class
is created and the orders variable is set to reference it. Suppose that you then
encountered a line of code to create a stack of a Customer type:
C#
As with the previous use of the Stack<T> class created by using the Order type, another
instance of the specialized Stack<T> class is created. The pointers that are contained
therein are set to reference an area of memory the size of a Customer type. Because the
number of reference types can vary wildly from program to program, the C#
implementation of generics greatly reduces the amount of code by reducing to one the
number of specialized classes created by the compiler for generic classes of reference
types.
Moreover, when a generic C# class is instantiated by using a value type or reference
type parameter, reflection can query it at run time and both its actual type and its type
parameter can be ascertained.
See also
System.Collections.Generic
Introduction to Generics
Generics
6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
Open a documentation issue
issues and pull requests. For
more information, see our
Provide product feedback
contributor guide.
C# language reference
The language reference provides an informal reference to C# syntax and idioms for
beginners and experienced C# and .NET developers.
C# language reference
e OVERVIEW
C# language strategy
i REFERENCE
C# keywords
What's new
h WHAT'S NEW
What's new in C# 12
What's new in C# 11
What's new in C# 10
i REFERENCE
Version compatibility
Stay in touch
i REFERENCE
Twitter
Specifications
Read the detailed specification for the C# language and the detailed specifications for
the latest features.
e OVERVIEW
Specification process
i REFERENCE
i REFERENCE
Foreword
Introduction
i REFERENCE
Scope
Normative references
General description
Conformance
i REFERENCE
Lexical structure
Basic concepts
Types
Variables
Conversions
Patterns
Expressions
Statements
i REFERENCE
Namespaces
Classes
Structs
Arrays
Interfaces
Enums
Delegates
Exceptions
Attributes
Unsafe code
i REFERENCE
Grammar
Portability issues
Standard library
Documentation comments
Bibliography