Introduction
IWriterBase is root interface for writers consumers.
/// <summary>Root interface for writers.</summary>
public interface IWriter
{
/// <summary>The most specific type this writer reads from.</summary>
Type FromType { get; }
/// <summary>The most specific type this writer writes to.</summary>
Type ToType { get; }
}
/// <summary>Indicates that writer can write from <typeparamref name="From"/>.</summary>
public interface IWriterFrom<From> : IWriter { }
/// <summary>Indicates that Writer can write into <typeparamref name="To"/>.</summary>
public interface IWriterTo<To> : IWriter { }
/// <summary>Indicates that implements one of <see cref="IWriter{From, To}"/>, <see cref="IWriterFromRef{From, To}"/>, <see cref="IWriterToRef{From, To}"/> or <see cref="IWriterFromRefToRef{From, To}"/>.</summary>
public interface IWriterBase<From, To> : IWriterFrom<From>, IWriterTo<To> { }
/// <summary>Writes <typeparamref name="From"/> data to <typeparamref name="To"/>.</summary>
/// <typeparam name="From">Value to write</typeparam>
/// <typeparam name="To">Record/Container</typeparam>
public interface IWriter<From, To> : IWriterBase<From, To>
{
/// <summary>Can write.</summary>
/// <remarks>This property is needed for decoration layering.</remarks>
bool CanWrite { get; }
/// <summary>Try write <paramref name="from"/> to container in <paramref name="to"/>.</summary>
/// <param name="from">source where content is read from.</param>
/// <param name="to">target where write is written to. Structs and value types cannot be written to.</param>
/// <returns>True if was write completed, false if write was not completed or supported.</returns>
/// <exception cref="Exception">Unexpected error.</exception>
bool TryWrite(From from, To to, IServiceProvider? context = null);
}
/// <summary>Writes <typeparamref name="From"/> data to <typeparamref name="To"/>.</summary>
/// <typeparam name="From">Value to write</typeparam>
/// <typeparam name="To">Record/Container</typeparam>
public interface IWriterFromRef<From, To> : IWriterBase<From, To>
{
/// <summary>Can write</summary>
/// <remarks>This property is needed for decoration layering.</remarks>
bool CanWrite { get; }
/// <summary>Try write <paramref name="from"/> to container in <paramref name="to"/>.</summary>
/// <param name="from">source where content is read from.</param>
/// <param name="to">target lass where content is written to. Structs and value types cannot be written to.</param>
/// <returns>True if was write completed, false if write was not completed or supported.</returns>
/// <exception cref="Exception">Unexpected error.</exception>
bool TryWrite(ref From from, To to, IServiceProvider? context = null);
}
/// <summary>Writes <typeparamref name="From"/> data to <typeparamref name="To"/>.</summary>
/// <typeparam name="From">Value to write</typeparam>
/// <typeparam name="To">Record/Container</typeparam>
public interface IWriterToRef<From, To> : IWriterBase<From, To>
{
/// <summary>Can write</summary>
/// <remarks>This property is needed for decoration layering.</remarks>
bool CanWrite { get; }
/// <summary>Try write <paramref name="from"/> to container in <paramref name="to"/> or create new container if not available.</summary>
/// <param name="from">source where content is read from.</param>
/// <param name="to">target where write is written to. If 'null' then writer may create new container.</param>
/// <returns>True if was write completed, false if write was not completed or supported.</returns>
/// <exception cref="Exception">Unexpected error.</exception>
bool TryWrite(From from, ref To to, IServiceProvider? context = null);
}
/// <summary>Writes <typeparamref name="From"/> data to <typeparamref name="To"/>.</summary>
/// <typeparam name="From">Value to write</typeparam>
/// <typeparam name="To">Record/Container</typeparam>
public interface IWriterFromRefToRef<From, To> : IWriterBase<From, To>
{
/// <summary>Can write</summary>
/// <remarks>This property is needed for decoration layering.</remarks>
bool CanWrite { get; }
/// <summary>Try write <paramref name="from"/> to container in <paramref name="to"/> or create new container if not available.</summary>
/// <param name="from">source where content is read from.</param>
/// <param name="to">target where write is written to. If 'null' then writer may create new container.</param>
/// <returns>True if was write completed, false if write was not completed or supported.</returns>
/// <exception cref="Exception">Unexpected error.</exception>
bool TryWrite(ref From from, ref To to, IServiceProvider? context = null);
}
There are different ref versions of writer interface for different implementations.
IWriterBase └── IWriterBase<From, To> ├── IWriter<From, To> ├── IWriterFromRef<From, To> ├── IWriterToRef<From, To> └── IWriterFromRefToRef<From, To>
The consumer of writer can use IWriterBase and extension methods that forward to correct implementation.
IWriter writer = new MyClassLabelWriter();
writer.WriteAs("Hello", instance);
IWriterBase<From, To> is another abstract interface for consumers.
IWriterBase<string, MyClass> writer = new MyClassLabelWriter();
writer.Write("Hello", instance);
Writer
Writer to class implements IWriter<From, To>.
IWriter<string, MyClass> writer = new MyClassLabelWriter();
Writer<From, To> base class can be used for implementations.
public class MyClassLabelWriter : Writer<string, MyClass>
{
public override bool TryWrite(string from, MyClass to, IServiceProvider? context = null)
{
if (to == null) return false;
to.Label = from;
return true;
}
}
Writer to struct implements IWriterToRef<From, To>.
IWriterToRef<string, MyStruct> writer = new MyStructLabelWriter();
public class MyStructLabelWriter : WriterToRef<string, MyStruct>
{
public override bool TryWrite(string from, ref MyStruct to, IServiceProvider? context = null)
{
to.Label = from;
return true;
}
}
Writer can be adapted from delegate.
IWriter<string, MyClass> writer =
DelegateWriter.Writer<string, MyClass>((string value, MyClass instance) => instance.Label = value);
And for structs IWriterToRef<From, To>.
IWriterToRef<string, MyStruct> writer =
DelegateWriter.Writer<string, MyStruct>((string value, ref MyStruct instance) => instance.Label = value);
Extension method .Write() forwards to .TryWrite() and throws StatusException with BadWriteNotSupported if return value is false.
// Create instance
MyClass instance = new MyClass { Label = "ABC" };
// Write
writer.Write("ZYX", instance);
// Create instance
MyStruct instance = new MyStruct { Label = "ABC" };
// Write
writer.Write("ZYX", ref instance);
If writer is IWriterBase then extension method .WriteAs()<From, To> can be used to forward to .TryWrite().
MyClass instance = new MyClass { Label = "ABC" };
IWriter writer = new MyClassLabelWriter();
writer.WriteAs<string, MyClass>("ZYX", instance);
Reader
Reader implements IWriterToRef<From, To> for class.
IWriterToRef<MyClass, string> reader = new MyClassLabelReader();
WriterToRef<From, To> base class can be used for convenient implementations.
public class MyClassLabelReader : WriterToRef<MyClass, string>
{
public override bool TryWrite(MyClass from, ref string to, IServiceProvider? context = null)
{
if (from == null) return false;
to = from.Label;
return true;
}
}
Reader can be adapted from delegate.
IWriterToRef<MyClass, string> reader =
DelegateWriter.Reader<MyClass, string>((MyClass @class) => @class.Label);
Extension method .Write() can be used for reading, though pointer to output must be provided with ref.
// Create instance
MyClass instance = new MyClass { Label = "ABC" };
// Write to here
string label = null!;
// Write from instance to stack frame
reader.Write(instance, ref label);
Extension method .Read() forwards to TryWrite().
// Write from instance to stack frame
label = reader.Read(instance);
If writer is IWriterBase then extension method .ReadAs()<From, To> forwars to the correct .TryWrite.
IWriter reader = new MyClassLabelReader();
string label = reader.ReadAs<MyClass, string>(instance);
Visitor
IWriterVisitor is the interface for writer visitor implementations. Invoked from IWriterVisitable.Accept(IWriterVisitor).
/// <summary>Visitable writer</summary>
public interface IWriterVisitable : IWriter
{
/// <summary>Try accept <paramref name="visitor"/>. Visits all <see cref="IWriter{From, To}"/> types that are implemented.</summary>
/// <returns>Return true if all visitors were visited (all returned true).</returns>
bool Accept(IWriterVisitor visitor);
}
/// <summary>Writer visitor</summary>
public interface IWriterVisitor
{
/// <summary>Try visit <paramref name="writerT"/>.</summary>
/// <returns>true to continue visitation</returns>
bool Visit<From, To>(IWriter<From, To> writerT);
/// <summary>Try visit <paramref name="writerTFR"/>.</summary>
/// <returns>true to continue visitation</returns>
bool Visit<From, To>(IWriterFromRef<From, To> writerTFR);
/// <summary>Try visit <paramref name="writerTTR"/>.</summary>
/// <returns>true to continue visitation</returns>
bool Visit<From, To>(IWriterToRef<From, To> writerTTR);
/// <summary>Try visit <paramref name="writerTFRTR"/>.</summary>
/// <returns>true to continue visitation</returns>
bool Visit<From, To>(IWriterFromRefToRef<From, To> writerTFRTR);
/// <summary>Try visit <paramref name="refererT"/>.</summary>
/// <returns>true to continue visitation</returns>
bool Visit<From, To>(IReferer<From, To> refererT) where From : notnull;
/// <summary>Try visit <paramref name="refererTFR"/>.</summary>
/// <returns>true to continue visitation</returns>
bool Visit<From, To>(IRefererFromRef<From, To> refererTFR) where From : notnull;
}
Writer that implements IWriterBase accepts visitors.
IWriter reader = new MyClassLabelReader();
IWriterVisitor visitor = new MyVisitor();
(reader as IWriterVisitable)!.Accept(visitor);
public class MyVisitor : IWriterVisitor
{
public bool Visit<From, To>(IWriter<From, To> writerT)
{ WriteLine($"IWriter<{typeof(From).Name},{typeof(To).Name}>"); return true; }
public bool Visit<From, To>(IWriterFromRef<From, To> writerTFR)
{ WriteLine($"IWriter<{typeof(From).Name},{typeof(To).Name}>"); return true; }
public bool Visit<From, To>(IWriterToRef<From, To> writerTTR)
{ WriteLine($"IWriter<{typeof(From).Name},{typeof(To).Name}>"); return true; }
public bool Visit<From, To>(IWriterFromRefToRef<From, To> writerTFRTR)
{ WriteLine($"IWriter<{typeof(From).Name},{typeof(To).Name}>"); return true; }
public bool Visit<From, To>(IReferer<From, To> refererT) where From : notnull
{ WriteLine($"IReferer<{typeof(From).Name},{typeof(To).Name}>"); return true; }
public bool Visit<From, To>(IRefererFromRef<From, To> refererTFR) where From : notnull
{ WriteLine($"IRefererFromRef<{typeof(From).Name},{typeof(To).Name}>"); return true; }
}
The extension method .GetWriterToAndFromTypes() gets implemented from and to types.
List<KeyValuePair<Type, Type>> types = reader.GetWriterToAndFromTypes();
As do .GetWriterInterfaces().
List<Type> types = reader.GetWriterInterfaces();
And .GetWriterRefAndTypes().
List<(bool fromRef, Type fromType, bool toRef, Type toType)> types = reader.GetWriterRefAndTypes();
IWriterComposition
IWriterComposition interface exposes inner components for decorator implementations.
/// <summary>Exposes writer internal components.</summary>
public interface IWriterComposition : IWriter, IComposition<IWriter> { }
.PrintTree() extension method prints inner structure as tree.
IWriter reader = new MyClassLabelReader();
reader = WriterAdapter.Adapt(reader);
string tree = reader.PrintTree();
.VisitTree() extension method visits writer structure.
IWriter reader = new MyClassLabelReader();
reader = WriterAdapter.Adapt(reader);
foreach (var line in reader.VisitTree())
WriteLine(line);
Full Example
Full example
using System;
using System.Collections.Generic;
using Avalanche.Service;
using Avalanche.Writer;
using static System.Console;
public class writer
{
public static void Run()
{
{
MyClass instance = new MyClass();
// <01>
IWriter writer = new MyClassLabelWriter();
writer.WriteAs("Hello", instance);
// </01>
}
{
MyClass instance = new MyClass();
// <02>
IWriterBase<string, MyClass> writer = new MyClassLabelWriter();
writer.Write("Hello", instance);
// </02>
}
{
// <W01>
IWriter<string, MyClass> writer =
DelegateWriter.Writer<string, MyClass>((string value, MyClass instance) => instance.Label = value);
// </W01>
}
{
// <W01B>
IWriterToRef<string, MyStruct> writer =
DelegateWriter.Writer<string, MyStruct>((string value, ref MyStruct instance) => instance.Label = value);
// </W01B>
}
{
// <W02>
IWriter<string, MyClass> writer = new MyClassLabelWriter();
// </W02>
}
{
// <W02B>
IWriterToRef<string, MyStruct> writer = new MyStructLabelWriter();
// </W02B>
}
{
IWriter<string, MyClass> writer = new MyClassLabelWriter();
// <W04>
// Create instance
MyClass instance = new MyClass { Label = "ABC" };
// Write
writer.Write("ZYX", instance);
// </W04>
}
{
IWriterToRef<string, MyStruct> writer = new MyStructLabelWriter();
// <W04B>
// Create instance
MyStruct instance = new MyStruct { Label = "ABC" };
// Write
writer.Write("ZYX", ref instance);
// </W04B>
}
{
// <W05>
MyClass instance = new MyClass { Label = "ABC" };
IWriter writer = new MyClassLabelWriter();
writer.WriteAs<string, MyClass>("ZYX", instance);
// </W05>
}
{
// <R01>
IWriterToRef<MyClass, string> reader =
DelegateWriter.Reader<MyClass, string>((MyClass @class) => @class.Label);
// </R01>
}
{
// <R02>
IWriterToRef<MyClass, string> reader = new MyClassLabelReader();
// </R02>
}
{
IWriterToRef<MyClass, string> reader = new MyClassLabelReader();
// <R04>
// Create instance
MyClass instance = new MyClass { Label = "ABC" };
// Write to here
string label = null!;
// Write from instance to stack frame
reader.Write(instance, ref label);
// </R04>
// <R05>
// Write from instance to stack frame
label = reader.Read(instance);
// </R05>
}
{
MyClass instance = new MyClass { Label = "ABC" };
// <R06>
IWriter reader = new MyClassLabelReader();
string label = reader.ReadAs<MyClass, string>(instance);
// </R06>
}
{
// <V00>
IWriter reader = new MyClassLabelReader();
IWriterVisitor visitor = new MyVisitor();
(reader as IWriterVisitable)!.Accept(visitor);
// </V00>
}
{
IWriter reader = new MyClassLabelReader();
// <V01>
List<KeyValuePair<Type, Type>> types = reader.GetWriterToAndFromTypes();
// </V01>
}
{
IWriter reader = new MyClassLabelReader();
// <V02>
List<Type> types = reader.GetWriterInterfaces();
// </V02>
}
{
IWriter reader = new MyClassLabelReader();
// <V03>
List<(bool fromRef, Type fromType, bool toRef, Type toType)> types = reader.GetWriterRefAndTypes();
// </V03>
}
{
// <C04>
IWriter reader = new MyClassLabelReader();
reader = WriterAdapter.Adapt(reader);
string tree = reader.PrintTree();
// </C04>
WriteLine(tree);
}
{
// <C05>
IWriter reader = new MyClassLabelReader();
reader = WriterAdapter.Adapt(reader);
foreach (var line in reader.VisitTree())
WriteLine(line);
// </C05>
}
}
// <00>
public class MyClass
{
public string Label = null!;
}
public struct MyStruct
{
public string Label;
}
// </00>
// <R03>
public class MyClassLabelReader : WriterToRef<MyClass, string>
{
public override bool TryWrite(MyClass from, ref string to, IServiceProvider? context = null)
{
if (from == null) return false;
to = from.Label;
return true;
}
}
// </R03>
// <W03>
public class MyClassLabelWriter : Writer<string, MyClass>
{
public override bool TryWrite(string from, MyClass to, IServiceProvider? context = null)
{
if (to == null) return false;
to.Label = from;
return true;
}
}
// </W03>
// <W03B>
public class MyStructLabelWriter : WriterToRef<string, MyStruct>
{
public override bool TryWrite(string from, ref MyStruct to, IServiceProvider? context = null)
{
to.Label = from;
return true;
}
}
// </W03B>
// <V06>
public class MyVisitor : IWriterVisitor
{
public bool Visit<From, To>(IWriter<From, To> writerT)
{ WriteLine($"IWriter<{typeof(From).Name},{typeof(To).Name}>"); return true; }
public bool Visit<From, To>(IWriterFromRef<From, To> writerTFR)
{ WriteLine($"IWriter<{typeof(From).Name},{typeof(To).Name}>"); return true; }
public bool Visit<From, To>(IWriterToRef<From, To> writerTTR)
{ WriteLine($"IWriter<{typeof(From).Name},{typeof(To).Name}>"); return true; }
public bool Visit<From, To>(IWriterFromRefToRef<From, To> writerTFRTR)
{ WriteLine($"IWriter<{typeof(From).Name},{typeof(To).Name}>"); return true; }
public bool Visit<From, To>(IReferer<From, To> refererT) where From : notnull
{ WriteLine($"IReferer<{typeof(From).Name},{typeof(To).Name}>"); return true; }
public bool Visit<From, To>(IRefererFromRef<From, To> refererTFR) where From : notnull
{ WriteLine($"IRefererFromRef<{typeof(From).Name},{typeof(To).Name}>"); return true; }
}
// </V06>
}