在 Unity 中使用 .NET 4.xUsing .NET 4.x in Unity

C# 和 .NET (以 Unity 指令碼為基礎的技術) 持續接收到更新,因為 Microsoft 一開始是在 2002 年發行它們。C# and .NET, the technologies underlying Unity scripting, have continued to receive updates since Microsoft originally released them in 2002. 但是,Unity 開發人員可能察覺不到穩定新增至 C# 語言和.NET Framework 的新功能。But Unity developers may not be aware of the steady stream of new features added to the C# language and .NET Framework. 原因是在 Unity 2017.1 之前,Unity 使用 .NET 3.5 對等指令碼執行階段,因而遺失數年的更新。That's because before Unity 2017.1, Unity has been using a .NET 3.5 equivalent scripting runtime, missing years of updates.

隨著 Unity 2017.1 的發行,Unity 引進將其指令碼執行階段升級至 .NET 4.6 (C# 6 相容版本) 的實驗性版本。With the release of Unity 2017.1, Unity introduced an experimental version of its scripting runtime upgraded to a .NET 4.6, C# 6 compatible version. 在 Unity 2018.1 中,不再將 .NET 4.x 對等執行階段視為實驗性,現在會將舊版 .NET 3.5 對等執行階段視為舊版本。In Unity 2018.1, the .NET 4.x equivalent runtime is no longer considered experimental, while the older .NET 3.5 equivalent runtime is now considered to be the legacy version. 而隨著 Unity 2018.3 的發行,Unity 預測會將已升級的指令碼執行階段設為預設選取項目,甚至進一步更新為 C# 7。And with the release of Unity 2018.3, Unity is projecting to make the upgraded scripting runtime the default selection, and to update even further to C# 7. 如需本藍圖的詳細資訊和最新更新,請閱讀 Unity 的部落格文章,或瀏覽其 Experimental Scripting Previews 論壇For more information and the latest updates on this roadmap, read Unity's blog post or visit their Experimental Scripting Previews forum. 在此同時,請參閱下列各節,來深入了解 .NET 4.x 指令碼執行階段現在可用的新功能。In the meantime, check out the sections below to learn more about the new features available now with the .NET 4.x scripting runtime.

先決條件Prerequisites

在 Unity 中啟用 .NET 4.x 指令碼執行階段Enabling the .NET 4.x scripting runtime in Unity

若要在 Unity 中啟用 .NET 4.x 指令碼執行階段,請採取下列步驟:To enable the .NET 4.x scripting runtime, take the following steps:

  1. 選取 [編輯] > [專案設定] > [Player] (播放程式),以在 Unity Inspector 中開啟 PlayerSettings。Open PlayerSettings in the Unity Inspector by selecting Edit > Project Settings > Player.

  2. 在 [組態] 標題下,按一下 [Scripting Runtime Version] (指令碼執行階段版本) 下拉式清單,然後選取 [.NET 4.x Equivalent] (.NET 4.x 對等項目)。Under the Configuration heading, click the Scripting Runtime Version dropdown and select .NET 4.x Equivalent. 系統將提示您重新啟動 Unity。You will be prompted to restart Unity.

選取 [.NET 4.x Equivalent] (.NET 4.x 對等項目)

選擇 .NET 4.x 或 .NET Standard 2.0 設定檔Choosing between .NET 4.x and .NET Standard 2.0 profiles

在您切換到 .NET 4.x 對等指令碼執行階段之後,即可使用 PlayerSettings 中的下拉式功能表 ([編輯] > [專案設定] > [Player] (播放程式)) 指定 [Api Compatibility Level] (API 相容性層級)。Once you've switched to the .NET 4.x equivalent scripting runtime, you can specify the Api Compatibility Level using the dropdown menu in the PlayerSettings (Edit > Project Settings > Player). 有兩個選項:There are two options:

  • .NET Standard 2.0.NET Standard 2.0. 此設定檔符合 .NET Foundation 所發行的 .NET Standard 2.0 設定檔This profile matches the .NET Standard 2.0 profile published by the .NET Foundation. Unity 建議將 .NET Standard 2.0 用於新專案。Unity recommends .NET Standard 2.0 for new projects. 這小於適用於大小受限平台的 .NET 4.x。It's smaller than .NET 4.x, which is advantageous for size-constrained platforms. 此外,Unity 致力於跨 Unity 所支援的所有平台支援此設定檔。Additionally, Unity has committed to supporting this profile across all platforms that Unity supports.

  • .Net 4.x。.NET 4.x. 此設定檔提供對最新 .NET 4 API 的存取。This profile provides access to the latest .NET 4 API. 它包含 .NET Framework 類別庫中所有可用的程式碼,同時支援 .NET Standard 2.0 設定檔。It includes all of the code available in the .NET Framework class libraries and supports .NET Standard 2.0 profiles as well. 如果您的專案需要 .NET Standard 2.0 設定檔中未包含的 API 部分,則請使用 .NET 4.x 設定檔。Use the .NET 4.x profile if your project requires part of the API not included in the .NET Standard 2.0 profile. 不過,Unity 的所有平台上可能都不支援此 API 的某些部分。However, some parts of this API may not be supported on all of Unity's platforms.

您可以在 Unity 的部落格文章中深入了解這些選項。You can read more about these options in Unity's blog post.

在使用 .NET 4.x API 相容性層級時新增組件參考Adding assembly references when using the .NET 4.x Api Compatibility Level

使用 [Api Compatibility Level] (API 相容性層級) 下拉式清單中的 .NET Standard 2.0 設定時,可參考和使用 API 設定檔中的所有組件。When using the .NET Standard 2.0 setting in the Api Compatibility Level dropdown, all assemblies in the API profile are referenced and usable. 不過,使用較大的 .NET 4.x 設定檔時,預設不會參考 Unity 隨附的部分組件。However, when using the larger .NET 4.x profile, some of the assemblies that Unity ships with aren't referenced by default. 若要使用這些 API,您必須手動新增組件參考。To use these APIs, you must manually add an assembly reference. 您可以在 Unity 編輯器安裝的 MonoBleedingEdge/lib/mono 目錄中檢視 Unity 隨附的組件:You can view the assemblies Unity ships with in the MonoBleedingEdge/lib/mono directory of your Unity editor installation:

MonoBleedingEdge 目錄

例如,如果您使用 .NET 4.x 設定檔,並且想要使用 HttpClient,則必須新增 System.Net.Http.dll 的組件參考。For example, if you're using the .NET 4.x profile and want to use HttpClient, you must add an assembly reference for System.Net.Http.dll. 否則,編譯器會通知您遺漏組件參考:Without it, the compiler will complain that you're missing an assembly reference:

遺漏組件參考

Visual Studio 會在每次開啟 Unity 專案時重新產生其 .csproj 和 .sln 檔案。Visual Studio regenerates .csproj and .sln files for Unity projects each time they're opened. 因此,您無法直接在 Visual Studio 中新增組件參考,因為它們會在重新開啟專案時遺失。As a result, you cannot add assembly references directly in Visual Studio because they'll be lost upon reopening the project. 相反地,必須使用名為 csc 的特殊文字檔:Instead, a special text file named csc.rsp must be used:

  1. 在 Unity 專案的根 資產 目錄中,建立名為 csc 的新文字檔。Create a new text file named csc.rsp in your Unity project's root Assets directory.

  2. 在空文字檔中的第一行,輸入:-r:System.Net.Http.dll,然後儲存檔案。On the first line in the empty text file, enter: -r:System.Net.Http.dll and then save the file. 您可以將 "System.Net.Http.dll" 取代為任何可能遺漏參考的內含組件。You can replace "System.Net.Http.dll" with any included assembly that might be missing a reference.

  3. 重新啟動 Unity 編輯器。Restart the Unity editor.

利用 .NET 相容性Taking advantage of .NET compatibility

除了新 C# 語法和語言功能之外,.NET 4.x 指令碼執行階段還可讓 Unity 使用者存取與舊版 .NET 3.5 指令碼執行階段不相容之 .NET 套件的超大型程式庫。In addition to new C# syntax and language features, the .NET 4.x scripting runtime gives Unity users access to a huge library of .NET packages that are incompatible with the legacy .NET 3.5 scripting runtime.

將套件從 NuGet 新增至 Unity 專案Add packages from NuGet to a Unity project

NuGet 是適用於 .NET 的套件管理員。NuGet is the package manager for .NET. NuGet 整合到 Visual Studio。NuGet is integrated into Visual Studio. 不過,Unity 專案需要新增 NuGet 套件的特殊程序。However, Unity projects require a special process to add NuGet packages. 這是因為當您在 Unity 中開啟專案時,會重新產生其 Visual Studio 專案檔,並復原所需的組態。This is because when you open a project in Unity, its Visual Studio project files are regenerated, undoing necessary configurations. 若要將套件從 NuGet 新增至 Unity 專案,請執行下列作業:To add a package from NuGet to your Unity project do the following:

  1. 瀏覽 NuGet 以找到您要新增的相容套件 (.NET Standard 2.0 或 .NET 4.x)。Browse NuGet to locate a compatible package you'd like to add (.NET Standard 2.0 or .NET 4.x). 此範例示範如何將 Json.NET (使用 JSON 的熱門套件) 新增至 .NET Standard 2.0 專案。This example will demonstrate adding Json.NET, a popular package for working with JSON, to a .NET Standard 2.0 project.

  2. 按一下 [ 下載 ] 按鈕:Click the Download button:

    [下載] 按鈕

  3. 找到下載的檔案,並將副檔名從 .nupkg 變更為 .zipLocate the downloaded file and change the extension from .nupkg to .zip.

  4. 在 ZIP 檔案內,瀏覽至 lib/netstandard2.0 目錄,並複製 Newtonsoft.Json.dll 檔案。Within the zip file, navigate to the lib/netstandard2.0 directory and copy the Newtonsoft.Json.dll file.

  5. 在 Unity 專案的根 Assets 資料夾中,建立名為 Plugins 的新資料夾。In your Unity project's root Assets folder, create a new folder named Plugins. Plugins 是 Unity 中的特殊資料夾名稱。Plugins is a special folder name in Unity. 如需詳細資訊,請參閱 Unity 文件See the Unity documentation for more information.

  6. Newtonsoft.Json.dll 檔案貼入 Unity 專案的 Plugins 目錄。Paste the Newtonsoft.Json.dll file into your Unity project's Plugins directory.

  7. 在 Unity 專案的 Assets 目錄中,建立名為 link.xml 的檔案,並新增下列 XML。Create a file named link.xml in your Unity project's Assets directory and add the following XML. 這將確保 Unity 的位元組程式碼去除程序不會在匯出至 IL2CPP 平台時移除必要資料。This will ensure Unity's bytecode stripping process does not remove necessary data when exporting to an IL2CPP platform. 此步驟是這個程式庫特有的步驟時,以類似方式使用反映的其他程式庫可能會發生問題。While this step is specific to this library, you may run into problems with other libraries that use Reflection in similar ways. 如需詳細資訊,請參閱本主題的 Unity 文件For more information, please see Unity's docs on this topic.

    <linker>
      <assembly fullname="System.Core">
        <type fullname="System.Linq.Expressions.Interpreter.LightLambda" preserve="all" />
      </assembly>
    </linker>
    

一切準備就緒後,您現在可以使用 Json.NET 套件。With everything in place, you can now use the Json.NET package.

using Newtonsoft.Json;
using UnityEngine;

public class JSONTest : MonoBehaviour
{
    class Enemy
    {
        public string Name { get; set; }
        public int AttackDamage { get; set; }
        public int MaxHealth { get; set; }
    }
    private void Start()
    {
        string json = @"{
            'Name': 'Ninja',
            'AttackDamage': '40'
            }";

        var enemy = JsonConvert.DeserializeObject<Enemy>(json);

        Debug.Log($"{enemy.Name} deals {enemy.AttackDamage} damage.");
        // Output:
        // Ninja deals 40 damage.
    }
}

這個簡單範例使用沒有相依性的程式庫。This is a simple example of using a library which has no dependencies. NuGet 套件依賴其他 NuGet 套件時,您需要手動下載這些相依性,並使用相同的方式將其新增至專案。When NuGet packages rely on other NuGet packages, you would need to download these dependencies manually and add them to the project in the same way.

新的語法和語言功能New syntax and language features

使用更新過的指令碼執行階段可讓 Unity 開發人員存取 C# 6 和一堆新語言功能和語法。Using the updated scripting runtime gives Unity developers access to C# 6 and a host of new language features and syntax.

Auto 屬性初始設定式Auto-property initializers

在 Unity 的 .NET 3.5 指令碼執行階段中,auto-property 語法可讓您輕鬆地快速定義未初始化的屬性,但必須在指令碼的其他位置初始化。In Unity's .NET 3.5 scripting runtime, the auto-property syntax makes it easy to quickly define uninitialized properties, but initialization has to happen elsewhere in your script. 現在使用 .NET 4.x 執行階段,就可以初始化同一行的 auto-properties:Now with the .NET 4.x runtime, it's possible to initialize auto-properties in the same line:

// .NET 3.5
public int Health { get; set; } // Health has to be initialized somewhere else, like Start()

// .NET 4.x
public int Health { get; set; } = 100;

字串插補String interpolation

使用較舊的 .NET 3.5 執行階段,字串串連需要冗長的必要語法。With the older .NET 3.5 runtime, string concatenation required awkward syntax. 現在有了 .NET 4.x 執行時間, $ 字串插補功能可讓您以更直接易懂的語法將運算式插入字串中:Now with the .NET 4.x runtime, the $ string interpolation feature allows expressions to be inserted into strings in a more direct and readable syntax:

// .NET 3.5
Debug.Log(String.Format("Player health: {0}", Health)); // or
Debug.Log("Player health: " + Health);

// .NET 4.x
Debug.Log($"Player health: {Health}");

運算式主體成員Expression-bodied members

使用 .NET 4.x 執行階段中可用的較新 C# 語法,Lambda 運算式可以取代函數主體,讓它們更為簡潔:With the newer C# syntax available in the .NET 4.x runtime, lambda expressions can replace the body of functions to make them more succinct:

// .NET 3.5
private int TakeDamage(int amount)
{
    return Health -= amount;
}

// .NET 4.x
private int TakeDamage(int amount) => Health -= amount;

您也可以在唯讀屬性中使用運算式主體成員︰You can also use expression-bodied members in read-only properties:

// .NET 4.x
public string PlayerHealthUiText => $"Player health: {Health}";

以工作為基礎的非同步模式 (TAP)Task-based Asynchronous Pattern (TAP)

非同步程式設計允許執行耗時作業,而不會讓您的應用程式變得無回應。Asynchronous programming allows time consuming operations to take place without causing your application to become unresponsive. 此功能也可讓您的程式碼先等待耗時作業完成,再繼續執行根據這些作業結果的程式碼。This functionality also allows your code to wait for time consuming operations to finish before continuing with code that depends on the results of these operations. 例如,您可以等待載入檔案或完成網路作業。For example, you could wait for a file to load or a network operation to complete.

在 Unity 中,通常會使用協同程式完成非同步程式設計。In Unity, asynchronous programming is typically accomplished with coroutines. 不過,從 C# 5 之後,在 .NET 開發中慣用的非同步程式設計方法已是搭配使用 asyncawait 關鍵字與 System.Threading.Task工作非同步模式 (TAP)However, since C# 5, the preferred method of asynchronous programming in .NET development has been the Task-based Asynchronous Pattern (TAP) using the async and await keywords with System.Threading.Task. 總而言之,在 async 函數中,您可以 await (等待) 工作完成,而不需要封鎖更新應用程式的其餘部分:In summary, in an async function you can await a task's completion without blocking the rest of your application from updating:

// Unity coroutine
using UnityEngine;
public class UnityCoroutineExample : MonoBehaviour
{
    private void Start()
    {
        StartCoroutine(WaitOneSecond());
        DoMoreStuff(); // This executes without waiting for WaitOneSecond
    }
    private IEnumerator WaitOneSecond()
    {
        yield return new WaitForSeconds(1.0f);
        Debug.Log("Finished waiting.");
    }
}
// .NET 4.x async-await
using UnityEngine;
using System.Threading.Tasks;
public class AsyncAwaitExample : MonoBehaviour
{
    private async void Start()
    {
        Debug.Log("Wait.");
        await WaitOneSecondAsync();
        DoMoreStuff(); // Will not execute until WaitOneSecond has completed
    }
    private async Task WaitOneSecondAsync()
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        Debug.Log("Finished waiting.");
    }
}

TAP 是一個複雜主題,而開發人員應該考慮其 Unity 特定細微差別。TAP is a complex subject, with Unity-specific nuances developers should consider. 因此,TAP 不是 Unity 中協同程式的通用取代項目;不過,它是另一個要運用的工具。As a result, TAP isn't a universal replacement for coroutines in Unity; however, it is another tool to leverage. 此功能的範圍不在本文範圍內,但在下面提供一些一般最佳做法和秘訣。The scope of this feature is beyond this article, but some general best practices and tips are provided below.

利用 Unity 開始使用 TAP 的參考Getting started reference for TAP with Unity

這些秘訣可協助您在 Unity 中開始使用 TAP:These tips can help you get started with TAP in Unity:

  • 要等候的非同步函式應該有傳回型別 TaskTask<TResult>Asynchronous functions intended to be awaited should have the return type Task or Task<TResult>.
  • 傳回工作的非同步函數應該在其名稱附加尾碼 "Async"Asynchronous functions that return a task should have the suffix "Async" appended to their names. "Async" 尾碼有助於指出應該一律等候函數。The "Async" suffix helps indicate that a function should always be awaited.
  • 只會使用可從傳統同步程式碼引發 async 函數之函數的 async void 傳回型別。Only use the async void return type for functions that fire off async functions from traditional synchronous code. 這類函數本身無法等候,而且不應該在其名稱中有 "Async" 尾碼。Such functions cannot themselves be awaited and shouldn't have the "Async" suffix in their names.
  • 根據預設,Unity 使用 UnitySynchronizationContext 確保在主要執行緒上執行 async 函數。Unity uses the UnitySynchronizationContext to ensure async functions run on the main thread by default. Unity API 無法在主要執行緒外部存取。The Unity API isn't accessible outside of the main thread.
  • 您可以使用和等方法在背景執行緒上執行工作 Task.Run Task.ConfigureAwait(false)It's possible to run tasks on background threads with methods like Task.Run and Task.ConfigureAwait(false). 這項技術適用於卸載主要執行緒的耗費資源作業,以提高效能。This technique is useful for offloading expensive operations from the main thread to enhance performance. 不過,使用背景執行緒可能會導致很難偵錯的問題 (例如競爭條件)。However, using background threads can lead to problems that are difficult to debug, such as race conditions.
  • Unity API 無法在主要執行緒外部存取。The Unity API isn't accessible outside the main thread.
  • Unity WebGL 組建不支援使用執行緒的工作。Tasks that use threads aren't supported on Unity WebGL builds.

協同程式與 TAP 的差異Differences between coroutines and TAP

協同程式與 TAP / async-await 之間有一些重要差異:There are some important differences between coroutines and TAP / async-await:

  • 協同程式無法傳回值,但 Task<TResult> 可以。Coroutines cannot return values, but Task<TResult> can.
  • 您無法在 try-catch 陳述式中放置 yield,這讓協同程式很難處理錯誤。You cannot put a yield in a try-catch statement, making error handling difficult with coroutines. 不過,try-catch 可與 TAP 搭配運作。However, try-catch works with TAP.
  • 在未衍生自 MonoBehaviour 的類別中,無法使用 Unity 的協同程式功能。Unity's coroutine feature isn't available in classes that don't derive from MonoBehaviour. TAP 很適合在這類類別中執行非同步程式設計。TAP is great for asynchronous programming in such classes.
  • 此時,Unity 不建議使用 TAP 完全取代協同程式。At this point, Unity doesn't suggest TAP as a wholesale replacement of coroutines. 分析是知道其中一種方法與任何指定專案另一種方法之特定結果的唯一方式。Profiling is the only way to know the specific results of one approach versus the other for any given project.

注意

截自 Unity 2018.2,完全不支援偵錯含中斷點的 async 方法;不過,Unity 2018.3 預期會有此功能As of Unity 2018.2, debugging async methods with break points isn't fully supported; however, this functionality is expected in Unity 2018.3.

nameof 運算子nameof operator

nameof 運算子會取得變數、型別或成員的字串名稱。The nameof operator gets the string name of a variable, type, or member. 有些需要使用 nameof 的情況是記錄錯誤,以及取得列舉的字串名稱:Some cases where nameof comes in handy are logging errors, and getting the string name of an enum:

// Get the string name of an enum:
enum Difficulty {Easy, Medium, Hard};
private void Start()
{
    Debug.Log(nameof(Difficulty.Easy));
    RecordHighScore("John");
    // Output:
    // Easy
    // playerName
}
// Validate parameter:
private void RecordHighScore(string playerName)
{
    Debug.Log(nameof(playerName));
    if (playerName == null) throw new ArgumentNullException(nameof(playerName));
}

呼叫端資訊屬性Caller info attributes

呼叫端資訊屬性提供方法呼叫端的相關資訊。Caller info attributes provide information about the caller of a method. 您必須針對要與「呼叫端資訊」屬性搭配使用的每個參數提供預設值:You must provide a default value for each parameter you want to use with a Caller Info attribute:

private void Start ()
{
    ShowCallerInfo("Something happened.");
}
public void ShowCallerInfo(string message,
        [System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
        [System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
        [System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
    Debug.Log($"message: {message}");
    Debug.Log($"member name: {memberName}");
    Debug.Log($"source file path: {sourceFilePath}");
    Debug.Log($"source line number: {sourceLineNumber}");
}
// Output:
// Something happened
// member name: Start
// source file path: D:\Documents\unity-scripting-upgrade\Unity Project\Assets\CallerInfoTest.cs
// source line number: 10

using staticUsing static

using static 可讓您使用靜態函數,而不需要鍵入其類別名稱。Using static allows you to use static functions without typing its class name. 如果您需要使用相同類別中的數個靜態函數,則使用 using static 可以節省空間和時間:With using static, you can save space and time if you need to use several static functions from the same class:

// .NET 3.5
using UnityEngine;
public class Example : MonoBehaviour
{
    private void Start ()
    {
        Debug.Log(Mathf.RoundToInt(Mathf.PI));
        // Output:
        // 3
    }
}
// .NET 4.x
using UnityEngine;
using static UnityEngine.Mathf;
public class UsingStaticExample: MonoBehaviour
{
    private void Start ()
    {
        Debug.Log(RoundToInt(PI));
        // Output:
        // 3
    }
}

IL2CPP 考量IL2CPP Considerations

將遊戲匯出至 iOS 這類平台時,Unity 將使用其 IL2CPP 引擎以將 IL「轉換」為 C++ 程式碼,而且接著會使用目標平台的原生編譯器來編譯 C++ 程式碼。When exporting your game to platforms like iOS, Unity will use its IL2CPP engine to "transpile" IL to C++ code which is then compiled using the native compiler of the target platform. 在此情節中,有幾項不支援的 .NET 功能,例如反映的組件和 dynamic 關鍵字的用法。In this scenario, there are several .NET features which are not supported, such as parts of Reflection, and usage of the dynamic keyword. 雖然您可以利用自己的程式碼控制這些功能的使用,但是請注意,使用未以 Unity 和 IL2CPP 撰寫的協力廠商 DLL 和 SDK 可能會發生問題。While you can control using these features in your own code, you may run into problems using 3rd party DLLs and SDKs which were not written with Unity and IL2CPP in mind. 如需本主題的詳細資訊,請參閱 Unity 網站上的 Scripting Restrictions (指令碼限制) 文件。For more information on this topic, please see the Scripting Restrictions docs on Unity's site.

此外,如上述 Json.NET 範例所述,Unity 將嘗試在 IL2CPP 匯出程序期間去除未使用的程式碼。Additionally, as mentioned in the Json.NET example above, Unity will attempt to strip out unused code during the IL2CPP export process. 雖然這通常不是問題,但使用反映的程式庫可能會不小心去除將在執行時間呼叫的屬性或方法,而無法在匯出時判斷。While this typically isn't an issue, with libraries that use Reflection, it can accidentally strip out properties or methods that will be called at run time that can't be determined at export time. 若要修正這些問題,請將 link.xml 檔案新增至專案,而專案包含不要對其執行去除程序的組件和命名空間清單。To fix these issues, add a link.xml file to your project which contains a list of assemblies and namespaces to not run the stripping process against. 如需完整詳細資料,請參閱位元組程式碼去除的 Unity 文件For full details, please see Unity's docs on bytecode stripping.

.NET 4.x 範例 Unity 專案.NET 4.x Sample Unity Project

此範例包含數個 .NET 4.x 功能範例。The sample contains examples of several .NET 4.x features. 您可以在 GitHub 下載專案或檢視原始程式碼。You can download the project or view the source code on GitHub.

其他資源Additional resources