Update your C # in Unity ~ Expression-style function members ~
There was a time when only old C # could be used in Unity. But that’s a thing of the past. The latest LTS at the time of this writing, Unity 2019.4, supports C # 7.3. In addition, C # 8.0 will be supported in Unity 2020.2, the latest Beta version at the time of this writing.
Since I could only use old C # in Unity for a long time, I think there are many people who say “Is there such a function in C #? I didn’t know!”. In this “Update your C # in Unity” series, we will introduce “You can use relatively new features of C # in Unity like this!”.
Language feature name: expression-style function member
Added version: New addition in C # 6.0, possible addition of members in C # 7.0
Description: Ability to describe members more concisely if the implementation of a member, such as a method or property, is a single expression.
When writing code in C #, there are times when you write a method that just calls a single method. For example, a method that calls StartCoroutine and returns a Coroutine as follows:
using System.Collections;
using UnityEngine;
public class Launcher : MonoBehaviour
{
public Coroutine Launch()
{
return StartCoroutine(LaunchImpl());
}
private IEnumerator LaunchImpl()
{
//Abbreviation
yield break;
}
}
When the method implementation is written in a single expression like this, the method can be written in a more concise expression format.
using System.Collections;
using UnityEngine;
public class Launcher : MonoBehaviour
{
//Described in expression format
//return or{Or}I don't need
public Coroutine Launch() => StartCoroutine(LaunchImpl());
private IEnumerator LaunchImpl()
{
//Abbreviation
yield break;
}
}
return
, {
,}
are boilerplate descriptions that are not the essence of processing. By utilizing the function members in the form of expressions, it became a concise description that described only the essence of processing.
By the way, you can also write a method whose return value is void as follows.
using UnityEditor;
public static class AssetUpdater
{
[MenuItem("Assets/ForceReserializeAssets")]
private static void ForceReserializeAssets() => AssetDatabase.ForceReserializeAssets();
}
Expression-style function members are often used for simple processing implementations of ToString methods and getter-only properties.
[Serializable]
public class Circle
{
[SerializeField]
private float x;
[SerializeField]
private float y;
[SerializeField]
private float radius;
public Circle(float x, float y, float radius)
{
this.x = x;
this.y = y;
this.radius = radius;
}
//Can also be used to return a field as is
public float X => x;
public float Y => y;
public float Radius => radius;
//Can also be used for properties that describe logic
public float Area => Mathf.PI * radius * radius;
//Can also be used to implement ToString
public override string ToString() => $"Center ({X},{Y}) Radius:{Radius}";
}
It can also be used for properties and indexers that have both getters and setters.
[Serializable]
public class State
{
[SerializeField] private int score;
public State(int score)
{
this.score = score;
}
public int Score
{
get => score;
set => score = value;
}
}
public class Dungeon
{
private int[][] map;
public int this[int i, int j]
{
set => map[i][j] = value;
get => map[i][j];
}
}
You may be wondering, “Isn’t it happy to be shortened?”
The answer to that question is, “When a lot of short members are lined up, I’m glad that it becomes very short if I write it in the form of a formula.”
For example, if you implement some Vector2 operators in the traditional way, you’ll probably get this:
public static Vector2 operator +(Vector2 a, Vector2 b) {
return new Vector2(a.x + b.x, a.y + b.y);
}
public static Vector2 operator -(Vector2 a, Vector2 b) {
return new Vector2(a.x - b.x, a.y - b.y);
}
public static Vector2 operator *(Vector2 a, Vector2 b) {
return new Vector2(a.x * b.x, a.y * b.y);
}
public static Vector2 operator /(Vector2 a, Vector2 b) {
return new Vector2(a.x / b.x, a.y / b.y);
}
public static Vector2 operator -(Vector2 a) {
return new Vector2(-a.x, -a.y);
}
public static Vector2 operator *(Vector2 a, float d) {
return new Vector2(a.x * d, a.y * d);
}
public static Vector2 operator *(float d, Vector2 a) {
new Vector2(a.x * d, a.y * d);
}
public static Vector2 operator /(Vector2 a, float d) {
return new Vector2(a.x / d, a.y / d);
}
It takes up a lot of space with {
and }
.
It’s nice to describe members in a formula format when the members are lined up like this. The non-essential “ return
, {
, }
“does not take up the number of lines or space.
This will be shorter like this.
public static Vector2 operator +(Vector2 a, Vector2 b) => new Vector2(a.x + b.x, a.y + b.y);
public static Vector2 operator -(Vector2 a, Vector2 b) => new Vector2(a.x - b.x, a.y - b.y);
public static Vector2 operator *(Vector2 a, Vector2 b) => new Vector2(a.x * b.x, a.y * b.y);
public static Vector2 operator /(Vector2 a, Vector2 b) => new Vector2(a.x / b.x, a.y / b.y);
public static Vector2 operator -(Vector2 a) => new Vector2(-a.x, -a.y);
public static Vector2 operator *(Vector2 a, float d) => new Vector2(a.x * d, a.y * d);
public static Vector2 operator *(float d, Vector2 a) => new Vector2(a.x * d, a.y * d);
public static Vector2 operator /(Vector2 a, float d) => new Vector2(a.x / d, a.y / d);
It became very short when I listed it.
By the way, it can also be used in the constructor. In combination with “read-only automatic properties” and “tuple generation / decomposition”, you can also apply something like this. (I use value tuples, but they don’t generate value tuples internally.) I sometimes see this writing in official Microsoft documentation.
public class Point {
public int X { get; }
public int Y { get; }
public Point(int x, int y) => (X, Y) = (x, y);
}
//Internally it looks like this
//I'm using value tuples, but no value tuples are generated internally https://sharplab.io/#v2:EYLgtghgzgLgpgJwDQBMQGoA+ABATARgFgAoE7AZgAI9KAFAewEsA7GSgbxMu+qpbYAaHSgHM4MANyUAvlx4VK/SgE1hYyTLnctvOk1YAKJQA8ki1pQCeASkoBeAHyUDAs8tt3npq9YklZxEA===
//public class Point
//{
// [CompilerGenerated]
// [DebuggerBrowsable(DebuggerBrowsableState.Never)]
// private readonly int <X>k__BackingField;
//
// [CompilerGenerated]
// [DebuggerBrowsable(DebuggerBrowsableState.Never)]
// private readonly int <Y>k__BackingField;
//
// public int X
// {
// [CompilerGenerated]
// get
// {
// return <X>k__BackingField;
// }
// }
//
// public int Y
// {
// [CompilerGenerated]
// get
// {
// return <Y>k__BackingField;
// }
// }
//
// public Point(int x, int y)
// {
// <X>k__BackingField = x;
// <Y>k__BackingField = y;
// }
//}
Expression-style function members allow you to describe members more concisely if the implementation of a member, such as a method or property, is a single expression.
You may be wondering, “Isn’t it happy to be shortened?”
However, “When a lot of short members are lined up in a row, I’m glad that it becomes very short if I write it with members in the form of an expression.”
Please make use of it.