Skip to content

Interface

SyntaxDescription
interface IShape { float Area(); }Declaring and implementing an interface
interface IValue { int Id { get; } }Interface properties
public int Id { get { return 1; } }Implementing a computed property
interface IFast : IGadgetInterface inheritance
struct Box : IShapeImplementation by a value type
record Tag(int N) : ILabelImplementation by a record
class C : TsukimiBehaviour, IGadgetImplementation by a Behaviour
struct Both : IGadget, IResetMultiple implementations by a value type
class C : TsukimiBehaviour, IGadget, IResetMultiple implementations by a Behaviour
IShape s = new Box();Holding in a local variable
void Use(IShape s)Receiving as a parameter
IShape Make()Returning as a return value
IShape[] all = new IShape[2];Storing a value type in an interface array
public IGadget other;Holding in an interface-typed field
other is IResetType-testing against another interface
public IGadget[] all;Holding in an interface-typed array
GetComponent<IGadget>()Getting a component by interface
other != nullComparing with null
other?.Tick()Null-conditional call
s is BoxTesting for a concrete type
s switch { Box b => ..., _ => ... }Branching on a concrete type
s switch { Box b => ..., Dot d => ... }Branching without a default case
where T : IShapeConstraining a type argument by interface
SyntaxDescriptionErrorReasonInstead
int IValue.Id { get { return 1; } }(実装する側)Explicitly implementing an interface propertyTUKI0001by designImplement it as a public property
v.Id(v は Behaviour に付けたインタフェース)Reading a property through an interface attached to a BehaviourTUKI0001by designMake the interface side a method (int Id();) instead (a value type implementation can still read it as a property)
int Twice() { return Value() * 2; }Writing a default implementation on the interfaceTUKI0001by designWrite it in each implementing type
float IShape.Area() { ... }Implementing it explicitlyTUKI0001by designImplement it as an ordinary public method
private IShape held;Holding an interface attached to a value type in a fieldTUKI0001not yetPass it through a local variable or parameter. An interface attached to a Behaviour can be held in a field.
Box b = (Box)s;Casting from an interface to a concrete typeTUKI0001not yetReceive it with a type pattern (s switch { Box b => ... })
IGadget g = new Widget(); g is IResetType-testing an interface holding a value typeTUKI0001not yetReceive it as an interface attached to a Behaviour, or check the concrete type
IShape s = new Box(); s == nullComparing an interface implemented on a value type with nullTUKI0001by designReceive it as an interface attached to a Behaviour (a value type’s value is never null)
IShape s = new Box(); s?.Area()Using ?. on an interface attached to a value typeTUKI0001by designSame as above
An interface attached to a value type can be placed in a local variable, parameter, return value, or array. It cannot be placed in a field.
An interface attached to a Behaviour can be placed in a field or an array, and can be compared with null.

The value of an interface attached to a value type is converted to the same form as a union (sum type). It consists of one number that shows which type is currently held, plus a slot for each type. This is why the forms that don’t compile match the union’s. An interface attached to a Behaviour is not this form. It points to the Behaviour itself.

An interface can only contain method and property declarations. Writing a body is rejected (Forms that don’t compile).

using UnityEngine;
using Tsukimi;
public interface IShape
{
float Area();
}
public struct Box : IShape
{
public float Area() { return 1f; }
}
public class IfaceDeclare : TsukimiBehaviour
{
void Start()
{
IShape s = new Box();
Debug.Log(s.Area()); // => 1
}
}
using UnityEngine;
using Tsukimi;
public interface IValue
{
int Id { get; }
}
public struct Num : IValue
{
public int Id { get; set; }
}
public class IfProperty : TsukimiBehaviour
{
void Start()
{
IValue v = new Num { Id = 7 };
Debug.Log(v.Id); // => 7
}
}

A value type’s implementation can be a property with a body. An explicit implementation doesn’t compile (Forms that don’t compile).

using UnityEngine;
using Tsukimi;
public interface IValue
{
int Id { get; }
}
public struct Num : IValue
{
public int Id { get { return 1; } }
}
public class R_props : TsukimiBehaviour
{
void Start()
{
IValue v = new Num();
Debug.Log(v.Id); // => 1
}
}
using UnityEngine;
using Tsukimi;
public interface IGadget
{
void Tick();
}
public interface IFast : IGadget
{
void Boost();
}
public struct Runner : IFast
{
public void Tick() { Debug.Log(1); } // => 1
public void Boost() { Debug.Log(2); } // => 2
}
public class IfaceInherit : TsukimiBehaviour
{
void Start()
{
IFast f = new Runner();
f.Tick();
f.Boost();
}
}
using UnityEngine;
using Tsukimi;
public interface IShape
{
float Area();
}
public struct Box : IShape
{
public float Area() { return 1f; }
}
public class IfaceStructImplements : TsukimiBehaviour
{
void Start()
{
Box b = new Box();
IShape s = b;
Debug.Log(s.Area()); // => 1
}
}
using UnityEngine;
using Tsukimi;
public interface ILabel
{
int Id();
}
public record Tag(int N) : ILabel
{
public int Id() { return N; }
}
public class IfaceRecordImplements : TsukimiBehaviour
{
void Start()
{
ILabel l = new Tag(1);
Debug.Log(l.Id()); // => 1
}
}

A Behaviour can also implement an interface. this can be assigned to a variable of that interface type.

using UnityEngine;
using Tsukimi;
public interface IGadget
{
void Tick();
}
public class IfaceBehaviourImplements : TsukimiBehaviour, IGadget
{
public void Tick() { Debug.Log(1); }
void Start()
{
IGadget g = this;
g.Tick();
}
}
using UnityEngine;
using Tsukimi;
public interface IGadget
{
void Tick();
}
public interface IReset
{
void Reset();
}
public struct Both : IGadget, IReset
{
public void Tick() { Debug.Log(1); } // => 1
public void Reset() { Debug.Log(2); } // => 2
}
public class IfaceStructMultipleInterfaces : TsukimiBehaviour
{
void Start()
{
Both b = new Both();
IGadget g = b;
IReset r = b;
g.Tick();
r.Reset();
}
}

A Behaviour can also implement multiple interfaces.

using UnityEngine;
using Tsukimi;
public interface IGadget
{
void Tick();
}
public interface IReset
{
void Reset();
}
public class IfaceBehaviourMultipleInterfaces : TsukimiBehaviour, IGadget, IReset
{
public void Tick() { Debug.Log(1); } // => 1
public void Reset() { Debug.Log(2); } // => 2
void Start()
{
Tick();
Reset();
}
}
using UnityEngine;
using Tsukimi;
public interface IShape
{
float Area();
}
public struct Box : IShape
{
public float Area() { return 1f; }
}
public class IfaceLocalVariable : TsukimiBehaviour
{
void Start()
{
IShape s = new Box();
Debug.Log(s.Area()); // => 1
}
}
using UnityEngine;
using Tsukimi;
public interface IShape
{
float Area();
}
public struct Box : IShape
{
public float Area() { return 1f; }
}
public class IfaceParameter : TsukimiBehaviour
{
void Start()
{
Use(new Box());
}
private void Use(IShape s)
{
Debug.Log(s.Area()); // => 1
}
}
using UnityEngine;
using Tsukimi;
public interface IShape
{
float Area();
}
public struct Box : IShape
{
public float Area() { return 1f; }
}
public class IfaceReturnValue : TsukimiBehaviour
{
void Start()
{
IShape s = Make();
Debug.Log(s.Area()); // => 1
}
private IShape Make()
{
return new Box();
}
}

Storing a value type in an interface array

Section titled “Storing a value type in an interface array”
using UnityEngine;
using Tsukimi;
public interface IShape
{
float Area();
}
public struct Box : IShape
{
public float Area() { return 1f; }
}
public class IfaceArrayOfValues : TsukimiBehaviour
{
void Start()
{
IShape[] all = new IShape[2];
all[0] = new Box();
all[1] = new Box();
float total = 0f;
for (int i = 0; i < all.Length; i++)
{
total += all[i].Area();
}
Debug.Log(total); // => 2
}
}

An interface attached to a Behaviour can be held in a field.

using UnityEngine;
using Tsukimi;
public interface IGadget
{
void Tick();
}
public class IfaceBehaviourField : TsukimiBehaviour, IGadget
{
public IGadget other;
public void Tick() { }
void Start()
{
other.Tick();
}
}

An interface attached to a Behaviour can be tested for whether it implements another interface. This doesn’t compile for an interface holding a value type (Forms that don’t compile).

using UnityEngine;
using Tsukimi;
public interface IGadget
{
void Tick();
}
public interface IReset
{
void Reset();
}
public class R_isc : TsukimiBehaviour, IGadget
{
public IGadget other;
public void Tick() { }
void Start()
{
bool b = other is IReset;
Debug.Log(b);
}
}

Drop in multiple Behaviours from the inspector and loop over them in one pass.

using UnityEngine;
using Tsukimi;
public interface IGadget
{
void Tick();
}
public class IfaceBehaviourArray : TsukimiBehaviour, IGadget
{
public IGadget[] all;
public void Tick() { }
void Start()
{
for (int i = 0; i < all.Length; i++)
{
all[i].Tick();
}
}
}

Specify an interface as the type argument to get a Behaviour that implements it.

using UnityEngine;
using Tsukimi;
public interface IGadget
{
void Tick();
}
public class IfaceGetComponent : TsukimiBehaviour, IGadget
{
public void Tick() { }
void Start()
{
IGadget g = GetComponent<IGadget>();
if (g != null)
{
g.Tick();
}
}
}

This works only for an interface attached to a Behaviour.

using UnityEngine;
using Tsukimi;
public interface IGadget
{
void Tick();
}
public class IfaceCompareNull : TsukimiBehaviour, IGadget
{
public IGadget other;
public void Tick() { }
void Start()
{
Debug.Log(other == null); // => true
if (other != null)
{
other.Tick();
}
}
}

?. also works only for an interface attached to a Behaviour.

using UnityEngine;
using Tsukimi;
public interface IGadget
{
void Tick();
}
public class IfaceNullConditional : TsukimiBehaviour, IGadget
{
public IGadget other;
public void Tick() { }
void Start()
{
other?.Tick();
}
}

Tests whether it’s a concrete type. It cannot test whether it implements another interface (Forms that don’t compile).

using UnityEngine;
using Tsukimi;
public interface IShape
{
float Area();
}
public struct Box : IShape
{
public float Area() { return 1f; }
}
public class IfaceIsConcrete : TsukimiBehaviour
{
void Start()
{
IShape s = new Box();
bool isBox = s is Box;
Debug.Log(isBox); // => true
}
}
using UnityEngine;
using Tsukimi;
public interface IShape
{
float Area();
}
public struct Box : IShape
{
public float Area() { return 1f; }
}
public struct Dot : IShape
{
public float Area() { return 0f; }
}
public class IfaceSwitchTypePattern : TsukimiBehaviour
{
void Start()
{
IShape s = new Box();
float a = s switch
{
Box b => b.Area(),
Dot d => d.Area(),
_ => 0f,
};
Debug.Log(a); // => 1
}
}

If the cases cover every implementing type, no default case is needed. This is the same as a union.

using UnityEngine;
using Tsukimi;
public interface IShape
{
float Area();
}
public struct Box : IShape
{
public float Area() { return 1f; }
}
public struct Dot : IShape
{
public float Area() { return 0f; }
}
public class IfaceSwitchExhaustive : TsukimiBehaviour
{
void Start()
{
IShape s = new Box();
float a = s switch
{
Box b => b.Area(),
Dot d => d.Area(),
};
Debug.Log(a); // => 1
}
}
using UnityEngine;
using Tsukimi;
public interface IShape
{
float Area();
}
public struct Box : IShape
{
public float Area() { return 1f; }
}
public class IfaceGenericConstraint : TsukimiBehaviour
{
void Start()
{
Debug.Log(Measure(new Box())); // => 1
}
private float Measure<T>(T shape) where T : IShape
{
return shape.Area();
}
}