使用最新的 C# 功能进行更新对于编写更简洁、更高效和更现代的代码至关重要。以下是_最新版本中一些最新和最有用的 C# 功能,以及可帮助您将它们集成到代码库中的示例:_
记录类型是引用类型,它们为值相等性、不变性和简洁性提供内置功能。它们非常适合不变性和结构相等性很重要的数据模型。
例:
public record Person(string FirstName, string LastName);
var person1 = new Person("John", "Doe");
var person2 = new Person("John", "Doe");
Console.WriteLine(person1 == person2); // True (value equality)
With-expressions:记录附带关键字,允许您通过复制修改后的现有记录来创建新记录。with
var person3 = person1 with { LastName = "Smith" };
模式匹配在最新版本的 C# 中得到了极大的增强。现在,您可以在更多方案中使用模式,并且具有更大的灵活性,包括关系模式、逻辑模式和属性模式。
关系模式:根据比较进行匹配。
int age = 25;
var category = age switch
{
< 18 => "Child",
< 65 => "Adult",
_ => "Senior"
};
逻辑模式:将模式与逻辑运算符 (、、) 组合在一起。andornot
int number = 10;
if (number is > 0 and < 100)
{
Console.WriteLine("Number is between 0 and 100.");
}
Property Patterns:以更简洁的方式匹配特定属性。
if (person1 is { FirstName: "John", LastName: "Doe" })
{
Console.WriteLine("It's John Doe!");
}
访问器允许您在对象初始化后使属性不可变。此功能有助于创建不可变对象,同时在对象构造过程中保持灵活性。init
例:
public class Car
{
public string Make { get; init; }
public string Model { get; init; }
}
var car = new Car { Make = "Tesla", Model = "Model S" };
// car.Make = "Ford"; // This will cause a compile-time error
顶级语句允许您在控制台应用程序中省略 method 的样板代码,从而使入口点更简洁。Main
之前:
class Program
{
static void Main(string\[\] args)
{
Console.WriteLine("Hello, World!");
}
}
现在:
Console.WriteLine("Hello, World!");
此功能允许您在上下文中明确时省略类型,从而简化了对象创建。
示例:
List<int> numbers = new(); // Type inferred from the variable declaration
Person person = new("Jane", "Doe"); // Type inferred from constructor call
尽管表达式通常与记录类型一起使用,但 C# 10.0 也为非记录类型引入了此功能,允许您在修改对象的某些属性的同时克隆对象。with
示例:
var car1 = new Car { Make = "Tesla", Model = "Model 3" };
var car2 = car1 with { Model = "Model S" };
您现在可以声明全局 using 指令来简化项目中所有文件的语句。using
示例(在 ):GlobalUsings.cs
global using System;
global using System.Collections.Generic;
这样就无需在每个文件中重复 common 指令。using
此功能允许命名空间应用于整个文件而无需块,从而减少缩进。
之前:
namespace MyNamespace
{
public class MyClass { }
}
现在:
namespace MyNamespace;
public class MyClass { }
Lambda 表达式已获得多项改进,包括返回类型推理、Lambda 上的属性以及 Lambda 的自然类型。
返回类型推理:C# 10 可以推断 lambda 表达式的返回类型,即使没有显式指定类型。
Func<int, int> square = x => x * x;
Lambda 属性:您现在可以将属性应用于 lambda 表达式。
[return: NotNull]
Func<string, string?> ensureNotNull = [NotNull] (string? input) => input ?? throw new ArgumentNullException(nameof(input));
现在可以在常量表达式中使用内插字符串,这在以前版本的 C# 中是不可能的。
const string firstName = "John";
const string lastName = "Doe";
const string fullName = $"{firstName} {lastName}"; // Now possible in C# 10.0
C# 引入了本机大小的整数 ( 和 ),它们是特定于平台的整数类型,适用于低级编程方案。nintnuint
nint nativeInt = 100;
nuint nativeUInt = 200U;
C# 引入了可为 null 的引用类型以提高 null 安全性。使用可为 null 的注释功能,如果可能取消引用 null 值,编译器会发出警告。
string? nullableString = null; // Warning if dereferenced without null check
if (nullableString != null)
{
Console.WriteLine(nullableString.Length); // Safe
}
C# 11 引入了原始字符串文本,这使得处理多行字符串和特殊字符变得更加容易。这允许您包含换行符并避免转义特殊字符,例如引号和反斜杠。
var json = """
{
"name": "John Doe",
"age": 30
}
""";
该关键字确保在对象创建期间初始化某些属性。当您想要确保某些字段在构造后不会保持未初始化状态,从而提高对象完整性时,这非常有用。required
public class Person
{
public required string FirstName { get; set; }
public required string LastName { get; set; }
}
var person = new Person { FirstName = "John", LastName = "Doe" }; // Valid
如果在对象初始化期间未为属性提供值,则会引发编译时错误。required
C# 11 扩展了对泛型数学的支持,从而支持跨不同数值类型进行更灵活和可重用的数学运算。
public T Add<T>(T a, T b) where T : INumber<T>
{
return a + b;
}
var sum = Add(10, 20); // Works with integers
var decimalSum = Add(10.5m, 20.3m); // Works with decimals
This makes it possible to perform math operations generically on numeric types (int, float, decimal, etc.) using the INumber<T> interface.
C# 11 中的列表模式允许您更有效地匹配数组和集合。您现在可以解构和匹配集合中的元素,从而提高模式匹配的灵活性。
int[] numbers = { 1, 2, 3, 4 };
if (numbers is [1, 2, .. var rest])
{
Console.WriteLine(string.Join(", ", rest)); // Output: 3, 4
}
该运算符可用于在 pattern matching 期间捕获列表中的剩余元素。..
C# 11 引入了 UTF-8 字符串文本,以直接处理 UTF-8 编码的字符串。这可以优化通常使用 UTF-8 的网络或文件 IO 等场景的性能。
ReadOnlySpan<byte> utf8Bytes = "Hello, World!"u8;
这使得在性能关键型应用程序中处理 UTF-8 编码的数据变得更加容易和高效。
在 C# 11 中,结构会自动初始化为其默认值,即使你没有显式初始化它们。这可以防止未初始化的结构字段,从而提高安全性。
struct Point
{
public int X;
public int Y;
}
Point p = default; // p.X and p.Y will be 0 without explicit initialization
C# 11 扩展了模式匹配以支持 和 .这对于对大型数组或内存片进行高效、低分配的操作非常有用。Span<T>ReadOnlySpan<T>
Span<int> numbers = stackalloc[] { 1, 2, 3, 4 };
if (numbers is [1, 2, .. var rest])
{
Console.WriteLine(string.Join(", ", rest.ToArray())); // Output: 3, 4
}
现在,您可以使用 to 更简洁地引用表达式中的参数,从而提高代码可读性。nameof
void PrintName(string firstName)
{
if (firstName is null)
{
throw new ArgumentNullException(nameof(firstName));
}
}
这消除了硬编码的参数名称并降低了出错的风险。
C# 11 允许在接口中使用静态抽象成员,这改进了对跨静态成员的多态行为的支持。
public interface IShape
{
static abstract double CalculateArea(double dimension);
}
public class Circle : IShape
{
public static double CalculateArea(double radius) => Math.PI \* radius \* radius;
}
double area = Circle.CalculateArea(5); // Area of the circle
这对于泛型数学等方案非常有用,并允许您在静态级别跨类型强制实施通用功能。
属性模式已扩展为允许在嵌套成员上进行匹配,并允许在模式中进行更复杂的表达式进行匹配。
if (person is { Address.City: "New York", Age: > 30 })
{
Console.WriteLine("Person lives in New York and is over 30 years old.");
}
此功能简化了嵌套数据的提取,并提高了复杂对象图的可读性。
C# 11 允许结构包含字段,从而支持更高级的方案,其中需要将对其他值的引用存储在结构中,而无需在堆上分配。ref
public struct RefStruct
{
private ref int _value;
public RefStruct(ref int value) => _value = ref value;
}
这样可以在性能关键型应用程序中实现更高效的内存管理,尤其是在处理大型数据集或内存密集型操作时。
在 C# 11 中,您可以定义文件局部类型,确保类型只能在定义的文件中访问。这通过限制类型的范围来改进封装。
file class FileLocalType
{
public string Name { get; set; }
}
当您想要定义仅在单个文件中相关且不应在项目中的其他位置访问的类型时,此功能非常有用。