What Are Optional Init Arguments?
By default Init(args) assumes that every Init argument a client receives is required. If an argument is missing, and the Null Argument Guard is enabled, you get warnings shown in the Inspector, logged to the Console in Edit Mode and exceptions thrown at runtime.

Sometimes, however, you might want to create a client that can work perfectly fine without receiving some of its dependencies from the outside through its Init method. For example, a component might log messages using a logger injected through its Init method when provided one, but fallback to using a default logger implementation if none has been provided.

You can let Init(args) know about such optional Init arguments by marking them as nullable.
Marking Init Arguments Optional
There are a couple of different ways to let Init(args) know that an Init argument is optional. They all have the same effect, so you can use whichever one you prefer.
Nullable Reference Types
If you have nullable reference types enabled, you can make the Init parameter have a nullable type:
#nullable enable
using Sisus.Init;
class Enemy : MonoBehaviour<IPlayer, ILogger>
{
IPlayer player = null!;
ILogger logger = null!;
protected override void Init(IPlayer player, ILogger? logger)
{
this.player = player;
this.logger = logger ?? new DefaultLogger();
}
}
[AllowNull] Attribute
Add the [AllowNull] attribute to the parameter:
using System.Diagnostics.CodeAnalysis;
using Sisus.Init;
class Enemy : MonoBehaviour<IPlayer, ILogger>
{
IPlayer player;
ILogger logger;
protected override void Init(IPlayer player, [AllowNull] ILogger logger)
{
this.player = player;
this.logger = logger ?? new DefaultLogger();
}
}
Default Value Of Null
Give the parameter in the Init method a default value of null:
using Sisus.Init;
class Enemy : MonoBehaviour<IPlayer, ILogger>
{
IPlayer player;
ILogger logger;
protected override void Init(IPlayer player, ILogger logger = null)
{
this.player = player;
this.logger = logger;
}
}
Initializer Init Class
If you have an Initializer with a nested Init class, you can make the field that corresponds with the Init argument have a nullable type or add the [AllowNull] or [MaybeNull] attribute to it to mark it as optional:
#nullable enable
using Sisus.Init;
class EnemyInitializer : CustomInitializer<Enemy, IPlayer, ILogger>
{
class Init
{
IPlayer Player = default;
ILogger? Logger = default;
}
protected override void InitTarget(Enemy enemy, IPlayer player, ILogger? logger)
{
((IInitializable<IPlayer, ILogger>)enemy).Init(player, logger ?? new DefaultLogger());
}
}
This can be useful when assigning the argument is optional when the client is configured using this particular initializer, but not necessarily in other situations.

What Changes?
Inspector
When an optional Init argument is missing, no warning is shown about it in the Inspector, even when Null Argument Guard is enabled.
Edit Mode
When an optional Init argument is missing, no warning is logged to the Console in Edit Mode, even when Null Argument Guard is enabled.
Runtime
When the client receives null for an optional argument, no InvalidInitArgumentsException is thrown. This is the case regardless of whether the arguments are passed to the client by an Initializer, or manually in code:
gameObject.AddComponent<Enemy, IPlayer, ILogger>(player, null);
Async Initialization
When an optional Init argument is missing when the client is loaded, the client does not wait in a disabled state for it to become available, even if it has been configured to wait for services using [Init(WaitForServices = true)].
Handling Optional Dependencies
Since an optional argument can be null, the client must be ready to handle that situation.
One option is to check for null every time before using the dependency:
void Attack()
{
player.TakeDamage(damage);
logger?.Log($"{name} attacked the player.");
}
Another option is to replace the missing dependency with a fallback instance during the client’s initialization:
protected override void Init(IPlayer player, ILogger? logger = null)
{
this.player = player;
this.logger = logger ?? new NullLogger();
}
Default Values
If an Init parameter is given a default value other than null, that value is used as the initial value of the argument when an Initializer is attached to the client:
class Enemy : MonoBehaviour<IPlayer, float>
{
protected override void Init(IPlayer player, float moveSpeed = 5f)
{
...
}
}
A field initializer in the nested Init class of a custom Initializer can be used for the same purpose, and takes precedence over the Init parameter’s default value when the initializer in question is attached to the client:
class EnemyInitializer : Initializer<Enemy, IPlayer, float>
{
class Init
{
IPlayer Player = default;
float MoveSpeed = 10f;
}
}
Optional Init Arguments vs Optional Initialization
Marking an Init argument optional only affects that one argument; the Null Argument Guard can still keep protecting all the other arguments of the client.
If you want to allow a component to be loaded without it receiving any dependencies in its Init method at all, you can use [Init(Enabled = false] instead. See [Init] attribute for more information.