The null-conditional operator allows you to safely access properties or call methods on an object that might be null without causing a null reference exception.
Example: int? length = text?.Length;
String interpolation simplifies string formatting by allowing you to embed expressions directly within a string using the $ symbol.
Example: string name = "Alice"; string message = $"Hello, {name}!";
You can combine string interpolation with format strings to control the formatting of interpolated values.
Example: $"Today is {DateTime.Now:yyyy-MM-dd}"
Use format specifiers directly within interpolated strings or composite format strings to control the formatting of values.
Example:
decimal price = 19.99M;
// Same output - Formats 'price' as currency with 2 decimal places
string message = $"The price is: {price:C2}";
string message = string.Format("The price is: {0:C2}", price);
B - Binary
C - Curreny
D - Decimal
E - Exponential(scientific)
F - Fixed-point
G - General
N - Number
P - Percent
R - Round-trip
X - Hexadecimal
You can use expression-bodied syntax to simplify the code for read-only properties, methods, and operators.
Example: public int Square(int x) => x * x;
Pattern matching simplifies complex conditional statements by providing a concise syntax for testing values against patterns.
Example: if (obj is int number) { /* use 'number' */ }
Local functions allow you to declare methods within methods, improving encapsulation and code organization.
Example:
int Add(int a, int b)
{
int LocalAdd(int x, int y) => x + y;
return LocalAdd(a, b);
}
Deconstruction enables you to split a tuple or other data structure into individual variables easily.
Example: (int x, int y) = GetPoint();
You can declare and initialize variables directly in the argument list of a method using the out keyword.
Example: if (int.TryParse(input, out int result)) { /* use 'result' */ }
Discards (_) allow you to ignore values in deconstruction or pattern matching when you're not interested in them.
Example: (int x, _) = GetPoint();
Tuple types and literals enable you to work with lightweight data structures without creating custom classes or structures.
Example: (string Name, int Age) person = ("Alice", 30);
The default keyword allows you to get the default value of a data type without knowing the type in advance.
Example: int defaultValue = default; // '0' for int
Object initializers allow you to create and initialize objects in a concise way.
Example: Person person = new Person { Name = "Alice", Age = 30 };
Extension methods enable you to add new methods to existing types without modifying their source code.
Example: Adding a custom extension method to the string type.
public static class StringExtensions
{
public static bool IsPalindrome(this string str)
{
// Implementation to check if 'str' is a palindrome
}
}
Asynchronous programming with async and await allows you to write non-blocking code, making your applications more responsive.
Example: async Task<string> DownloadDataAsync() { /* async operation */ }
Lambda expressions enable you to define inline, anonymous functions, making it easier to work with delegates, LINQ, and functional programming.
Example: Func<int, int> square = x => x * x;
The var keyword allows the compiler to infer the data type of a variable based on the assigned value, reducing the need for explicit type declarations.
Example: var number = 42; // 'number' is of type int
Caller Information attributes (CallerMemberName, CallerFilePath, CallerLineNumber) provide information about the caller of a method or property, aiding in debugging and logging.
Example:
public void Log(string message, [CallerMemberName] string caller = "")
{
Console.WriteLine($"Caller: {caller}, Message: {message}");
}
Custom exception filters allow you to specify conditions under which an exception handler should execute, providing more fine-grained control over exception handling.
Example:
try {
// ...
} catch (Exception ex) when (ex is IOException ioException) {
// Handle IOException specifically
}
You can deconstruct tuples directly in a foreach loop, making it easier to iterate over collections of tuples.
Example:
var points = new List<(int X, int Y)> { (1, 2), (3, 4), (5, 6) };
foreach (var (x, y) in points) {
Console.WriteLine($"X: {x}, Y: {y}");
}
In ASP.NET Core, attribute-based routing allows you to define routing information directly on controller actions, making routing configuration more concise.
Example:
[Route("api/[controller]")]
public class UsersController : ControllerBase {
[HttpGet("{id}")]
public IActionResult GetUser(int id) { /* ... */ }
}