cancel
Showing results for 
Search instead for 
Did you mean: 
Chris_Tucker
Star Contributor
Star Contributor

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.

Why didn’t LINQ work before?

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.

But why aren’t static fields allowed in 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.

The Fix

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:

  1. Do not assume that a static field has been initialized. The scripting app domain may be unloaded between script executions, or your scripts may be executing in completely separate app domains. Initializing a static field in one script does not guarantee that it will be initialized in all subsequent script executions.
  2. Do not assume that changes made to a static field in one script will be seen by another script. As mentioned in the previous tip, the app domain environment can cause static fields to not be persisted between script executions. Do not make any changes to the value of a static field with the expectation that another script will be able to read the modified value.
  3. Do not assume that static fields will only be accessed by a single thread. Although scripts currently execute in a single-threaded environment, there is no guarantee that they will always continue to do so. In order to ensure maximum compatibility with future versions of OnBase, make sure that your static fields are accessed in a thread-safe manner.

An easy way to follow all of these guidelines is to declare your static fields as static readonly.

In Conclusion

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.

1 Comment