Skip to main content

Attributes

By defining the interface that corresponds to the WIT in C# and giving the appropriate attributes, the Source Generator that comes with WaaS automatically generates convenient code for binding.

info

If you use wit2waas, attributes are automatically applied, so manual attribute assignment is not required. Please refer to Use Generated Code.

Declare an interface

To generate bindings corresponding to WIT interfaces and worlds, declare a partial interface in C# and apply the [ComponentInterface] attribute.

using WaaS.ComponentModel.Binding;

[ComponentInterface(@"env")]
public partial interface IEnv
{
/* Define the interface members here */
}

For types and functions included in this interface, define them as members of this interface. Depending on the type of member to be defined, you need to apply the corresponding attribute.

Declare a function

To define a function included in the interface, define a method as a member of the interface and apply the [ComponentApi] attribute.

using WaaS.ComponentModel.Binding;
using System.Threading.Tasks;

[ComponentInterface(@"env")]
public partial interface IEnv
{
[ComponentApi(@"show-message")]
ValueTask ShowMessage(string @message);
}
  • The argument of [ComponentApi] must be the function name of WIT.
  • The return type can be an awaitable type such as ValueTask.
    • It is awaited by the caller and the result is obtained.
  • For other types that can be used as arguments or return values, refer to Types that can be used in functions.
warning

When calling an awaitable function, ExecutionContext.Invoke() cannot be used. Use ExecutionContext.InvokeAsync().

Types that can be used in a function

Component Model TypeC# Type
stringstring
boolbool
s8sbyte
u8byte
s16short
u16ushort
s32int
u32uint
s64long
u64ulong
f32float
f64double
charWaaS.ComponentModel.Binding.ComponentChar1
recordDefining a record
variantDefining a variant
list<T>System.ReadOnlyMemory<T>
tuple<...>System.ValueTuple<...> or System.Tuple<...>
flagsDefining flags
enumDefining an enum
options<T>Using options
result<T, E>WaaS.ComponentModel.Binding.Result<TOk, TError>
own<T>Defining resource types
borrow<T>Defining resource types

Defining a record

To define a record, declare a record in C# and apply the [ComponentRecord] attribute.

using WaaS.ComponentModel.Binding;

[ComponentInterface(@"env")]
public partial interface IEnv
{
[ComponentRecord]
public readonly partial struct Message
{
[ComponentField]
public (string, Sprite?) Speaker { get; private init; }
[ComponentField]
public string Text { get; private init; }
[ComponentField]
public MessageAttributes Attributes { get; private init; }
}
}
  • Define a partial class or struct as a member of the interface with [ComponentInterface] applied, and apply the [ComponentRecord] attribute.
  • Apply the [ComponentField] attribute to the property.
  • Apply the init or set accessor to the property. Accessibility doesn't matter.
  • The property type uses the corresponding C# type for the Component Model type. The available types are the same as Types that can be used in a function.

Defining a variant

To define a variant, declare a variant in C# and apply the [ComponentVariant] attribute.

#enable nullable
using WaaS.ComponentModel.Binding;

[ComponentInterface(@"env")]
public partial interface IEnv
{
[ComponentVariant]
public readonly partial struct Sprite
{
[ComponentCase]
public MyGame.MySequencer.IEnv.WellKnownSprites? WellKnown { get; private init; }
[ComponentCase]
public string? Addressable { get; private init; }
}
}
  • Defne a partial class or struct as a member of the interface with [ComponentInterface] applied, and apply the [ComponentVariant] attribute.
  • Apply the [ComponentCase] attribute to the property.
  • Apply the init or set accessor to the property. Accessibility doesn't matter.
  • The property type uses the corresponding C# type for the Component Model type. The available types are the same as Types that can be used in a function.
    • In addition, it is recommended to make the property type nullable.

Generated members

Members are generated to access the variant information.

// <auto-generated />
partial interface IEnv
{
partial struct Sprite
{
public VariantCase Case { get; private init; }

public enum VariantCase
{
WellKnown,
Addressable,
}
}
}

By using the Case property, you can get the type of the variant.

return sprite.Case switch
{
IEnv.Sprite.VariantCase.WellKnown => GetWellKnownSprite(sprite.WellKnown),
IEnv.Sprite.VariantCase.Addressable => GetAddressableSprite(sprite.Addressable),
_ => throw new IndexOutOfRangeException()
};

Defining flags

To define flags, declare an enum in C# and apply the [ComponentFlags] attribute.

using WaaS.ComponentModel.Binding;
using System;

[ComponentInterface(@"env")]
public partial interface IEnv
{
[Flags]
public enum MessageAttributes : byte
{
Fast = 1 << 0,
Large = 1 << 1,
Slow = 1 << 2,
Auto = 1 << 3,
}
}
  • Define an enum as a member of the interface with [ComponentInterface] applied, and apply the System.Flags attribute.
  • Use an integer type from byte to uint as the underlying type depending on the number of flags.
    • Use byte if there are 8 or fewer flags.
    • Use ushort if there are 16 or fewer flags.
    • Use uint for other cases.
info

With WaaS, you cannot define a flags type with more than 32 flags.

Defining an enum

To define an enum, declare an enum in C#.

using WaaS.ComponentModel.Binding;
using System;

[ComponentInterface(@"env")]
public partial interface IEnv
{
public enum WellKnownSprites
{
Unknown,
Hero,
Rival,
Professor,
}
}
  • Define an enum as a member of the interface with [ComponentInterface] applied.
  • The System.Flags attribute cannot be applied.
warning

In the Component Model enum, you cannot specify the values of the enumeration constants. If you specify the values of the enumeration constants on the C# side, they are sorted in ascending order as unsigned integer representations, and indexes are assigned in order.

public enum WellKnownSprites : uint
{
Unknown = 100, // '1' in Component Model
Hero = 200, // '2' in Component Model
Rival = 0, // '0' in Component Model
Professor = 1000, // '3' in Component Model
}

Using options

To represent option<T> in Component Model, use WaaS.ComponentModel.Binding.Option<T>.

However, T? can also be used in the following cases.

  • When the type T is a value type.
  • When it is used directly as the type of a member of a record or variant.
    • Except when used as a type argument, such as an element of a tuple or list.

Defining resource types

When a resource type is defined in an interface with the [ComponentInterface] attribute, the following members are required in the C# interface.

using WaaS.ComponentModel.Binding;
using WaaS.ComponentModel.Runtime;
using System;

[ComponentInterface(@"env")]
public partial interface IEnv
{
[ComponentResource(@"some-resource-name")]
public partial interface ISomeResourceImpl : IResourceImpl
{
/* ... */
}
}

  • Define a partial interface as a member of the interface with the [ComponentInterface] attribute applied.
  • The interface inherits IResourceImpl.

When using a resource type as a parameter or return value, use Owned<ISomeResourceImpl> to own it, and Borrowed<ISomeResourceImpl> to borrow it.

When Owned<ISomeResourceImpl> is passed to C# code as an argument, you must handle ownership properly.

Defining resource type members

public partial interface ISomeResourceImpl : IResourceImpl
{
[ComponentApi("[constructor]some-resource-name")]
ValueTask<Owned<ISomeResourceImpl>> SomeResource(/* ... */);

[ComponentApi("[method]some-resource-name.some-method")]
ValueTask</* return type */> SomeMethod(Borrowed<ISomeResourceImpl> self, /* ... */);

[ComponentApi("[static]some-resource-name.some-static-method")]
ValueTask</* return type */> SomeStaticMethod(/* ... */);
}
  • Define member methods with the [ComponentApi] attribute applied to the resource type interface. The basic rules are the same as usual, but differ in the following points.
    • Specify the following names in the argument of [ComponentApi].
      • Constructor: [constructor]resource name
      • Method: [method]resource name.method name
      • Static method: [static]resource name.method name
    • For the constructor, the return type is Owned<resource implementation type> or an awaitable type that results in it.
    • For instance methods, specify Borrowed<resource implementation type> as the first argument.
    • For details, refer to the WIT specification.

Footnotes

  1. Component Model's character type is a Unicode Scalar Value and cannot be used with C#'s char.