Class InputControlExtensions
Various extension methods for InputControl. Mostly low-level routines.
Namespace: UnityEngine.InputSystem
Syntax
public static class InputControlExtensions : object
Methods
AccumulateValueInEvent(InputControl<Single>, Void*, InputEventPtr)
Modify newState
to write an accumulated value of the control
rather than the value currently found in the event.
Declaration
public static void AccumulateValueInEvent(this InputControl<float> control, void *currentStatePtr, InputEventPtr newState)
Parameters
Type | Name | Description |
---|---|---|
InputControl<Single> | control | Control to perform the accumulation on. |
Void* | currentStatePtr | Memory containing the control's current state. See currentStatePtr. |
InputEventPtr | newState | Event containing the new state about to be written to the device. |
Remarks
This method reads the current, unprocessed value of the control from currentStatePtr
and then adds it to the value of the control found in newState
.
Note that the method does nothing if a value for the control is not contained in newState
.
This can be the case, for example, for DeltaStateEvents.
See Also
CheckStateIsAtDefault(InputControl)
Check whether the memory of the given control is in its default state.
Declaration
public static bool CheckStateIsAtDefault(this InputControl control)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | An input control on a device that's been added to the system (see added). |
Returns
Type | Description |
---|---|
Boolean | True if the state memory of the given control corresponds to the control's default. |
Remarks
This method is a cheaper check than actually reading out the value from the control and checking whether it is the same value as the default value. The method bypasses all value reading and simply performs a trivial memory comparison of the control's current state memory to the default state memory stored for the control.
Note that the default state for the memory of a control does not necessary need to be all zeroes. For example, a stick axis may be stored as an unsigned 8-bit value with the memory state corresponding to a 0 value being 127.
See Also
CheckStateIsAtDefault(InputControl, Void*, Void*)
Check if the given state corresponds to the default state of the control.
Declaration
public static bool CheckStateIsAtDefault(this InputControl control, void *statePtr, void *maskPtr = default(void *))
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | Control to check the state for in |
Void* | statePtr | Pointer to a state buffer containing the stateBlock for |
Void* | maskPtr | If not null, only bits set to |
Returns
Type | Description |
---|---|
Boolean | True if the control/device is in its default state. |
Remarks
Note that default does not equate all zeroes. Stick axes, for example, that are stored as unsigned byte values will have their resting position at 127 and not at 0. This is why we explicitly store default state in a memory buffer instead of assuming zeroes.
See Also
CheckStateIsAtDefaultIgnoringNoise(InputControl)
Declaration
public static bool CheckStateIsAtDefaultIgnoringNoise(this InputControl control)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control |
Returns
Type | Description |
---|---|
Boolean |
CheckStateIsAtDefaultIgnoringNoise(InputControl, Void*)
Check if the given state corresponds to the default state of the control or has different state only for parts marked as noise.
Declaration
public static bool CheckStateIsAtDefaultIgnoringNoise(this InputControl control, void *statePtr)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | Control to check the state for in |
Void* | statePtr | Pointer to a state buffer containing the stateBlock for |
Returns
Type | Description |
---|---|
Boolean | True if the control/device is in its default state (ignoring any bits marked as noise). |
Remarks
Note that default does not equate all zeroes. Stick axes, for example, that are stored as unsigned byte values will have their resting position at 127 and not at 0. This is why we explicitly store default state in a memory buffer instead of assuming zeroes.
See Also
CompareState(InputControl, Void*, Void*)
Declaration
public static bool CompareState(this InputControl control, void *statePtr, void *maskPtr = default(void *))
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | |
Void* | statePtr | |
Void* | maskPtr |
Returns
Type | Description |
---|---|
Boolean |
CompareState(InputControl, Void*, Void*, Void*)
Compare the control's stored state in firstStatePtr
to secondStatePtr
.
Declaration
public static bool CompareState(this InputControl control, void *firstStatePtr, void *secondStatePtr, void *maskPtr = default(void *))
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | |
Void* | firstStatePtr | Memory containing the control's stateBlock. |
Void* | secondStatePtr | Memory containing the control's stateBlock |
Void* | maskPtr | Optional mask. If supplied, it will be used to mask the comparison between
|
Returns
Type | Description |
---|---|
Boolean | True if the state is equivalent in both memory buffers. |
Remarks
Unlike CompareValue(Void*, Void*), this method only compares raw memory state. If used on a stick, for example, it may mean that this method returns false for two stick values that would compare equal using CompareValue(Void*, Void*) (e.g. if both stick values fall below the deadzone).
See Also
CompareStateIgnoringNoise(InputControl, Void*)
Compare the control's current state to the state stored in statePtr
.
Declaration
public static bool CompareStateIgnoringNoise(this InputControl control, void *statePtr)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | |
Void* | statePtr | State memory containing the control's stateBlock. |
Returns
Type | Description |
---|---|
Boolean | True if |
Remarks
This method ignores noise
This method will not actually read values but will instead compare state directly as it is stored in memory. ReadValue() is not invoked and thus processors will not be run.
See Also
CopyState(InputDevice, Void*, Int32)
Copy the state of the device to the given memory buffer.
Declaration
public static void CopyState(this InputDevice device, void *buffer, int bufferSizeInBytes)
Parameters
Type | Name | Description |
---|---|---|
InputDevice | device | An input device. |
Void* | buffer | Buffer to copy the state of the device to. |
Int32 | bufferSizeInBytes | Size of |
Remarks
The method will copy however much fits into the given buffer. This means that if the state of the device is larger than what fits into the buffer, not all of the device's state is copied.
See Also
CopyState<TState>(InputDevice, out TState)
Copy the state of the device to the given struct.
Declaration
public static void CopyState<TState>(this InputDevice device, out TState state)
where TState : struct, IInputStateTypeInfo
Parameters
Type | Name | Description |
---|---|---|
InputDevice | device | An input device. |
TState | state | Struct to copy the state of the device into. |
Type Parameters
Name | Description |
---|---|
TState | A state struct type such as MouseState. |
Remarks
This method will copy memory verbatim into the memory of the given struct. It will copy whatever memory of the device fits into the given struct.
See Also
EnumerateChangedControls(InputEventPtr, InputDevice, Single)
Go through all controls in the given eventPtr
that have changed value.
Declaration
public static InputControlExtensions.InputEventControlCollection EnumerateChangedControls(this InputEventPtr eventPtr, InputDevice device = null, float magnitudeThreshold = null)
Parameters
Type | Name | Description |
---|---|---|
InputEventPtr | eventPtr | An input event. Must be a StateEvent or DeltaStateEvent. |
InputDevice | device | Input device from which to enumerate controls. If this is |
Single | magnitudeThreshold | If not zero, minimum actuation threshold (see EvaluateMagnitude()) that
a control must reach (as per value in the given |
Returns
Type | Description |
---|---|
InputControlExtensions.InputEventControlCollection | An enumerator for the controls that have changed values in |
Remarks
This method is a shorthand for calling EnumerateControls(InputEventPtr, InputControlExtensions.Enumerate, InputDevice, Single) with IgnoreControlsInCurrentState.
// Detect button presses.
InputSystem.onEvent +=
(eventPtr, device) =>
{
// Ignore anything that is not a state event.
var eventType = eventPtr.type;
if (eventType != StateEvent.Type && eventType != DeltaStateEvent.Type)
return;
// Find all changed controls actuated above the button press threshold.
foreach (var control in eventPtr.EnumerateChangedControls
(device: device, magnitudeThreshold: InputSystem.settings.defaultButtonPressThreshold))
if (control is ButtonControl button)
Debug.Log($"Button {button} was pressed");
}
See Also
EnumerateControls(InputEventPtr, InputControlExtensions.Enumerate, InputDevice, Single)
Go through the controls that have values in eventPtr
, apply the given filters, and return each
control one by one.
Declaration
public static InputControlExtensions.InputEventControlCollection EnumerateControls(this InputEventPtr eventPtr, InputControlExtensions.Enumerate flags, InputDevice device = null, float magnitudeThreshold = null)
Parameters
Type | Name | Description |
---|---|---|
InputEventPtr | eventPtr | An input event. Must be a StateEvent or DeltaStateEvent. |
InputControlExtensions.Enumerate | flags | Filter settings that determine which controls to return. |
InputDevice | device | Input device from which to enumerate controls. If this is |
Single | magnitudeThreshold | If not zero, minimum actuation threshold (see EvaluateMagnitude()) that
a control must reach (as per value in the given |
Returns
Type | Description |
---|---|
InputControlExtensions.InputEventControlCollection | An enumerator for the controls with values in |
Remarks
This method is much more efficient than manually iterating over the controls of device
and locating
the ones that have changed in eventPtr
. See InputControlExtensions.InputEventControlEnumerator for details.
This method will not allocate GC memory and can safely be used with foreach
loops.
See Also
FindControlsRecursive<TControl>(InputControl, IList<TControl>, Func<TControl, Boolean>)
Declaration
public static void FindControlsRecursive<TControl>(this InputControl parent, IList<TControl> controls, Func<TControl, bool> predicate)
where TControl : InputControl
Parameters
Type | Name | Description |
---|---|---|
InputControl | parent | |
IList<TControl> | controls | |
Func<TControl, Boolean> | predicate |
Type Parameters
Name | Description |
---|---|
TControl |
FindInParentChain<TControl>(InputControl)
Find a control of the given type in control hierarchy of control
.
Declaration
public static TControl FindInParentChain<TControl>(this InputControl control)
where TControl : InputControl
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | Control whose parents to inspect. |
Returns
Type | Description |
---|---|
TControl |
Type Parameters
Name | Description |
---|---|
TControl | Type of control to look for. Actual control type can be subtype of this. |
Remarks
The found control of type TControl
which may be either
control
itself or one of its parents. If no such control was found,
returns null
.
GetAllButtonPresses(InputEventPtr, Single, Boolean)
Enumerate all pressed buttons in the given event.
Declaration
public static IEnumerable<InputControl> GetAllButtonPresses(this InputEventPtr eventPtr, float magnitude = null, bool buttonControlsOnly = true)
Parameters
Type | Name | Description |
---|---|---|
InputEventPtr | eventPtr | An event. Must be a StateEvent or DeltaStateEvent. |
Single | magnitude | |
Boolean | buttonControlsOnly |
Returns
Type | Description |
---|---|
IEnumerable<InputControl> | An enumerable collection containing all buttons that were pressed in the given event. |
See Also
GetFirstButtonPressOrNull(InputEventPtr, Single, Boolean)
Get the first pressed button from the given event or null if the event doesn't contain a new button press.
Declaration
public static InputControl GetFirstButtonPressOrNull(this InputEventPtr eventPtr, float magnitude = null, bool buttonControlsOnly = true)
Parameters
Type | Name | Description |
---|---|---|
InputEventPtr | eventPtr | |
Single | magnitude | |
Boolean | buttonControlsOnly |
Returns
Type | Description |
---|---|
InputControl | The control that was pressed. |
Remarks
Buttons will be evaluated in the order that they appear in the devices layout i.e. the bit position of each control in the devices state memory. For example, in the gamepad state, button north (bit position 4) will be evaluated before button east (bit position 5), so if both buttons were pressed in the given event, button north would be returned.
See Also
GetStatePtrFromStateEvent(InputControl, InputEventPtr)
Given a StateEvent or DeltaStateEvent, return the raw memory pointer that can
be used, for example, with ReadValueFromState(Void*) to read out the value of control
contained in the event.
Declaration
public static void *GetStatePtrFromStateEvent(this InputControl control, InputEventPtr eventPtr)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | Control to access state for in the given state event. |
InputEventPtr | eventPtr | A StateEvent or DeltaStateEvent containing input state. |
Returns
Type | Description |
---|---|
Void* | A pointer that can be used with methods such as ReadValueFromState(Void*) or |
Remarks
Note that the given state event must have the same state format (see format) as the device
to which control
belongs. If this is not the case, the method will invariably return null
.
In practice, this means that the method cannot be used with touch events or, in general, with events sent to devices that implement IInputStateCallbackReceiver (which Touchscreen does) except if the event is in the same state format as the device. Touch events will generally be sent as state events containing only the state of a single TouchControl, not the state of the entire Touchscreen.
HasButtonPress(InputEventPtr, Single, Boolean)
Return true if the given eventPtr
has any
Declaration
public static bool HasButtonPress(this InputEventPtr eventPtr, float magnitude = null, bool buttonControlsOnly = true)
Parameters
Type | Name | Description |
---|---|---|
InputEventPtr | eventPtr | |
Single | magnitude | |
Boolean | buttonControlsOnly |
Returns
Type | Description |
---|---|
Boolean |
See Also
HasValueChangeInEvent(InputControl, InputEventPtr)
Return true if control
has a different value (from its current one) in
eventPtr
.
Declaration
public static bool HasValueChangeInEvent(this InputControl control, InputEventPtr eventPtr)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | An input control. |
InputEventPtr | eventPtr | An input event. Must be a StateEvent or DeltaStateEvent. |
Returns
Type | Description |
---|---|
Boolean | True if |
HasValueChangeInState(InputControl, Void*)
Return true if the current value of control
is different to the one found
in statePtr
.
Declaration
public static bool HasValueChangeInState(this InputControl control, void *statePtr)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | Control whose state to compare to what is stored in |
Void* | statePtr | A block of input state memory containing the stateBlock
of |
Returns
Type | Description |
---|---|
Boolean | True if the value of |
IsActuated(InputControl, Single)
Return true if the given control is actuated.
Declaration
public static bool IsActuated(this InputControl control, float threshold = null)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | |
Single | threshold | Magnitude threshold that the control must match or exceed to be considered actuated. An exception to this is the default value of zero. If threshold is zero, the control must have a magnitude greater than zero. |
Returns
Type | Description |
---|---|
Boolean |
Remarks
Actuation is defined as a control having a magnitude (EvaluateMagnitude() greater than zero or, if the control does not support magnitudes, has been moved from its default state.
In practice, this means that when actuated, a control will produce a value other than its default value.
IsPressed(InputControl, Single)
Check whether the given control is considered pressed according to the button press threshold.
Declaration
public static bool IsPressed(this InputControl control, float buttonPressPoint = null)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | Control to check. |
Single | buttonPressPoint | Optional custom button press point. If not supplied, defaultButtonPressPoint is used. |
Returns
Type | Description |
---|---|
Boolean | True if the actuation of the given control is high enough for it to be considered pressed. |
Remarks
This method checks the actuation level of the control as IsActuated(InputControl, Single) does. For ButtonControls
and other float
value controls, this will effectively check whether the float value of the control exceeds the button
point threshold. Note that if the control is an axis that can be both positive and negative, the press threshold works in
both directions, i.e. it can be crossed both in the positive direction and in the negative direction.
See Also
QueueValueChange<TValue>(InputControl<TValue>, TValue, Double)
Queue a value change on the given control
which will be processed and take effect
in the next input update.
Declaration
public static void QueueValueChange<TValue>(this InputControl<TValue> control, TValue value, double time = null)
where TValue : struct
Parameters
Type | Name | Description |
---|---|---|
InputControl<TValue> | control | Control to change the value of. |
TValue | value | New value for the control. |
Double | time | Optional time at which the value change should take effect. If set, this will become the time of the queued event. If the time is in the future and the update mode is set to ProcessEventsInFixedUpdate, the event will not be processed until it falls within the time of an input update slice. Otherwise, the event will invariably be consumed in the next input update (see Update()). |
Type Parameters
Name | Description |
---|---|
TValue | Type of value. |
ReadDefaultValueAsObject(InputControl)
Read the control's default value and return it as an object.
Declaration
public static object ReadDefaultValueAsObject(this InputControl control)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | Control to read default value from. |
Returns
Type | Description |
---|---|
Object |
Remarks
This method allocates GC memory and should thus not be used during normal gameplay.
See Also
ReadUnprocessedValueFromEvent<TValue>(InputControl<TValue>, InputEventPtr)
Declaration
public static TValue ReadUnprocessedValueFromEvent<TValue>(this InputControl<TValue> control, InputEventPtr eventPtr)
where TValue : struct
Parameters
Type | Name | Description |
---|---|---|
InputControl<TValue> | control | |
InputEventPtr | eventPtr |
Returns
Type | Description |
---|---|
TValue |
Type Parameters
Name | Description |
---|---|
TValue |
ReadUnprocessedValueFromEvent<TValue>(InputControl<TValue>, InputEventPtr, out TValue)
Declaration
public static bool ReadUnprocessedValueFromEvent<TValue>(this InputControl<TValue> control, InputEventPtr inputEvent, out TValue value)
where TValue : struct
Parameters
Type | Name | Description |
---|---|---|
InputControl<TValue> | control | |
InputEventPtr | inputEvent | |
TValue | value |
Returns
Type | Description |
---|---|
Boolean |
Type Parameters
Name | Description |
---|---|
TValue |
ReadValueAsObject(InputControl)
Read the current value of the control and return it as an object.
Declaration
public static object ReadValueAsObject(this InputControl control)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control |
Returns
Type | Description |
---|---|
Object |
Remarks
This method allocates GC memory and thus may cause garbage collection when used during gameplay.
Use ReadValueIntoBuffer(InputControl, Void*, Int32) to read values generically without having to know the specific value type of a control.
See Also
ReadValueFromEvent<TValue>(InputControl<TValue>, InputEventPtr)
Read the value for the given control from the given event.
Declaration
public static TValue ReadValueFromEvent<TValue>(this InputControl<TValue> control, InputEventPtr inputEvent)
where TValue : struct
Parameters
Type | Name | Description |
---|---|---|
InputControl<TValue> | control | Control to read value for. |
InputEventPtr | inputEvent | Event to read value from. Must be a StateEvent or DeltaStateEvent. |
Returns
Type | Description |
---|---|
TValue | The value for the given control as read out from the given event or |
Type Parameters
Name | Description |
---|---|
TValue | Type of value to read. |
ReadValueFromEvent<TValue>(InputControl<TValue>, InputEventPtr, out TValue)
Check if the given event contains a value for the given control and if so, read the value.
Declaration
public static bool ReadValueFromEvent<TValue>(this InputControl<TValue> control, InputEventPtr inputEvent, out TValue value)
where TValue : struct
Parameters
Type | Name | Description |
---|---|---|
InputControl<TValue> | control | Control to read value for. |
InputEventPtr | inputEvent | Input event. This must be a StateEvent or DeltaStateEvent.
Note that in the case of a DeltaStateEvent, the control may not actually be part of the event. In this
case, the method returns false and stores |
TValue | value | Variable that receives the control value. |
Returns
Type | Description |
---|---|
Boolean | True if the value has been successfully read from the event, false otherwise. |
Type Parameters
Name | Description |
---|---|
TValue | Type of value to read. |
See Also
ReadValueFromEventAsObject(InputControl, InputEventPtr)
Read the value of control
from the given inputEvent
without having to
know the specific value type of the control.
Declaration
public static object ReadValueFromEventAsObject(this InputControl control, InputEventPtr inputEvent)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | Control to read the value for. |
InputEventPtr | inputEvent | An StateEvent or DeltaStateEvent to read the value from. |
Returns
Type | Description |
---|---|
Object | The current value for the control or |
See Also
ReadValueIntoBuffer(InputControl, Void*, Int32)
Read the current, processed value of the control and store it into the given memory buffer.
Declaration
public static void ReadValueIntoBuffer(this InputControl control, void *buffer, int bufferSize)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | |
Void* | buffer | Buffer to store value in. Note that the value is not stored with the offset found in byteOffset of the control's stateBlock. It will be stored directly at the given address. |
Int32 | bufferSize | Size of the memory available at |
See Also
ResetToDefaultStateInEvent(InputControl, InputEventPtr)
Writes the default state of control
into eventPtr
.
Declaration
public static bool ResetToDefaultStateInEvent(this InputControl control, InputEventPtr eventPtr)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | A control whose default state to write. |
InputEventPtr | eventPtr | A valid pointer to either a StateEvent or DeltaStateEvent. |
Returns
Type | Description |
---|---|
Boolean | True if the default state for |
Remarks
Note that the default state of a control does not necessarily need to correspond to zero-initialized memory. For example, if an axis control yields a range of [-1..1] and is stored as a signed 8-bit value, the default state will be 127, not 0.
// Reset the left gamepad stick to its default state (which results in a
// value of (0,0).
using (StateEvent.From(Gamepad.all[0], out var eventPtr))
{
Gamepad.all[0].leftStick.ResetToDefaultStateInEvent(eventPtr);
InputSystem.QueueEvent(eventPtr);
}
See Also
WriteValueFromObjectIntoEvent(InputControl, InputEventPtr, Object)
Declaration
public static void WriteValueFromObjectIntoEvent(this InputControl control, InputEventPtr eventPtr, object value)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | |
InputEventPtr | eventPtr | |
Object | value |
WriteValueIntoEvent<TValue>(InputControl, TValue, InputEventPtr)
Declaration
public static void WriteValueIntoEvent<TValue>(this InputControl control, TValue value, InputEventPtr eventPtr)
where TValue : struct
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | |
TValue | value | |
InputEventPtr | eventPtr |
Type Parameters
Name | Description |
---|---|
TValue |
WriteValueIntoEvent<TValue>(InputControl<TValue>, TValue, InputEventPtr)
Declaration
public static void WriteValueIntoEvent<TValue>(this InputControl<TValue> control, TValue value, InputEventPtr eventPtr)
where TValue : struct
Parameters
Type | Name | Description |
---|---|---|
InputControl<TValue> | control | |
TValue | value | |
InputEventPtr | eventPtr |
Type Parameters
Name | Description |
---|---|
TValue |
WriteValueIntoState(InputControl, Void*)
Write the control's current value into statePtr
.
Declaration
public static void WriteValueIntoState(this InputControl control, void *statePtr)
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | Control to read the current value from and to store state for in |
Void* | statePtr | State to receive the control's value in its respective stateBlock. |
Remarks
This method is equivalent to WriteValueIntoState(TValue, Void*) except that one does not have to know the value type of the given control.
See Also
WriteValueIntoState<TValue>(InputControl, TValue, Void*)
Declaration
public static void WriteValueIntoState<TValue>(this InputControl control, TValue value, void *statePtr)
where TValue : struct
Parameters
Type | Name | Description |
---|---|---|
InputControl | control | |
TValue | value | |
Void* | statePtr |
Type Parameters
Name | Description |
---|---|
TValue |
WriteValueIntoState<TValue>(InputControl<TValue>, TValue, Void*)
Declaration
public static void WriteValueIntoState<TValue>(this InputControl<TValue> control, TValue value, void *statePtr)
where TValue : struct
Parameters
Type | Name | Description |
---|---|---|
InputControl<TValue> | control | |
TValue | value | |
Void* | statePtr |
Type Parameters
Name | Description |
---|---|
TValue |
WriteValueIntoState<TValue>(InputControl<TValue>, Void*)
Declaration
public static void WriteValueIntoState<TValue>(this InputControl<TValue> control, void *statePtr)
where TValue : struct
Parameters
Type | Name | Description |
---|---|---|
InputControl<TValue> | control | |
Void* | statePtr |
Type Parameters
Name | Description |
---|---|
TValue |
WriteValueIntoState<TValue, TState>(InputControl<TValue>, TValue, ref TState)
Declaration
public static void WriteValueIntoState<TValue, TState>(this InputControl<TValue> control, TValue value, ref TState state)
where TValue : struct where TState : struct, IInputStateTypeInfo
Parameters
Type | Name | Description |
---|---|---|
InputControl<TValue> | control | |
TValue | value | Value for |
TState | state |
Type Parameters
Name | Description |
---|---|
TValue | |
TState |