C〓〓の中の9つの“黒の魔法”

11499 ワード

私たちはC#が非常に先進的な言語であることを知っています。これらの「構文糖」はあまりにも使いやすく、C#コンパイラが死んだものだと思われてしまうことがあります。
これらの高度な言語機能は、コンパイラが書いたもの(「黒の魔法」)なのか、それとも拡大(騒动)できる「アヒルのタイプ」なのかを見てみましょう。
まずリストを作ってみます。このカタログを見て判断してみてもいいです。「黒の魔法」といいますか?それとも「アヒルのタイプ」ですか?
  • LINQ動作は、IEnumerable<T>タイプと;
  • async/awaitとTask/ValueTaskタイプです。
  • 式ツリー、Expression<T>タイプと;
  • 補間文字列とFormatable Stringタイプ。
  • yield returnとIEnumerable<T>タイプ;
  • foreachサイクル、IEnumerable<T>タイプと;
  • usingキーワードは、IDisposableとインターフェースする。
  • TNullableタイプと;
  • の任意のタイプのIndex/Range汎型動作。
  • 1.C#操作は、LINQタイプと
    「黒の魔法」ではなく、「アヒルのタイプ」です。IEnumerable<T>LINQによって発行された新しい機能であり、データを非常に便利に操作することができる。今はC# 3.0年が経ちました。機能はまだ強化されていますが、他の言語よりずっと便利です。
    前のブログで述べたように、12は必ずしもLINQに基づいていなくてもいいです。一つのタイプを定義し、必要なIEnumerable<T>表現を実現すればいいです。
    
    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.LINQLINQ/selectタイプと
    「黒の魔法」ではなく、「アヒルのタイプ」です。.Selectasync/awaitにリリースされ、非同期プログラムを容易に行うことができ、その本質は状態機械である。Taskの本質は、ValueTaskというタイプの次の名前のインターフェースを探すことであり、このインターフェースはasync/awaitまたはC# 5.0に継承されたクラスを返さなければならない。
  • は、先にt.GetAwaiter()の方法を呼び出し、待ち受けaを取得する。
  • ブールタイプbを取得するためにa.Is Complettedを呼び出します。
  • b=trueであれば、a.GetResult()を直ちに実行し、実行結果を取得する。
  • b=falseであれば、状況を見ます。
  • a ICritical NotifyComprationが実現されていない場合は、(a as INotifyCompration).OComplected(action)を実行します。
    もしaがICritical NotifyComprationを実現したら、(a as ICritical NotifyCompration).OComplected(action)を実行します。
    実行はその後一時停止し、OComplettedが完了したら再度状態マシンに戻ります。
    関心のある人は、async/awaitにアクセスすることができます。具体的な仕様説明:https://github.com/dotnet/csharplang/blob/master/spec/expressions.md
    通常のGetAwaiter()INotifyCompletionに基づくものであり、次のように「騒動作」を用いて、単一スレッドのICriticalNotifyCompletionを実現することができる。
    
    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-スレッド番号:1
    第2回、時間:17:38-スレッド番号:1
    第3回、時間:17:38:05-スレッド番号:1
    第4回、時間:17:38-スレッド番号:1
    実行完了
    注意定義されたGetResult()を作成するには、IsCompleteを使用しなければならない必要がない。
    3.表現ツリー、Githubタイプ
    は「黒魔術」であり、「操作空間」はなく、タイプがTask.Delay()である場合にのみ、表現ツリーとして作成されます。 TaskEx.Delay()であり、TaskCompletionSource<T>とともに発表されたもので、遠見のある「黒の魔法」である。
    以下のコードのように:
    
    Expression<Func<int>> g3 = () => 3;
    コンパイラで翻訳されます。
    
    Expression<Func<int>> g3 = Expression.Lambda<Func<int>>(
     Expression.Constant(3, typeof(int)), 
     Array.Empty<ParameterExpression>());
    4.補間文字列とasync/awaitタイプ
    「黒の魔法」です。「操作空間」はありません。Expression<T>Expression<T>に発行され、これまで多くの言語で同様の機能が提供されてきた。
    タイプが である場合にのみ、異なるコンパイル結果が発生します。
    
    FormattableString x1 = $"Hello {42}";
    string x2 = $"Hello {42}";
    コンパイラの生成結果は以下の通りです。
    
    FormattableString x1 = FormattableStringFactory.Create("Hello {0}", 42);
    string x2 = string.Format("Hello {0}", 42);
    その本質は、C# 3.0を呼び出してタイプを作成することである。
    5.LINQFormattableStringタイプと;
    「黒の魔法」ですが、補足説明があります。 は、C# 6.0のためのもの以外に、FormattableStringFormattableStringFactory.Createyield returnIEnumerable<T>のために使用されてもよい。
    したがって、yield returnを用いてIEnumerable<T>IEnumerableIEnumerator<T>の動作をシミュレートするなら、比較的簡単である。
    
    var seq = GetNumbers();
    seq.MoveNext();
    Console.WriteLine(seq.Current); // 0
    seq.MoveNext();
    Console.WriteLine(seq.Current); // 1
    seq.MoveNext();
    Console.WriteLine(seq.Current); // 2
    seq.MoveNext();
    Console.WriteLine(seq.Current); // 3
    seq.MoveNext();
    Console.WriteLine(seq.Current); // 4
    
    IEnumerator<int> GetNumbers()
    {
     for (var i = 0; i < 5; ++i)
      yield return i;
    }
    IEnumerator――「サンデー」はC#にリリースされます。
    6.C++サイクル、Javaタイプと
    アヒルのタイプです。操作空間があります。generator<T>は、必ずしも組み合わせて使用する必要はないyield returnタイプではなく、オブジェクトがC# 2.0方法がある限り、
    
    void Main()
    {
     foreach (var i in new F())
     {
      Console.Write(i + ", "); // 1, 2, 3, 4, 5, 
     }
    }
    
    class F
    {
     public IEnumerator<int> GetEnumerator()
     {
      for (var i = 0; i < 5; ++i)
      {
       yield return i;
      }
     }
    }
    また、foreachがオブジェクトによって実現された場合、IEnumerable<T>非同期ループも同様に使用することができる。
    
    async Task Main()
    {
     await foreach (var i in new F())
     {
      Console.Write(i + ", "); // 1, 2, 3, 4, 5, 
     }
    }
    
    class F
    {
     public async IAsyncEnumerator<int> GetAsyncEnumerator()
     {
      for (var i = 0; i < 5; ++i)
      {
       await Task.Delay(1);
       yield return i;
      }
     }
    }
    foreachIEnumerable<T>GetEnumerator()と一緒に発表されたものです。具体的には私が以前書いた「コードデモンストレーションC菗各バージョンの新機能」が見られます。
    7.GetAsyncEnumerator()キーワード、await foreachインターフェース
    はい、そうでもないです。await foreach及び通常のC# 8.0 キーワードを使用しており、usingインターフェースに基づく必要がある。IDisposable は別の物語です。 は勝手に移動することができないので、引用タイプ――托管ヒープはメモリの移動を許可します。usingIDisposableとの関係を許可しません。これはref structを継承することを含みます。IAsyncDisposableref structです。
    しかし、資源を解放するための需要は依然としてあります。どうすればいいですか?「アヒルのタイプ」が来ました。手書きでref structの方法を書くことができます。どのインターフェースを受け継ぐ必要がありません。
    
    void S1Demo()
    {
     using S1 s1 = new S1();
    }
    
    ref struct S1
    {
     public void Dispose()
     {
      Console.WriteLine("    ");
     }
    }
    同じ道理で、もし インターフェースを使うならば:
    
    async Task S2Demo()
    {
     await using S2 s2 = new S2();
    }
    
    struct S2 : IAsyncDisposable
    {
     public async ValueTask DisposeAsync()
     {
      await Task.Delay(1);
      Console.WriteLine("Async  ");
     }
    }
    8. タイプと
    は「黒の魔法」であり、 だけがDispose()を受け入れることができ、IAsyncDisposableT?の値を直接受け取ることができます。
    サンプルコードは以下の通りです。
    
    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
    生成コードは以下の通りである(Nullable<T>Nullable<T>と全く同じで、コンパイルに失敗したコードをスキップした)。
    
    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.任意のタイプのT?汎用動作
    「黒の魔法」と「アヒルのタイプ」があります。操作空間があります。Nullable<T>は、 に発行されており、インデックス位置をnullのように操作しやすく、対応する値を取り出すことができる。以前は などの複雑な操作が必要でしたが、今はとても簡単です。
    
    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
    これにより、nullコンパイラはint?を無視し、呼び出しNullable<int>に直接翻訳した。
    しかし、配列はまた違っています。
    
    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));
    これは確かにIndex/Rangeタイプを作成し、Index/Rangeを呼び出して、完全に「黒の魔法」に属していることが分かります。
    しかし、同時に「アヒル」タイプでもあります。コードにC# 8.0属性とPython方法が実装されている限り、Substringを呼び出すことができます。
    
    var range2 = new F()[2..];
    Console.WriteLine(range2); // 2 -> -2
    
    class F
    {
     public int Length { get; set; }
     public IEnumerable<int> Slice(int start, int end)
     {
      yield return start;
      yield return end;
     }
    }
    発生コードは以下の通りです。
    
    F f = new F();
    int length2 = f.Length;
    length = 2;
    num = length2 - length;
    string range2 = f.Slice(length, num);
    Console.WriteLine(range2);
    締め括りをつける
    上記のように、C#の「黒の魔法」は確かに多いですが、「アヒルのタイプ」もたくさんあります。「騒动」の「操作空間」はとても大きいです。Index/Rangeはアヒルのタイプの元祖であるSubstringを追加すると伝えられています。その時、「操作空間」はきっと今よりもっと大きくて、とても楽しみにしています。
    ここでC〓〓の中の9つの「黒い魔法」についての文章を紹介します。これまでの文章を検索したり、下記の関連記事を見たりしてください。これからもよろしくお願いします。