<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C# 很少人知道的科技

          共 8930字,需瀏覽 18分鐘

           ·

          2020-11-09 03:34


          本文來告訴大家在C#很少有人會發(fā)現(xiàn)的科技。即使是工作了好多年的老司機也不一定會知道這些科技,如果覺得我是在騙你,那么請看看本文的內容

          原本最初 C# 的設計是簡單和高效開發(fā)的,在經過了這么多年眾多公司和開發(fā)者的努力下,整個 C# 里面包含了大量有趣的功能。其中一部分功能是針對于某些特殊需求設計的,例如高性能或高并發(fā)或無內存回收等。在經過了 10 多年的迭代,很少人能完全了解整個 C# 語言和框架級做了哪些有趣的功能

          我在網上找了很多大神的博客,然后和很多大神聊天,知道了一些科技,于是就在本文和大家分享一下。如果大家有了解本博客里面沒有收藏的科技,還請告訴我

          現(xiàn)在整個 C# 從編譯器到運行時都是開源的,所有權在 dotnet 基金會上,全部開源的項目都基于最友好的 MIT 協(xié)議和 Apache 2 開源協(xié)議,文檔協(xié)議遵循CC-BY協(xié)議。這將允許任何人任何組織和企業(yè)任意處置,包括使用,復制,修改,合并,發(fā)表,分發(fā),再授權,或者銷售。唯一的限制是,軟件中必須包含上述版 權和許可提示,后者協(xié)議將會除了為用戶提供版權許可之外,還有專利許可,并且授權是免費,無排他性的(任何個人和企業(yè)都能獲得授權)并且永久不可撤銷,用戶使用.NET 和 C# 完全不用擔心收費問題和版權問題,以及后續(xù)無法維護問題。而 dotnet 基金會是一個開放的平臺,我也是 dotnet 基金會的成員之一。微軟在 2020 的時候依然是 dotnet 基金會最大的支持組織

          現(xiàn)在最火的 dotnet 倉庫是?dotnet csharplang?倉庫,當前的 C# 語言特性由整個社區(qū)決定,這是一個官方開放用來討論 C# 語言未來的倉庫,天天都有大佬們在討論語言的特性,歡迎大家加入

          接下來讓我告訴大家一些很少有人會發(fā)現(xiàn)的科技

          無限級判斷空

          在 C# 6.0 可以使用??判斷空,那么就可以使用下面代碼

                      var v1 = "123";
          string v2 = null;
          string v3 = null;

          var v = v1 ?? v2 ?? v3;

          實際上可以無限的使用??判斷前面一個函數(shù)為空,那么問題來了,下面的代碼輸出的是多少?

          var n = 2 + foo?.N ?? 1;

          上面代碼的 foo 就是空的,那么 n 是多少?是 1 還是 2 還是 3 還是空?

          想要了解這道題的推導過程請看C# 高級面試題?里面寫了很多老司機都不一定能解出

          使用 using 關鍵詞省略長的定義

          例如有下面這個代碼,在這個代碼里面使用了很多的 List 嵌套,如下面代碼所示里面有很多定義的代碼

          var foo = new System.Collections.Generic.Dictionary<System.Collections.Generic.List<System.Collections.Generic.List<string>>, string>();

          可以看到上面代碼中,有大量的代碼都是用來作為類型的定義,假設這個值作為某個方法的參數(shù),那才是可怕

          一個簡單的方法是使用 using 關鍵詞,如在文件的開頭添加如下代碼

          using HvcnrclHnlfk = System.Collections.Generic.Dictionary<System.Collections.Generic.List<System.Collections.Generic.List<string>>,string>;

          在添加了上面代碼之后,在這個文件里的所有用到如上面很長的定義的代碼都可以使用?using?后面的值可以代替,如本文上面使用了?HvcnrclHnlfk?這個詞,來看看替換之后的代碼長度

          var foo = new HvcnrclHnlfk();

          辣么大

          實際上寫到這里我有些不好意思,好像剛剛說的都是大家都知道的,那么我就要開始寫大家很少知道的科技

          等等,什么是?辣么大?大哇?其實這是 lambda 表達式的翻譯

          請看看下面這段有趣的代碼

                      Func<string,string, EventHandler> foo = (x, y) => (s, e) =>
          {
          var button = (Button) s;
          button.Left = x;
          button.Top = y;
          };

          Button1.Click += foo(0, -1);

          上面的代碼通過一個 lambda 表達式返回一個另一個 lambda 表達式,或者說用一個委托返回另一個委托。這是一個特別有趣的寫法,通過函數(shù)返回函數(shù)的思想可以用來寫出一些有趣的邏輯,特別是在多層嵌套的時候

          當然使用委托可是會出現(xiàn)另一個問題的,請問下面的代碼實際調用的是哪個委托,下面代碼的 a 和 b 和 c 都是?Action委托,同時都不是空的

          ((a + b + c) - (a + c))();

          在數(shù)學上,其實函數(shù)也可以視為變量,很有科技范的 C# 當然也支持如此的功能,將函數(shù)包裝為委托的時候,可以讓委托本身支持加減法哦,只是這個加減法的規(guī)則有些詭異。不信,請猜猜上面代碼執(zhí)行了什么函數(shù)

          沖突的類型

          在遇到某些類型,特別是放在 NuGet 上的多個不同的庫里面的類型,這些類型有相同的類名,如 Data 或 Control 等很通用的命名的時候,在代碼中如果需要同時使用這兩個類,就需要補全整個命名空間,如下面代碼

          var webControl = new System.Web.UI.WebControls.Control();
          var formControl = new System.Windows.Forms.Control();

          如果經常使用這兩個控件,那么就需要寫很多補全命名空間的代碼,代碼很多。好在微軟的大佬們給出了一個坑方法,使用這個方法可以不寫命名空間,或者說只需要在文件開始 using 一次,請看代碼

          using web = System.Web.UI.WebControls;
          using win = System.Windows.Forms;

          web::Control webControl = new web::Control();
          win::Control formControl = new win::Control();

          參見:https://stackoverflow.com/a/9099/6116637

          extern alias

          如果使用了兩個不同的程序集放在兩個不同的 dll 文件里面,這兩個程序集都有相同命名空間和類型,那么如何使用指定的庫

          如下面代碼所示,在兩個 dll 里面都定義了?F.Foo?類型

          //a.dll

          namespace F
          {
          public class Foo
          {

          }
          }

          //b.dll

          namespace F
          {
          public class Foo
          {

          }
          }

          這時就可以使用 extern alias 關鍵詞

          參見:C#用extern alias解決兩個assembly中相同的類型全名 - fresky - 博客園

          字符串

          大家看到了 C# 6.0 的$,是不是可以和@一起?

                      var str = "kktpqfThiq";
          string foo = $@"換行{str}";

          注意兩個的順序,反過來直接告訴你代碼不能這樣寫

          此知識點不再適用,因為在 C# 8.0 的時候,可以按照任意的順序使用?$?和?@?標記。詳細請看?$ - 字符串內插 - C# 參考?特別感謝 592844340 群內熱心人員勘誤

          特殊關鍵字

          實際上有下面幾個關鍵字是沒有詳細的文檔,可能只有微軟的編譯器才知道

          __makeref

          __reftype

          __refvalue

          __arglist

          不過在 C# 7.2 可以使用其他的關鍵字做到一些功能,詳細請看我的 C# 7.0 博客

          使用 Unions (C++ 一樣的)

          如果看到 C++ 可以使用內聯(lián),不要說 C# 沒有這個功能,實際上也可以使用 FieldOffset 特性實現(xiàn)和 C++ 一樣的內聯(lián)的功能 ,請看下面代碼

          [StructLayout(LayoutKind.Explicit)]
          public class A
          {
          [FieldOffset(0)]
          public byte One;

          [FieldOffset(1)]
          public byte Two;

          [FieldOffset(2)]
          public byte Three;

          [FieldOffset(3)]
          public byte Four;

          [FieldOffset(0)]
          public int Int32;
          }

          如下面代碼就定義了int變量,修改這個變量就是修改其他的三個變量

               static void Main(string[] args)
          {
          A a = new A { Int32 = int.MaxValue };

          Console.WriteLine(a.Int32);
          Console.WriteLine("{0:X} {1:X} {2:X} {3:X}", a.One, a.Two, a.Three, a.Four);

          a.Four = 0;
          a.Three = 0;
          Console.WriteLine(a.Int32);
          }

          運行代碼可以看到輸出如下

          2147483647
          FF FF FF 7F
          65535

          可以看到修改其中某個值都會相互影響,這幾個值共用了相同的一個內存空間

          接口默認方法

          實際上可以給接口使用默認方法,使用的方式如下

          public static void Foo(this IF1 foo)
          {
          //實際上大家也看到是如何定義
          }

          當然了,在 C# 8.0 還有更直接的方法,詳細請看?在 C# 中使用默認接口方法安全地更新接口

          stackalloc

          很多人都不知道這個科技,這是不安全代碼,從棧申請空間

          int* block = stackalloc int[100]; 

          使用的時候需要小心你的棧也許會炸掉

          參見:stackalloc

          指定編譯

          這個是一個有趣的特性實現(xiàn)的功能,是一個編譯器技術,寫給編譯器看的特性。使用 Conditional 特性可以讓代碼在指定條件不使用,如下面的代碼,規(guī)定了只有在 DEBUG 宏定義的時候才讓 F2 方法生效。因此在 Release 下就不會使用 F2 方法了

              public sealed clas Foo
          {
          public Foo F1()
          {
          Console.WriteLine("進入F1");
          return this;
          }

          [Conditional("DEBUG")]
          public void F2()
          {
          Console.WriteLine("F2");
          }
          }

          簡單讓代碼跑一下

                  static void Main(string[] args)
          {
          var foo = new Foo();
          foo.F1();
          foo.F2();
          }

          結果是什么,大家也知道,在 Debug 和 Release 輸出是不相同。但是這么簡單的怎么會在這里說呢,請大家看看這個代碼輸出什么

               static void Main(string[] args)
          {
          var foo = new Foo();
          foo.F1().F2();
          }

          實際上在 Release 下什么都不會輸出,此時的 F1 不會被執(zhí)行

          true 判斷

          下面寫個見鬼的代碼

                      var foo = new Foo(10);

          if (foo)
          {
          Console.WriteLine("我的類沒有繼承 bool ,居然可以這樣寫");
          }

          沒錯 Foo 沒有繼承 bool 居然可以這樣寫

          實際上就是重寫 true 方法,請看代碼

              public class Foo
          {
          public Foo(int value)
          {
          _count = value;
          }

          private readonly int _count;

          public static bool operator true(Foo mt)
          {
          return mt._count > 0;
          }

          public static bool operator false(Foo mt)
          {
          return mt._count < 0;
          }
          }

          是不是覺得很多有人這樣寫,下面讓大家看一個很少人會知道的科技,感謝walterlv?提供

          重寫運算返回

          很少人知道實際上重寫?==?可以返回任意的類型,而不是只有 bool 類型,請看下面代碼

          是可以編譯通過的,因為我重寫運算

             class Foo
          {
          public int Count { get; set; }

          public static string operator ==(Foo f1, Foo f2)
          {
          if (f1?.Count == f2?.Count)
          {
          return "lindexi";
          }

          return "";
          }

          public static string operator !=(Foo f1, Foo f2)
          {
          return "";
          }
          }

          可以重寫的運算很多,返回值可以自己隨意定義

          await 任何類型

          等待任意的類型,包括已定義的基礎類型,如下面代碼

          await "林德熙逗比";

          await "不告訴你";

          這個代碼是可以編譯通過的,但是只有在我的設備。在看了這個博客之后,可能你也可以在你的設備編譯

          其實 await 是可以寫很多次的,如下面代碼

          await await await await await await await await await await await await await await await await await await await await await await await "林德熙逗比";

          變量名使用中文

          實際上在C#支持所有 Unicode 字符,這是編譯器支持的,所以變量名使用中文也是可以的,而且可以使用特殊的字符

                  public string H\u00e5rf?ner()
          {
          return "可以編譯";
          }

          if this == null

          一般看到下面的代碼都覺得是不可能進入輸出的

          if (this == null) Console.WriteLine("this is null");

          如果在 if 里面都能使用 this == null 成立,那么一定是vs炸了。實際上這個代碼還是可以運行的

          在一般的函數(shù),如下面的 Foo 函數(shù),在調用就需要使用f.Foo()的方法,方法里 this 就是 f 這個對象,如果?f == null那么在調用方法就直接不讓運行,如何到方法里的判斷

          f.Foo(); //如果 f 為空,那么這里就不執(zhí)行

          void Foo()
          {
          // 如果 this 為空,怎么可以調用這個方法
          if (this == null) Console.WriteLine("this is null");
          }

          實際上是可以做的,請看(C#)if (this == null)?你在逗我,this 怎么可能為 null!用 IL 編譯和反編譯看穿一切 - walterlv 這篇博客

          如上面博客,關鍵在修改?callvirt?為?call?調用,直接修改 IL 可以做出很多特殊的寫法

          那么這個可以用在哪里?可以用在防止大神反編譯,如需要使用下面邏輯

          //執(zhí)行的代碼

          //不執(zhí)行的代碼

          此時簡單的反編譯也許會這么寫

          if(true)
          {
          //執(zhí)行的代碼
          }
          else
          {
          //不執(zhí)行的代碼
          }

          但是直接寫 true 很容易讓反編譯看到不使用代碼,而且在優(yōu)化代碼會被去掉,所以可以使用下面代碼

          if(this == null)
          {
          //執(zhí)行的代碼
          }
          else
          {
          //不執(zhí)行的代碼
          }

          實際在微軟代碼也是這樣寫,點擊string的實現(xiàn)源代碼可以看到微軟代碼

          重載的運算符

          實際上我可以將 null 強轉某個類,創(chuàng)建一個新的對象,請看代碼

          Fantastic fantastic = (FantasticInfo) null;
          fantastic.Foo();

          這里的 FantasticInfo 和 Fantastic 沒有任何繼承關系,而且調用 Foo 不會出現(xiàn)空引用,也就是 fantastic 是從一個空的對象創(chuàng)建出來的

          是不是覺得上面的科技很黑,實際原理沒有任何黑的科技,請看代碼

              public class Fantastic
          {
          private Fantastic()
          {
          }

          public static implicit operator Fantastic(FantasticInfo value) => new Fantastic();

          public void Foo()
          {
          }
          }

          public class FantasticInfo
          {
          }

          通過這個方式可以讓開發(fā)者無法直接創(chuàng)建 Fantastic 類,而且在不知道 FantasticInfo 的情況無法創(chuàng)建 Fantastic 也就是讓大家需要了解 FantasticInfo 才可以通過上面的方法創(chuàng)建,具體請看只有你能 new 出來!.NET 隱藏構造函數(shù)的 n 種方法(Builder Pattern / 構造器模式) - walterlv

          課件鏈接:https://r302.cc/J4gxOX

          當然還有新的?C# 7.0?和?C# 8.0?的新的語法

          例如下面的內部方法返回自身

          方法返回自身可以接近無限調用

          有一天我看到了下面的代碼,你猜小伙伴用什么代碼定義了 Foo 這個代碼?

          Foo()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()()();

          其實只需要定義一個委托,用內部方法實現(xiàn)委托,因為內部方法是可以返回自身,于是就可以使用5行代碼寫出 Foo 的定義

                  delegate Foo Foo(); // 定義委托

          static void Main(string[] args)
          {
          Foo Foo() // 定義內部方法
          {
          return Foo;
          }
          }

          不過括號還不可以無限使用,因為編譯器有一個表達式的長度限制

          無限長度的委托調用

          試試這個代碼,也許你可以無限寫下去,只要 Roslyn 不會炸就可以

           delegate Fx Fx(Fx fx);
          Fx fx = fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx => fx =>
          fx => fx => fx => fx => fx => fx => fx => fx => fx => fx;

          以下部分準確來說是 .NET 提供的功能,請問 C# 和 .NET 是什么關系?其實我也無法用一兩句話說清,扔掉了 .NET 依然可以用 C# 寫程序,反過來扔掉 C# 也依然能用 .NET 寫程序

          表達式樹獲取函數(shù)命名

          定義一個類,下面通過表達式樹從類獲得函數(shù)命名

              class Foo
          {
          public void KzcSevfio()
          {
          }
          }
                 static void Main(string[] args)
          {
          GetMethodName<Foo>(foo => foo.KzcSevfio());
          }

          private static void GetMethodName<T>(Expression<Action<T>> action) where T : class
          {
          if (action.Body is MethodCallExpression expression)
          {
          Console.WriteLine(expression.Method.Name);
          }
          }

          這樣就可以拿到函數(shù)的命名

          DebuggerDisplay

          如果想要在調試的時候,鼠標移動到變量顯示他的信息,可以重寫類的 ToString

              public sealed class Foo
          {
          public int Count { get; set; }

          public override string ToString()
          {
          return Count.ToString();
          }
          }

          但是如果 ToString 被其他地方用了,如何顯示?

          微軟告訴大家,使用 DebuggerDisplay 特性

              [DebuggerDisplay("{DebuggerDisplay}")]
          public sealed class Foo
          {
          public int Count { get; set; }

          private string DebuggerDisplay => $"(count {Count})";
          }

          他可以使用私有的屬性、字段,使用方法很簡單

          參見Using the DebuggerDisplay Attribute

          數(shù)字格式

          string format = "000;-#;(0)";

          string pos = 1.ToString(format); // 001
          string neg = (-1).ToString(format); // -1
          string zer = 0.ToString(format); // (0)

          參見:自定義數(shù)字格式字符串

          調用堆棧

          如果需要獲得調用方法的堆棧,可以使用這個文章的方法

            class Program
          {
          static void Main(string[] args)
          {
          var foo = new Foo();
          foo.F1();
          }
          }

          public sealed class Foo
          {
          public void F1()
          {
          F2();
          }

          void F2()
          {
          var stackTrace = new StackTrace();
          var n = stackTrace.FrameCount;
          for (int i = 0; i < n; i++)
          {
          Console.WriteLine(stackTrace.GetFrame(i).GetMethod().Name);
          }
          }
          }

          輸出

          F2
          F1

          參見:WPF 判斷調用方法堆棧

          歡迎加入 dotnet 職業(yè)技術學院?https://t.me/dotnet_campus?使用 Telegram 方法請看?如何使用 Telegram

          特別感謝

          特別感謝?呂毅 - walterlv?提供的逗比代碼

          特別感謝隊長提供的?.NET Core也是國產化信息系統(tǒng)開發(fā)的重要選項 - 張善友 - 博客園?博客。本文開頭為了更準確的描述,于是抄了隊長的博客內容


          往期精彩回顧




          【推薦】.NET Core開發(fā)實戰(zhàn)視頻課程?★★★

          .NET Core實戰(zhàn)項目之CMS 第一章 入門篇-開篇及總體規(guī)劃

          【.NET Core微服務實戰(zhàn)-統(tǒng)一身份認證】開篇及目錄索引

          Redis基本使用及百億數(shù)據量中的使用技巧分享(附視頻地址及觀看指南)

          .NET Core中的一個接口多種實現(xiàn)的依賴注入與動態(tài)選擇看這篇就夠了

          10個小技巧助您寫出高性能的ASP.NET Core代碼

          用abp vNext快速開發(fā)Quartz.NET定時任務管理界面

          在ASP.NET Core中創(chuàng)建基于Quartz.NET托管服務輕松實現(xiàn)作業(yè)調度

          現(xiàn)身說法:實際業(yè)務出發(fā)分析百億數(shù)據量下的多表查詢優(yōu)化

          關于C#異步編程你應該了解的幾點建議

          C#異步編程看這篇就夠了


          瀏覽 58
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  在线免费黄 | 尤物视频最新网址 | 出租屋女农民工毛片 | 美女网站18 | 亚洲人妻天堂 |