Those of you who write Unity Scripts may have noticed how difficult it is to use LINQ extension methods in your scripts. Trying to use LINQ with an anonymous method resulted in a cryptic error message about static fields not being allowed in scripts, and using LINQ without anonymous methods is cumbersome and verbose. We want Unity Scripts to be easy to develop and maintain -- that’s why, starting in OnBase 16, we have removed all restrictions on using LINQ in Unity Scripts.
Scripts which used LINQ methods failed to compile because they triggered a compile-time check for static fields (More information on the reasoning behind this check is found below). But what does LINQ have to do with static fields? To understand that, we’ll take a look at the code generated by the compiler when using LINQ. I have a simple class with a method that filters a list of strings to only those strings which start with the letter “a”:
public class LINQExample{ public IEnumerable<string> StartsWithA(IEnumerable<string> strings) { return strings.Where(s => s.StartsWith("a")); }}
If I build my project and decompile the built assembly, we can see the code that is generated:
public class LINQExample{ [CompilerGenerated] private static Func<string, bool> CS$<>9__CachedAnonymousMethodDelegate1; public LINQExample() { base.ctor(); } public IEnumerable<string> StartsWithA(IEnumerable<string> strings) { IEnumerable<string> source = strings; if (LINQExample.CS$<>9__CachedAnonymousMethodDelegate1 == null) { // ISSUE: method pointer LINQExample.CS$<>9__CachedAnonymousMethodDelegate1 = new Func<string, bool>((object) null, __methodptr(<StartsWithA>b__0)); } Func<string, bool> predicate = LINQExample.CS$<>9__CachedAnonymousMethodDelegate1; return Enumerable.Where<string>(source, predicate); } [CompilerGenerated] private static bool <StartsWithA>b__0(string s) { return s.StartsWith("a"); }}
At the top of the class, there is a static variable, CS$<>9__CachedAnonymousMethodDelegate1. This variable is the equivalent of the anonymous method in our original code, s => s.StartsWith("a"), and is the source of the compiler error about static fields in Unity Scripts.
That restriction was put into place because of the environment in which scripts are executed. In order to isolate them from the rest of OnBase, all Unity Scripts execute in a scripting app domain. However, we do not make any guarantees about the lifetime or behavior of the scripting app domain. This means that the behavior of static variables outside of a single script execution is undefined. In order to prevent bugs from the improper use of static variables, we decided to restrict their use completely.
In order to make LINQ methods work in scripts (and to fix some other, more esoteric, bugs), we decided to remove the restriction on static fields. We felt that the benefits of being able to use static fields outweighed the potential risks. If you do decide to use a static field in your script, following these guidelines will help avoid any bugs:
An easy way to follow all of these guidelines is to declare your static fields as static readonly.
We hope that you’ll be able to use LINQ to write powerful Unity Scripts with more ease than ever before. As always, feel free to leave a comment or post on the forum if you have any questions.