在Unity中解放高版本C#特性

之前我在知乎问过相关问题,后面参考了几个知友的回答,借鉴了一些项目,也算是基本解决了这个问题

也顺便附上我的知乎回答:https://www.zhihu.com/question/11672499668/answer/103930816515

以下是关于如何在 Unity 中使用更高版本的 C# 特性的详细解决办法(具有一定局限性

Comfirm the C# version that Unity supports

关于你的 unity editor 本身能够支持到多高的 C# 版本,以我的 unity 2022.3.52f1c1 为例,去 unity editor 所在的目录下面这个路径,打开 README 里面有 dotnet sdk 版本

dotnet_sdk

然后去官方搜索一下该版本的相关信息:

sdk_search

发现可以支持到 C#10,在这个版本中,C#处于可预览状态,如果是一般的 cs 项目(使用该 sdk),可以直接在 csproj 指定 <LangVersion>preview</LangVersion> 来使用部分的 C#11 特性。

但是 Unity 中的所谓 C# 项目并不是这么一回事,项目根目录里面的一堆 .csproj 文件,只是 Unity 自动生成给 IDE 看的,这也是为什么用 Rider,VSCode 之类的 IDE 打开项目后能进行解析()

Unity Compile

官方手册里面有关于这个的说明:Unity editor 会指定默认的编译选项给后端的 Roslyn 编译器(也就是C#版本指定 9.0)

compiler

不过后面也说明了可以传递别的参数(),于是可以在这篇文章找到答案,用户可以在 Assets/ 目录下添加 csc.rsp 文件来指定编译参数,文档给的例子是在 .rsp 文件里通过 -define:XXXX 来定义宏,用以条件编译。

但是我们显然能玩点别的东西,比如指定 -langversion ,按照前文的思路,我们可以启用 C#11 的部分特性。

所以我们创建一个 csc.rsp 文件放在 Assets/ 目录下(或者你的程序集的同目录),写入以下内容:

1
-langversion:preview -nullable

这些指令是将语言版本设置为预览状态,然后启用nullable编译

关于后者,你可以在代码中使用 ? 来声明一个类型可为 null,比如:

1
2
3
4
public struct Example
{
public int? ValueInt;
}

如果不启用的 nullable,使用 ? 会警告你 “需要在 #nullable 的上下文使用”。

然后你可以在你的脚本里面使用 C#11 的特性,回到 UnityEditor 中看看能不能通过编译。以下是一段简单的测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public struct DefaultValue
{
// set default value in struct field
public int Value = 114514;
}

public struct NonArgConstructor
{
public string Value;

// struct constructor with no-args
public NonArgConstructor()
{
Value = "1145141919810";
}
}

如果你使用的较新的 unity 2022.3 (>= 2022.3.12f1)那么这段代码是可以通过编译的(当然你也可以整个 mono 类去创建这两个 struct 输出确认是否真的设置成了指定的默认值)

不过你在编写上述代码的时候,IDE 里面一定是一片爆红吧 :laughing: 。

不用担心,让 csproj 来救你(x

Change .csproj files

在前面有说过,Unity 项目根目录的 .csproj 文件都是给 IDE 看的,你可以随便打开一个 .csproj 文件,里面应该很容易找到一行内容是:<LangVersion>9.0</LangVersion>

这会导致你的 IDE 会认为你的项目使用 C#9 ,所以前面编写 C#11 的代码的时候自然是会报错的啦(),你可以手动修改一下文件试试。

尝试将你的 Assembly-CSharp.csproj 文件里面的 langversion 那一行改成 <LangVersion>preview</LangVersion>

如果你使用程序集,比如有一个 My.Test.asmdef 的程序集,那么 UnityEditor 会自动给这个程序集生成一个 My.Test.csproj (当然也是在项目根目录),那么 IDE 对该程序集中的代码的解析,就会受到 My.Test.csproj 的影响,这时候需要去修改 My.Test.csproj 里面的 langversion 选项了()

回到你的 IDE 里面应该就不会报错了。

当然每次编译后,unity 都会重新生成一遍,所以建议写个脚本,读取 csproj, 然后修改相关的选项,然后可以监听 compilationFinished 事件自动执行。

Some lunatic ideas for higher version (P.S.)

还有一些本人没有实践过的想法,有些已经被别人尝试过,有些不确定有没有人试过,感兴趣的可以玩玩 :laughing:

  • 开一个 C# 项目,目标框架设置 <TargetFramework>netstandard2.1</TargetFramework>,然后语言版本设置 <TargetFramework>latest</TargetFramework> ,应该能够使用到 C#12 甚至更高的特性,当然部分特性可能缺少预定义类,可以通过 PolySharp 来补充(),详情可以参考 R3 项目。
  • 修改你的 Unity Editor 的 Roslyn 编译器依赖的 SDK 版本(?)(如果要尝试建议备份一下)

Welcome to my other publishing channels