Many attributes let you pass in string parameters that can reference members or contain C# expressions to be evaluated. This can be extremely useful, as it lets you quickly and easily inject basic logic into your inspector. Attribute expressions are denoted by a string that starts with an @ symbol.
One of the many attributes that support expressions is InfoBox, which accepts an expression that evaluate to a string value, which is used as the content of the box.
The following attribute declaration is one of the simplest possible uses of attribute expressions, and will result in an infobox that displays the unmodified contents of the myStr field:
[InfoBox("@myStr")]
public string myStr;
You can also write more complex logic. The following attribute declaration will result in an infobox that always displays the current time:
[InfoBox(@"@""The current time is: "" + DateTime.Now.ToString(""HH:mm:ss"")")]
public string myStr;
Attributes like ShowIf and HideIf also support expressions:
[ShowIf("@this.someNumber >= 0f && this.someNumber <= 10f")]
public string myStr;
public float someNumber;
There are also some special expression keywords that let you access various contextual values from an attribute expression. For example, the $property keyword lets you access the InspectorProperty instance of the member that the expression is being evaluated on:
[Serializable]
public class Example
{
[InfoBox(@"@""This member's parent property is called "" + $property.Parent.NiceName")]
public string myStr;
}
// Now, anywhere you declare it, myStr will now dynamically know the name of its parent
public Example exampleInstance;
There's also the $value keyword, which lets you access the value of the member the expression is on without needing to type the name of the member. This lets you reuse the same expression (perhaps declared in a const) on several different members. For example, a more universal version of the InfoBox expression in the first example in this tutorial could be:
[InfoBox("@$value")]
public string myStr;
These values are called "named expression arguments", and correspond to the named values that are passed into the expression system by the ValueResolver or ActionResolver that is handling the expression. For more information about which named values are available in expressions, and how to add more of your own, see Named Values.
It is also possible to easily retrieve the properties of other members in the scope of the expression using the special property query syntax: #(memberName).
public List<string> someList;
[OnValueChanged("@#(someList).State.Expanded = $value")]
public bool expandList;
The above expression finds the property for the someList member and modifies its expanded state when the value of the expandList member is changed. Learn more about the state system here.
Odin's attribute expressions are backed by a lightweight compiler that supports the majority of C#'s expression syntax. Expressions are quite performant; they are not interpreted, but are fully compiled into emitted IL which is then further JIT'ed into native machine code. If you write an invalid expression, you will be provided with a helpful compiler error indicating what is wrong:
As you might surmise, attribute expressions are an extremely flexible and powerful tool, and make it significantly easier to add custom logic and behaviour to all of Odin's attributes, without needing to create and reference countless extra members.