Enhanced C#
Loyc library documentation
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Events Pages
Nested classes | Public static fields | Properties | Public Member Functions | Static Public Member Functions | List of all members
TypeSyncRegistry Class Reference

A registry that maps data types to synchronizers, to support dynamic typing (polymorphic serialization): writing an object whose runtime type is not known statically, and reading it back via the type tag stored in the data stream. More...


Source file:

Remarks

A registry that maps data types to synchronizers, to support dynamic typing (polymorphic serialization): writing an object whose runtime type is not known statically, and reading it back via the type tag stored in the data stream.

This registry knows which synchronizer handles which type, and nothing about tags: the tag <-> type mapping, the TypeTagAttribute convention, and the policy for unknown/mismatched tags all belong to TypeTagRegistry. The Add methods do, however, record the tags they discover in TypeTagRegistry.Default (the instance that is ambient at the time of the call), so that one registration call is enough in the typical case.

Synchronizers are registered by passing an open generic class with a single type parameter constrained to ISyncManager, such as MySynchronizers<SM>. Add(Type, bool) discovers all synchronizers in the class:

Reflection runs only during registration and once per (registry, SyncManager type) pair to build dispatch tables of delegates; there is no reflection and no runtime code generation on the per-object path, which is one dictionary lookup plus a delegate call.

Ambient Service Pattern: the registry used by DynamicSync{SM, T} is Default, an async-local value that can be swapped temporarily:

using (SyncTypeRegistry.SetDefault(myRegistry))
SyncJson.Write(drawing, Drawing.Sync&lt;SyncJson.Writer>);

This also means multiple synchronizers can exist for one type: register each in a different registry and swap (usually together with a paired TypeTagRegistry). Alternatively, pass a specific registry to the SyncDynamicExt.SyncDynamic{SM, T}(SM, FieldId, T, TypeSyncRegistry, TypeTagRegistry?, ObjectMode) overloads to bypass the ambient service entirely. Registration is expected to happen at startup, but late registration is supported and thread-safe.

Public static fields

static TypeSyncRegistry Default => _ambient.Value
 The ambient registry used by DynamicSync{SM, T} and DefaultSynchronizer: the current execution context's override (see SetDefault) if one is active, else GlobalDefault. See AmbientService{T} for how overrides flow across await and why the no-override case costs only a static field read. More...
 

Properties

static TypeSyncRegistry GlobalDefault [get, set]
 Gets or sets the registry used by every execution context that has no ambient override from SetDefault. It can be replaced app-wide. More...
 

Public Member Functions

void Add (Type openGenericClass, bool replaceExisting=false)
 Registers all synchronizers found in an open generic class with a single type parameter constrained to ISyncManager (e.g. Add(typeof(MySynchronizers<>))). See class remarks for the recognized synchronizer shapes. Each synchronizer's tag (per the ambient TypeTagRegistry's convention, normally TypeTagAttribute) is recorded in TypeTagRegistry.Default. More...
 
void Add< T > (string?tag, Type openGenericClass, bool replaceExisting=false)
 Registers the synchronizer for T found in an open generic class, with an explicit tag that is recorded in TypeTagRegistry.Default (pass tag: null for no tag, in which case values of type T can only be read when T is the statically expected type). The explicit tag takes precedence over any TypeTagAttribute. More...
 
void Add< T > (string?tag, SyncObjectFunc< ISyncManager, T > body, bool replaceExisting=false)
 Registers a synchronizer in the form of a delegate that accepts ISyncManager itself. This is the most convenient form, but also the slowest one: each field the delegate synchronizes uses interface dispatch, and if the SyncManager is a struct, it is boxed once per object. For maximum speed, put your synchronizers in a generic class instead and use Add(Type, bool). If tag is null, the ambient TypeTagRegistry's convention (normally a TypeTagAttribute on the delegate's method) is consulted. More...
 
bool Handles (Type type)
 Returns true if this registry has a synchronizer for the given type, or for at least one type derived from it (in which case values of this type may be readable/writable dynamically via type tags). More...
 

Static Public Member Functions

static AmbientService
< TypeSyncRegistry >.Saved 
SetDefault (TypeSyncRegistry newValue)
 Sets the ambient (async-local) default registry. Designed to be used in a using statement, which restores the old value at the end. More...
 

Member Function Documentation

void TypeSyncRegistry.Add ( Type  openGenericClass,
bool  replaceExisting = false 
)
inline

Registers all synchronizers found in an open generic class with a single type parameter constrained to ISyncManager (e.g. Add(typeof(MySynchronizers<>))). See class remarks for the recognized synchronizer shapes. Each synchronizer's tag (per the ambient TypeTagRegistry's convention, normally TypeTagAttribute) is recorded in TypeTagRegistry.Default.

Exceptions
ArgumentExceptionThe class is not an open generic class of the required shape, no synchronizers were found in it, or (when replaceExisting is false) a type or tag was already registered.

References TypeTagRegistry.AttributeTagOf(), and TypeTagRegistry.Default.

void TypeSyncRegistry.Add< T > ( string?  tag,
Type  openGenericClass,
bool  replaceExisting = false 
)
inline

Registers the synchronizer for T found in an open generic class, with an explicit tag that is recorded in TypeTagRegistry.Default (pass tag: null for no tag, in which case values of type T can only be read when T is the statically expected type). The explicit tag takes precedence over any TypeTagAttribute.

void TypeSyncRegistry.Add< T > ( string?  tag,
SyncObjectFunc< ISyncManager, T >  body,
bool  replaceExisting = false 
)
inline

Registers a synchronizer in the form of a delegate that accepts ISyncManager itself. This is the most convenient form, but also the slowest one: each field the delegate synchronizes uses interface dispatch, and if the SyncManager is a struct, it is boxed once per object. For maximum speed, put your synchronizers in a generic class instead and use Add(Type, bool). If tag is null, the ambient TypeTagRegistry's convention (normally a TypeTagAttribute on the delegate's method) is consulted.

References TypeTagRegistry.Default.

bool TypeSyncRegistry.Handles ( Type  type)
inline

Returns true if this registry has a synchronizer for the given type, or for at least one type derived from it (in which case values of this type may be readable/writable dynamically via type tags).

static AmbientService<TypeSyncRegistry>.Saved TypeSyncRegistry.SetDefault ( TypeSyncRegistry  newValue)
static

Sets the ambient (async-local) default registry. Designed to be used in a using statement, which restores the old value at the end.

Member Data Documentation

TypeSyncRegistry TypeSyncRegistry.Default => _ambient.Value
static

The ambient registry used by DynamicSync{SM, T} and DefaultSynchronizer: the current execution context's override (see SetDefault) if one is active, else GlobalDefault. See AmbientService{T} for how overrides flow across await and why the no-override case costs only a static field read.

Property Documentation

TypeSyncRegistry TypeSyncRegistry.GlobalDefault
staticgetset

Gets or sets the registry used by every execution context that has no ambient override from SetDefault. It can be replaced app-wide.