摘要:首先,我們需要有一個示例項目,用來觀察 System.ValueTuple 在框架內和 NuGet 包內的一些行爲。不知你是否好奇,System.ValueTuple 是新框架(.NET Core 3.0)開始引入的類型,但可以通過 NuGet 包向舊框架提供這些類型的使用。

不知你是否好奇,System.ValueTuple 是新框架(.NET Core 3.0)開始引入的類型,但可以通過 NuGet 包向舊框架提供這些類型的使用。並且,這些包即便安裝到本來就有此類型的新框架上也能正常運行而不會出現多處類型定義的問題。

這些類型是如何做到框架內定義了,包裏也定義了,卻能像同一個類型一樣作爲參數和返回值傳遞?本文帶你瞭解其中的奧祕。

示例項目

首先,我們需要有一個示例項目,用來觀察 System.ValueTuple 在框架內和 NuGet 包內的一些行爲。

創建一個 .NET Core 控制檯項目。然後我們需要修改兩個地方:

  1. Program.cs 文件
  2. 項目文件(*.csproj)文件
class Program
{
    static void Main()
    {
        var (a, b) = Foo();
        System.Console.WriteLine($"歡迎閱讀{a}的博客 {b}");
    }

    static (string a, string b) Foo() => ("呂毅", "blog.walterlv.com");
}
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net462;net48;netstandard2.0;netcoreapp2.0;netcoreapp3.1</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.ValueTuple" Version="4.5.0" />
  </ItemGroup>

</Project>

接下來,我們的研究都將基於此項目。

研究開始

System.ValueTuple 對舊框架的支持體現在三個方面:

  1. 舊框架中也能寫出新框架中的這種語法;
  2. 舊框架中也能正常使用此類型;
  3. 新框架中此類型不會與包中的類型衝突。

我們分別來看看這三個都是如何實現的。

語法支持

C# 從 7.0 開始支持元組類型的語法,即可以寫出這樣的代碼:

var (a, b) = Foo();

關於此新增功能,可以前往這裏查看:

C# 從 8.0 開始,各種原本需要實現特定接口才能寫出的語法,現在也可以不用實現接口了,只要有對應的方法存在即可,比如:

  • IDisposable
  • IEnumerable
  • Deconstruct

另外,從 C# 5.0 開始引入的 async/await 也是如此,無需實現任何接口,有 GetAwaiter 方法就夠了。也是一樣的情況,詳見:

也就是說,只要你的項目使用的 C# 版本在 7.0 以上,就可以使用元組解構這樣的語法。即便在 C# 7.0 以下,也能使用 System.ValueTuple,只是不能使用此語法而已。

舊框架兼容

System.ValueTuple 對舊框架的兼容,單純的就是通過 NuGet 包引入了這些類型,以及這些類型的實現而已。

我們在示例項目的 net462 的輸出目錄下找到 System.ValueTuple.dll 進行反編譯可以看出來這一點:

新框架不衝突

我們再去新框架裏面看看 System.ValueTuple 的情況。

例如先看看 net48 目錄下的 System.ValueTuple.dll:

可以發現,net48 下的 System.ValueTuple 已經全部使用 TypeForwardedTo 特性轉移到了 mscorelib 程序集。

.NET Core 3.1 版本和 .NET Standard 2.0 版本的輸出目錄裏是沒有 System.ValueTuple.dll 的,那麼它們的依賴是如何決定的呢?

答案是——不需要依賴!

我們來拆開 System.ValueTuple 的 NuGet 包看看。可在這裏下載: NuGet Gallery - System.ValueTuple 4.5.0

可發現它提供了這些不同框架的支持:

其中:

TypeForwardedTo
_._

原生支持 System.ValueTuple 的框架,其 NuGet 包中的框架內的文件是 _._ ,這個文件的出現僅僅是爲了能讓 zip 裏面有一個對應框架的文件夾。而 zip 對空文件夾的支持並不好,所以加一個這樣的文件可以避免文件夾消失,造成 NuGet 認爲不支持這樣的框架。

結論

TypeForwardedTo

額外的,我寫過另一個通過此方式獲得新舊框架兼容的包:

參考資料

相關文章