C#中的9个“黑魔法”

2020-04-01 12:00:03于丽

我们知道C#是非常先进的语言,因为是它很有远见的“语法糖”。这些“语法糖”有时过于好用,导致有人觉得它是C#编译器写死的东西,没有道理可讲的——有点像“黑魔法”。

那么我们可以看看C#这些高级语言功能,是编译器写死的东西(“黑魔法”),还是可以扩展(骚操作)的“鸭子类型”。

我先列一个目录,大家可以对着这个目录试着下判断,说说是“黑魔法”(编译器写死),还是“鸭子类型”(可以自定义“骚操作”):

    LINQ操作,与IEnumerable<T>类型; async/await,与Task/ValueTask类型; 表达式树,与Expression<T>类型; 插值字符串,与FormattableString类型; yield return,与IEnumerable<T>类型; foreach循环,与IEnumerable<T>类型; using关键字,与IDisposable接口; T?,与Nullable<T>类型; 任意类型的Index/Range泛型操作。

1. LINQ操作,与IEnumerable<T>类型

不是“黑魔法”,是“鸭子类型”。

LINQC# 3.0发布的新功能,可以非常便利地操作数据。现在12年过去了,虽然有些功能有待增强,但相比其它语言还是方便许多。

如我上一篇博客提到,LINQ不一定要基于IEnumerable<T>,只需定定义一个类型,实现所需要的LINQ表达式即可,LINQselect关键字,会调用.Select方法,可以用如下的“骚操作”,实现“移花接木”的效果:

void Main()
{
 var query = 
  from i in new F()
  select 3;
  
 Console.WriteLine(string.Join(",", query)); // 0,1,2,3,4
}

class F
{
 public IEnumerable<int> Select<R>(Func<int, R> t)
 {
  for (var i = 0; i < 5; ++i)
  {
   yield return i;
  }
 }
}

2. async/await,与Task/ValueTask类型

不是“黑魔法”,是“鸭子类型”。

async/await发布于C# 5.0,可以非常便利地做异步编程,其本质是状态机。

async/await的本质是会寻找类型下一个名字叫GetAwaiter()的接口,该接口必须返回一个继承于INotifyCompletionICriticalNotifyCompletion的类,该类还需要实现GetResult()方法和IsComplete属性。

先调用t.GetAwaiter()方法,取得等待器a; 调用a.IsCompleted取得布尔类型b; 如果b=true,则立即执行a.GetResult(),取得运行结果; 如果b=false,则看情况:

如果a没实现ICriticalNotifyCompletion,则执行(a as INotifyCompletion).OnCompleted(action)
如果a实现了ICriticalNotifyCompletion,则执行(a as ICriticalNotifyCompletion).OnCompleted(action)