Как в Unity3d использовать возможности C# 5/6/7?

403
08 июля 2017, 07:14

Захотел я идти в ногу со временем, посмотрел новые фишки и плюшки C#6/7, воодушевился. Там так всё вкусно и круто, что грех не пользоваться. Открыл Unity, Visual Studio, написал пару строк и...впал в уныние. На любые попытки использования новых фич мне говорят одно:

Feature bla bla bla is not available in C#4. Please use language version 6 or greater

или

Feature bla bla bla cannot be used because it is not part of the C# 4.0 language specification

Посмотрел версию Mono, которую использует Unity 5.6 (!!! самая последняя версия) и это было: 2.0 (Visual Studio built mono). В итоге в ней есть всё, что есть в C#3, плюс часть из C#4.

Это меня не очень радует и устраивает.

Есть ли какая возможность, всё-таки, использовать новые версии языка, а не застыть в далёком 2010-ом? Что можно сделать и как?

Answer 1

Да, можно использовать новые версии, но с оговорочкой...об этом ниже.

Хоть Unity и застряла на долгое время на CLR 2.0, тем не менее новые фичи C# не требуют самой последней версии CLR. Компиляторы Microsoft и Mono могут скомпилировать C# 5/6/7 для CLR 2.0 если их явно об этом попросить.

Замечание: однако некоторые фичи все же недоступны, например такие как dynamic.

Итак, эту возможность (и, на данный момент, может быть единственную) можно найти в репозитории битбакета Unity C# 5.0 and 6.0 Integration

Что надо сделать?
  • Для C# 6.0

    1. Скопировать папку CSharp60Support из репозитория (или со страницы закачки) к себе в Unity проект. Папку необходимо поместить в корень проекта, рядом с папкой Assets. НЕ во внутрь!
    2. Импортировать CSharp60Support.unitypackage (находится в папке CSharp60Support) в свой проект.
    3. Перезагрузить Unity
    4. [По желанию - не обязательно]: На Windows - запустить /CSharp60Support/ngen install.cmd с правами администратора. Команда скомпилирует csc.exe, pdb2mdb.exe и mcs.exe используя Ngen, что позволит производить компилирование в Unity чуточку быстрее.
  • Для C# 7.0

    1. Для MacOS скачать и установить Mono 4.6+. Для Windows скачать и установить .Net Framework 4.6.2+.
    2. Скопировать папку CSharp70Support из репозитория (или со страницы закачки) к себе в Unity проект. Папку необходимо поместить в корень проекта, рядом с папкой Assets. НЕ во внутрь!
    3. Импортировать CSharp70Support.unitypackage(находится в папке CSharp70Support) в свой проект.
    4. Перезагрузить Unity
    5. [По желанию - не обязательно] На Windows - запустить /CSharp70Support/ngen install.cmd с правами администратора. Команда скомпилирует csc.exe, pdb2mdb.exe и mcs.exe используя Ngen, что позволит производить компилирование в Unity чуточку быстрее.

Таким образом изменению подвергается только папка с текущим проектом. Остальные проекты будут работать как обычно с текущей версией языка, Mono и прочей жестью :)

Как это работает?
  1. /Assets/CSharp vNext Support/Editor/CSharpVNextSupport.dll - расширение для редактора, которое через рефлексию изменяет внутренние данные редактора, говоря ему, чтобы тот использовал альтернативный компилятор C# (/CSharpXXSupport/CSharpCompilerWrapper.exe). Если он не существует, то используется дефолтный.
  2. CSharpCompilerWrapper.exe получает и перенаправляет запросы на компиляцию от Unity к одному из актуальных C# компиляторов, которые используют следующие правила:
    • Если существует папка CSharp70Support и она содержит папку Roslyn, то используется компилятор C# 7.0;
    • Если существует папка CSharp60Support и она содержит папку Roslyn, то используется компилятор C# 6.0;
    • Иначе если существует папка CSharp60Support и она содержит mcs.exe, то использует компилятор Mono C# 6.0;
    • В ином случае используется компилятор по дефолту (/Unity/Editor/Data/Mono/lib/mono/2.0/gmcs.exe).

Убедитесь, что компилятор CSharpCompilerWrapper.exe действительно работает! Для этого посмотрите в логи компиляции: UnityProject/CSharpXXSupport/compilation.log

Какие платформы поддерживаются?

Работает это на всех основных платформах:

  • Windows (editor and standalone)
  • MacOS (editor and standalone)
  • Android
  • iOS

Чуть больше информации и небольших ограничениях вы можете почитать на главной странице репозитория, а также (т.к. основная информация дана выше) в виде цитаты с источника на английском языке:

Response (.rsp) files

If you want to use a response file to pass extra options to the compiler (e.g. -unsafe), the file must be named CSharpCompilerWrapper.rsp.

platforms are "supported"

On MacOS the Roslyn compiler cannot create debug information files (.pdb) that Unity can consume. If you compile your code with Roslyn on MacOS you won't be able to debug it.

Since WebGL doesn't offer any multithreading support, AsyncBridge and Task Parallel Library are not available for this platform. Caller Info attributes are also not available, because their support comes with AsyncBridge library.

AsyncBridge/TPL stuff is also not compatible with Windows Store Application platform (and probably all the platforms that use .Net runtime instead of Mono runtime) due to API differences between the recent versions of .Net Framework and the ancient version of TPL (System.Threading.dll) that comes with AsyncBridge. Namely, you can't use async/await, Caller Info attributes and everything from System.Threading.dll (concurrent collections for example).

Other known issues
  • C# 5.0/6.0 is not compatible with Unity Cloud Build service for obvious reason.

  • Using Mono C# 6.0 compiler may cause occasional Unity crashes while debugging in Visual Studio - http://forum.unity3d.com/threads/c-6-0.314297/page-2#post-2225696

  • IL2CPP doesn't support exception filters added in C# 6.0 (ExceptionFiltersTest.cs).

  • If a MonoBehaviour is declared inside a namespace, the source file should not contain any C# 6.0-specific language constructions before the MonoBehaviour declaration. Otherwise, the editor won't recognize the script as a MonoBehaviour component.

    Bad example:

    using UnityEngine;
    using static System.Math; // C# 6.0 syntax!
    namespace Foo
    {
     class Baz
     {
        object Qux1 => null; // C# 6.0 syntax!
        object Qux2 { get; } = null; // C# 6.0 syntax!
     }
      class Bar : MonoBehaviour { } // "No MonoBehaviour scripts in the file, or their names do not match the file name."
    }
    

    Good example:

    using UnityEngine;
    namespace Foo
    {
     class Bar : MonoBehaviour { } // ok
     class Baz
     {
        object Qux1 => null;
        object Qux2 { get; } = null;
     }
    }
    
  • There's a bug in Mono C# 6.0 compiler, related to null-conditional operator support (NullConditionalTest.cs):

    var foo = new[] { 1, 2, 3 };
    var bar = foo?[0];
    Debug.Log((foo?[0]).HasValue); // error CS1061: Type `int' does not 
    // contain a definition for `HasValue' and no extension method
    // `HasValue' of type `int' could be found. Are you missing an
    // assembly reference?
    

    Mono compiler thinks that foo?[0] is int while it's actually Nullable<int>. However, bar's type is deduced correctly - Nullable<int>.

License

All the source code is published under WTFPL version 2.

Want to talk about it?

http://forum.unity3d.com/threads/c-6-0.314297/#post-2108999

Random notes
  • Roslyn C# 6.0 compiler was taken from VS 2015 installation. C# 7.0 compiler was taken from VS 15 Preview 5 installation.

  • mcs.exe, pdb2mdb.exe and its dependencies were taken from Mono 4.4.1.0 installation. pdb2mdb.exe that comes with Unity is not compatible with the assemblies generated with Roslyn compiler.

  • AsyncBridge library contains a set of types that makes it possible to use async/await in projects that target CLR 2.0. It also provides Caller Info attributes support. For more information, check this blog post.

  • If you use async/await inside Unity events (Awake, Start, Update etc) you may notice that continuations (the code below await keyword) are executed in background threads. Most likely, this is not what you would want. To force await to return the execution to the main thread, you'll have to provide it with a synchronization context, like all WinForms and WPF applications do.

    Check UnityScheduler.cs, UnitySynchronizationContext.cs and UnityTaskScheduler.cs example implementations located in the project. These classes create and register several synchronization contexts for the Unity's main thread, so async/await could work the way they do in regular WinForms or WPF applications.

    For more information about what synchronization context is, what it is for and how to use it, see this set of articles by Stephen Toub: one, two, three.

P.S. Попробую отслеживать тему и держать ссылку на свежую версию актуальной.

READ ALSO
Обработка исключения Format Exception

Обработка исключения Format Exception

Как корректно обработать исключение Format Exception на проверку введенных значений в TextBox?

234
Json запрос с массивом

Json запрос с массивом

Здравствуйте!

332
OWIN twitter ошибка 401 Asp.net mvc

OWIN twitter ошибка 401 Asp.net mvc

Сайт пишу на aspnet mvc 5, при аутентификации через соц

229
WCF. Add Service Reference не видит сервис

WCF. Add Service Reference не видит сервис

Вот контракт и класс, его наследующий

240