void S1Demo()
{
using S1 s1 = new S1();
}
ref struct S1
{
public void Dispose()
{
Console.WriteLine("正常释放");
}
}
同样的道理,如果用IAsyncDisposable接口:
async Task S2Demo()
{
await using S2 s2 = new S2();
}
struct S2 : IAsyncDisposable
{
public async ValueTask DisposeAsync()
{
await Task.Delay(1);
Console.WriteLine("Async释放");
}
}
8. T?,与Nullable<T>类型
是“黑魔法”,只有Nullable<T>才能接受T?,Nullable<T>作为一个值类型,它还能直接接受null值(正常值类型不允许接受null值)。
示例代码如下:
int? t1 = null; Nullable<int> t2 = null; int t3 = null; // Error CS0037: Cannot convert null to 'int' because it is a non-nullable value type
生成代码如下(int?与Nullable<int>完全一样,跳过了编译失败的代码):
IL_0000: nop IL_0001: ldloca.s 0 IL_0003: initobj valuetype [System.Runtime]System.Nullable`1<int32> IL_0009: ldloca.s 1 IL_000b: initobj valuetype [System.Runtime]System.Nullable`1<int32> IL_0011: ret
9. 任意类型的Index/Range泛型操作
有“黑魔法”,也有“鸭子类型”——存在操作空间。
Index/Range发布于C# 8.0,可以像Python那样方便地操作索引位置、取出对应值。以前需要调用Substring等复杂操作的,现在非常简单。
string url = "https://www.super-cool.com/product/7705a33a-4d2c-455d-a42c-c95e6ac8ee99/summary";
string productId = url[35..url.LastIndexOf("/")];
Console.WriteLine(productId);
生成代码如下:
string url = "https://www.super-cool.com/product/7705a33a-4d2c-455d-a42c-c95e6ac8ee99/amd-r7-3800x";
int num = 35;
int length = url.LastIndexOf("/") - num;
string productId = url.Substring(num, length);
Console.WriteLine(productId); // 7705a33a-4d2c-455d-a42c-c95e6ac8ee99
可见,C#编译器忽略了Index/Range,直接翻译为调用Substring了。
但数组又不同:
var range = new[] { 1, 2, 3, 4, 5 }[1..3];
Console.WriteLine(string.Join(", ", range)); // 2, 3
生成代码如下:
int[] range = RuntimeHelpers.GetSubArray<int>(new int[5]
{
1,
2,
3,
4,
5
}, new Range(1, 3));
Console.WriteLine(string.Join<int>(", ", range));
可见它确实创建了Range类型,然后调用了RuntimeHelpers.GetSubArray<int>,完全属于“黑魔法”。
但它同时也是“鸭子”类型,只要代码中实现了










