C#中的9个“黑魔法”

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

执行随后暂停,OnCompleted完成后重新回到状态机;

有兴趣的可以访问Github具体规范说明:https://github.com/dotnet/csharplang/blob/master/spec/expressions.md

正常Task.Delay()是基于线程池计时器的,可以用如下“骚操作”,来实现一个单线程的TaskEx.Delay()

static Action Tick = null;

void Main()
{
 Start();
 while (true)
 {
  if (Tick != null) Tick();
  Thread.Sleep(1);
 }
}

async void Start()
{
 Console.WriteLine("执行开始");
 for (int i = 1; i <= 4; ++i)
 {
  Console.WriteLine($"第{i}次,时间:{DateTime.Now.ToString("HH:mm:ss")} - 线程号:{Thread.CurrentThread.ManagedThreadId}");
  await TaskEx.Delay(1000);
 }
 Console.WriteLine("执行完成");
}

class TaskEx
{
 public static MyDelay Delay(int ms) => new MyDelay(ms);
}

class MyDelay : INotifyCompletion
{
 private readonly double _start;
 private readonly int _ms;
 
 public MyDelay(int ms)
 {
  _start = Util.ElapsedTime.TotalMilliseconds;
  _ms = ms;
 }
 
 internal MyDelay GetAwaiter() => this;
 
 public void OnCompleted(Action continuation)
 {
  Tick += Check;
  
  void Check()
  {
   if (Util.ElapsedTime.TotalMilliseconds - _start > _ms)
   {
    continuation();
    Tick -= Check;
   }
  }
 }

 public void GetResult() {}
 
 public bool IsCompleted => false;
}

运行效果如下:

执行开始
第1次,时间:17:38:03 - 线程号:1
第2次,时间:17:38:04 - 线程号:1
第3次,时间:17:38:05 - 线程号:1
第4次,时间:17:38:06 - 线程号:1
执行完成

注意不需要非得使用TaskCompletionSource<T>才能创建定定义的async/await

3. 表达式树,与Expression<T>类型

是“黑魔法”,没有“操作空间”,只有当类型是Expression<T>时,才会创建为表达式树。

表达式树C# 3.0随着LINQ一起发布,是有远见的“黑魔法”。

如以下代码:

Expression<Func<int>> g3 = () => 3;

会被编译器翻译为:

Expression<Func<int>> g3 = Expression.Lambda<Func<int>>(
 Expression.Constant(3, typeof(int)), 
 Array.Empty<ParameterExpression>());

4. 插值字符串,与FormattableString类型

是“黑魔法”,没有“操作空间”。

插值字符串发布于C# 6.0,在此之前许多语言都提供了类似的功能。

只有当类型是FormattableString,才会产生不一样的编译结果,如以下代码:

FormattableString x1 = $"Hello {42}";
string x2 = $"Hello {42}";

编译器生成结果如下:

FormattableString x1 = FormattableStringFactory.Create("Hello {0}", 42);
string x2 = string.Format("Hello {0}", 42);