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...
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:
-
public static methods of the form
T Sync(SM sync, T? value) (the SyncObjectFunc{SM,T} shape, i.e. an object "body" that reads/writes fields but does not call BeginSubObject itself), and
-
implementations of ISyncObject{SM, T}.
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))
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.
|
| 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...
|
| |