<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      .NET NativeAOT 指南

      .NET NativeAOT 指南

      隨著 .NET 8 的發布,一種新的“時尚”應用模型 NativeAOT 開始在各種真實世界的應用中廣泛使用。

      除了對 NativeAOT 工具鏈的基本使用外,“NativeAOT”一詞還帶有原生世界的所有限制,因此您必須知道如何處理這些問題才能正確使用它。

      在這篇博客中,我將討論它們。

      基本用法

      使用 NativeAOT 非常簡單,只需要在發布應用時使用 MSBuild 傳遞一個屬性 PublishAot=true 即可。

      通常,它可以是:

      dotnet publish -c Release -r win-x64 /p:PublishAot=true
      

      其中 win-x64 是運行時標識符,可以替換為 linux-x64osx-arm64 或其他平臺。您必須指定它,因為 NativeAOT 需要為您指定的運行時標識符生成原生代碼。

      然后發布的應用可以在 bin/Release/<target framework>/<runtime identifier>/publish 中找到

      關于編譯

      在討論使用 NativeAOT 時可能遇到的各種問題的解決方案之前,我們需要稍微深入一點,看看 NativeAOT 是如何編譯代碼的。

      我們經常聽說 NativeAOT 會剪裁掉沒有被使用的代碼。而實際上,它并不像 IL 剪裁那樣從程序集中剪裁掉不必要的代碼,而是只編譯代碼中引用的東西。

      NativeAOT 編譯包括兩個階段:

      1. 掃描 IL 代碼,構建整個程序視圖(一個依賴圖),其中包含所有需要編譯的必要依賴節點。
      2. 對依賴圖中的每個方法進行實際的編譯,生成代碼。

      請注意,在編譯過程中可能會出現一些“延遲”的依賴,因此上述兩個階段可能會交錯出現。

      這意味著,在分析過程中沒有被計算為依賴的任何東西最終都不會被編譯。

      反射

      依賴圖是在編譯期間靜態構建的,這也意味著任何無法靜態分析的東西都不會被編譯。不幸的是,反射,即在不事先告訴編譯器的情況下在運行時獲取東西,正是編譯器無法弄清楚的一件事。

      NativeAOT 編譯器有一些能力可以根據編譯時的字面量來推斷出反射調用需要什么東西。

      例如:

      var type = Type.GetType("Foo");
      Activator.CreateInstance(type);
      
      class Foo
      {
          public Foo() => Console.WriteLine("Foo instantiated");
      }
      

      上面的反射目標(即 Foo)可以被編譯器弄清楚,因為編譯器可以看到你試圖獲取類型 Foo,所以類型 Foo 會被標記為一個依賴,這導致 Foo 被編譯到最終的產物中。

      如果你運行這個程序,它會如預期地打印 Foo instantiated

      但是如果我們將代碼改為如下:

      var type = Type.GetType(Console.ReadLine());
      Activator.CreateInstance(type);
      
      class Foo
      {
          public Foo() => Console.WriteLine("Foo instantiated");
      }
      

      現在讓我們用 NativeAOT 構建并運行這個程序,然后輸入 Foo 來創建一個 Foo 的實例。你會立刻得到一個異常:

      Unhandled Exception: System.ArgumentNullException: Value cannot be null. (Parameter 'type')
         at System.ArgumentNullException.Throw(String) + 0x2b
         at System.ActivatorImplementation.CreateInstance(Type, Boolean) + 0xe7
         ...
      

      這是因為編譯器無法看到你在哪里使用了 Foo,所以它根本不會為 Foo 生成任何代碼,導致這里的 typenull

      此外,依賴分析是精確到單個方法的,這意味著即使一個類型被認為是一個依賴,如果該類型中的某個方法沒有被使用,該方法也不會被包含在代碼生成中。

      雖然這可以通過將所有類型和方法添加到依賴圖中來解決,這樣編譯器就會為它們生成代碼。這就是 TrimmerRootAssembly 的作用:通過提供 TrimmerRootAssembly,NativeAOT 編譯器會將你指定的程序集中的所有東西都作為根。

      但是涉及泛型的情況就不是這樣了。

      動態泛型實例化

      在 .NET 中,我們有泛型,編譯器會為每個非共享的泛型類型和方法生成不同的代碼。

      假設我們有一個類型 Point<T>

      struct Point<T>
      {
          public T X, Y;
      }
      

      如果我們有一段代碼試圖使用 Point<int>,編譯器會為 Point<int> 生成專門的代碼,使得 Point.XPoint.Y 都是 int。如果我們有一個 Point<float>,編譯器會生成另一個專門的代碼,使得 Point.XPoint.Y 都是 float

      通常情況下,這不會導致任何問題,因為編譯器可以靜態地找出你在代碼中使用的所有實例化,直到你試圖使用反射來構造一個泛型類型或一個泛型方法:

      var type = Type.GetType(Console.ReadLine());
      var pointType = typeof(Point<>).MakeGenericType(type);
      

      上面的代碼在 NativeAOT 下不會工作,因為編譯器無法推斷出 Point<T> 的實例化,所以編譯器既不會生成 Point<int> 的代碼,也不會生成 Point<float> 的代碼。

      盡管編譯器可以為 intfloat,甚至泛型類型定義 Point<> 生成代碼,但是如果編譯器沒有生成 Point<int> 的實例化代碼,你就無法使用 Point<int>

      即使你使用 TrimmerRootAssembly 來告訴編譯器將你的程序集中的所有東西都作為根,也仍然不會為像 Point<int>Point<float> 這樣的實例化生成代碼,因為它們需要根據類型參數來單獨構造。

      解決方案

      既然我們已經找出了在 NativeAOT 下可能發生的潛在問題,讓我們來談談解決方案。

      在其他地方使用它

      最簡單的想法是,我們可以通過在代碼中使用它來讓編譯器知道我們需要什么。

      例如,對于代碼

      var type = Type.GetType(Console.ReadLine());
      var pointType = typeof(Point<>).MakeGenericType(type);
      

      只要我們知道我們要使用 Point<int>Point<float>,我們可以在其他地方使用它一次,然后編譯器就會為它們生成代碼:

      // 我們使用一個永遠為假的條件來確保代碼不會被執行
      // 因為我們只想讓編譯器知道依賴關系
      // 注意,如果我們在這里簡單地使用一個 `if (false)`
      // 這個分支會被編譯器完全移除,因為它是多余的
      // 所以,讓我們在這里使用一個不平凡但不可能的條件
      if (DateTime.Now.Year < 0)
      {
          var list = new List<Type>();
          list.Add(typeof(Point<int>));
          list.Add(typeof(Point<float>));
      }
      

      DynamicDependency

      我們有一個屬性 DynamicDependencyAttribute 來告訴編譯器一個方法依賴于另一個類型或方法。

      所以我們可以利用它來告訴編譯器:“如果 A 被包含在依賴圖中,那么也添加 B”。

      下面是一個例子:

      class Foo
      {
          readonly Type t = typeof(Bar);
      
          [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(Bar))]
          public void A()
          {
              foreach (var prop in t.GetProperties())
              {
                  Console.WriteLine(prop);
              }
          }
      }
      
      class Bar
      {
          public int X { get; set; }
          public int Y { get; set; }
      }
      

      現在只要編譯器發現有任何代碼路徑調用了 Foo.ABar 中的所有公共屬性都會被添加到依賴圖中,這樣我們就能夠對 Bar 的每個公共屬性進行動態反射調用。

      這個屬性還有許多重載,可以接受不同的參數來適應不同的用例,您可以在這里查看文檔。

      此外,現在我們知道 Foo.A 中的動態反射在剪裁和 NativeAOT 下不會造成任何問題,我們可以使用 UnconditionalSuppressMessage 來抑制警告信息,這樣在構建過程中就不會再產生任何警告了。

      class Foo
      {
          readonly Type t = typeof(Bar);
      
          [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(Bar))]
          [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2080",
              Justification = "The properties of Bar have been preserved by DynamicDependency.")]
          public void A()
          {
              foreach (var prop in t.GetProperties())
              {
                  Console.WriteLine(prop);
              }
          }
      }
      

      DynamicallyAccessedMembers

      有時我們試圖動態地訪問類型 T 的成員,其中 T 可以是一個類型參數或一個 Type 的實例:

      void Foo<T>()
      {
          foreach (var prop in typeof(T).GetProperties())
          {
              Console.WriteLine(prop);
          }
      }
      
      class Bar
      {
          public int X { get; set; }
          public int Y { get; set; }
      }
      

      如果我們調用 Foo<Bar>,很不幸,這在 NativeAOT 下不會工作。編譯器確實看到你是用類型參數 Bar 調用 Foo 的,但在 Foo<T> 的上下文中,編譯器不知道 T 是什么,而且沒有其他代碼直接使用 Bar 的屬性,所以編譯器不會為 Bar 的屬性生成代碼。

      這里我們可以使用 DynamicallyAccessedMembers 來告訴編譯器為 T 的所有公共屬性生成代碼:

      void Foo<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>()
      {
          // ...
      }
      

      現在當編譯器編譯調用 Foo<Bar> 時,它知道 T(特別的,這里指 Bar)的所有公共屬性都應該被視為依賴。

      這個屬性也可以應用在一個 Type 上:

      Foo(typeof(Bar));
      
      void Foo([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type t)
      {
          foreach (var prop in t.GetProperties())
          {
              Console.WriteLine(prop);
          }
      }
      

      甚至在一個 string 上:

      Foo("Bar");
      
      void Foo([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] string s)
      {
          foreach (var prop in Type.GetType(s).GetProperties())
          {
              Console.WriteLine(prop);
          }
      }
      

      所以在這里你可能會發現我們有一個替代方案,用于我們在 DynamicDependency 一節中提到的代碼示例:

      class Foo
      {
          [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
          readonly Type t = typeof(Bar);
      
          public void A()
          {
              foreach (var prop in t.GetProperties())
              {
                  Console.WriteLine(prop);
              }
          }
      }
      

      順便說一句,這也是推薦的方法。

      TrimmerRootAssembly

      如果你不擁有代碼,但你仍然希望代碼在 NativeAOT 下工作。你可以嘗試使用 TrimmerRootAssembly 來告訴編譯器將一個程序集中的所有類型和方法都作為依賴。但請注意,這種方法不適用于泛型實例化。

      <ItemGroup>
          <TrimmerRootAssembly Include="MyAssembly" />
      </ItemGroup>
      

      TrimmerRootDescriptor

      對于高級用戶,他們可能想要控制從一個程序集中包含什么。在這種情況下,可以指定一個 TrimmerRootDescriptor

      <ItemGroup>
          <TrimmerRootDescriptor Include="link.xml" />
      </ItemGroup>
      

      TrimmerRootDescriptor 文件的文檔和格式可以在這里找到。

      Runtime Directives

      對于泛型實例化的情況,它們無法通過 TrimmerRootAssembly 或 TrimmerRootDescriptor 來解決,這里需要一個包含 runtime directives 的文件來告訴編譯器需要編譯的東西。

      <ItemGroup>
          <RdXmlFile Include="rd.xml" />
      </ItemGroup>
      

      rd.xml 中,你可以為你的泛型類型和方法指定實例化。

      rd.xml 文件的文檔和格式可以在這里找到。

      這種方法不推薦,但它可以解決你在使用 NativeAOT 時遇到的一些難題。請在使用 trimmer descriptor 或 runtime directives 之前,總是考慮用 DynamicallyAccessedMembersDynamicDependency 來注釋你的代碼,使其與剪裁/AOT 兼容。

      結語

      NativeAOT 是 .NET 中一個非常棒和強大的工具。有了 NativeAOT,你可以以可預測的性能構建你的應用,同時節省資源(更低的內存占用和更小的二進制大小)。

      它還將 .NET 帶到了不允許 JIT 編譯器的平臺,例如 iOS 和主機平臺。此外,它還使 .NET 能夠運行在嵌入式設備甚至裸機設備上(例如在 UEFI 上運行)。

      在使用工具之前了解工具,這樣你會節省很多時間。

      posted @ 2024-01-31 18:00  hez2010  閱讀(5518)  評論(13)    收藏  舉報
      主站蜘蛛池模板: 一道本AV免费不卡播放| 天堂av成人网在线观看| 国产精品激情| 99久久免费精品国产色| 永丰县| 亚洲无线码中文字幕在线| 性一交一乱一伦| 无码福利写真片视频在线播放| 91中文字幕在线一区| 中文字幕日韩有码国产| 免费看国产精品3a黄的视频| 女女互揉吃奶揉到高潮视频| 亚洲熟女乱综合一区二区| 亚洲精品成人区在线观看| 国产一区二区不卡视频在线| 无码精品国产va在线观看dvd | 卡一卡二卡三精品| 日韩熟妇| 无码成a毛片免费| 国产精品老熟女免费视频| 人妻中文字幕不卡精品| 国产精品免费视频不卡| 最近中文国语字幕在线播放| 又色又爽又黄的视频网站| 久久久久免费看成人影片| AV人摸人人人澡人人超碰| 国产美女69视频免费观看| 日韩不卡手机视频在线观看| 一区二区三区四区自拍视频| 亚洲深夜精品在线观看| 亚洲精品二区在线播放| 国产日韩AV免费无码一区二区三区| 国产初高中生粉嫩无套第一次| 无码日韩人妻精品久久| 久久精品女人天堂av| 一级女性全黄久久片免费| 午夜av高清在线观看| 国产性一交一乱一伦一色一情| 中文字幕乱码中文乱码毛片| av在线网站手机播放| 中文文字幕文字幕亚洲色 |