Interface
Forms that compile (23)
Section titled “Forms that compile (23)”Forms that don’t compile (9)
Section titled “Forms that don’t compile (9)”| Syntax | Description | Error | Reason | Instead |
|---|---|---|---|---|
int IValue.Id { get { return 1; } }(実装する側) | Explicitly implementing an interface property | TUKI0001 | by design | Implement it as a public property |
v.Id(v は Behaviour に付けたインタフェース) | Reading a property through an interface attached to a Behaviour | TUKI0001 | by design | Make 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 interface | TUKI0001 | by design | Write it in each implementing type |
float IShape.Area() { ... } | Implementing it explicitly | TUKI0001 | by design | Implement it as an ordinary public method |
private IShape held; | Holding an interface attached to a value type in a field | TUKI0001 | not yet | Pass 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 type | TUKI0001 | not yet | Receive it with a type pattern (s switch { Box b => ... }) |
IGadget g = new Widget(); g is IReset | Type-testing an interface holding a value type | TUKI0001 | not yet | Receive it as an interface attached to a Behaviour, or check the concrete type |
IShape s = new Box(); s == null | Comparing an interface implemented on a value type with null | TUKI0001 | by design | Receive 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 type | TUKI0001 | by design | Same 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.
Declaring interfaces
Section titled “Declaring interfaces”Declaring and implementing an interface
Section titled “Declaring and implementing an interface”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 }}Interface properties
Section titled “Interface properties”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 }}Implementing a computed property
Section titled “Implementing a computed property”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 }}Interface inheritance
Section titled “Interface inheritance”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(); }}Implementing interfaces
Section titled “Implementing interfaces”Implementation by a value type
Section titled “Implementation by a value type”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 }}Implementation by a record
Section titled “Implementation by a record”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 }}Implementation by a Behaviour
Section titled “Implementation by a Behaviour”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(); }}Multiple implementations by a value type
Section titled “Multiple implementations by a value type”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(); }}Multiple implementations by a Behaviour
Section titled “Multiple implementations by a Behaviour”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(); }}Holding as a value
Section titled “Holding as a value”Holding in a local variable
Section titled “Holding in a local variable”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 }}Receiving as a parameter
Section titled “Receiving as a parameter”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 }}Returning as a return value
Section titled “Returning as a return value”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 }}Holding in an interface-typed field
Section titled “Holding in an interface-typed field”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(); }}Type-testing against another interface
Section titled “Type-testing against another interface”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); }}Holding in an interface-typed array
Section titled “Holding in an interface-typed array”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(); } }}Getting and null
Section titled “Getting and null”Getting a component by interface
Section titled “Getting a component by interface”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(); } }}Comparing with null
Section titled “Comparing with null”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(); } }}Null-conditional call
Section titled “Null-conditional call”?. 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(); }}Branching by type
Section titled “Branching by type”Testing for a concrete type
Section titled “Testing for a concrete type”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 }}Branching on a concrete type
Section titled “Branching on a concrete type”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 }}Branching without a default case
Section titled “Branching without a default case”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 }}Generics
Section titled “Generics”Constraining a type argument by interface
Section titled “Constraining a type argument by interface”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(); }}