I'm on a quest to write a TypedBinaryReader that would be able to read any type that BinaryReader normally supports, and a type that implements a specific interface. I have come really close, but I'm not quite there yet.
For the value types, I mapped the types to functors that call the appropriate functions.
For the reference types, as long as they inherit the interface I specified and can be constructed, the function below works.
However, I want to create an universal generic method call, ReadUniversal<T>() that would work for both value types and the above specified reference types.
This is attempt number one, it works, but It's not generic enought, I still have to cases.
public class TypedBinaryReader : BinaryReader {
private readonly Dictionary<Type, object> functorBindings;
public TypedBinaryReader(Stream input) : this(input, Encoding.UTF8, false) { }
public TypedBinaryReader(Stream input, Encoding encoding) : this(input, encoding, false) { }
public TypedBinaryReader(Stream input, Encoding encoding, bool leaveOpen) : base(input, encoding, leaveOpen) {
functorBindings = new Dictionary<Type, object>() {
{typeof(byte), new Func<byte>(ReadByte)},
{typeof(int), new Func<int>(ReadInt32)},
{typeof(short), new Func<short>(ReadInt16)},
{typeof(long), new Func<long>(ReadInt64)},
{typeof(sbyte), new Func<sbyte>(ReadSByte)},
{typeof(uint), new Func<uint>(ReadUInt32)},
{typeof(ushort), new Func<ushort>(ReadUInt16)},
{typeof(ulong), new Func<ulong>(ReadUInt64)},
{typeof(bool), new Func<bool>(ReadBoolean)},
{typeof(float), new Func<float>(ReadSingle)}
};
}
public T ReadValueType<T>() {
return ((Func<T>)functorBindings[typeof(T)])();
}
public T ReadReferenceType<T>() where T : MyReadableInterface, new() {
T item = new T();
item.Read(this);
return item;
}
public List<T> ReadMultipleValuesList<T, R>() {
dynamic size = ReadValueType<R>();
List<T> list = new List<T>(size);
for (dynamic i = 0; i < size; ++i) {
list.Add(ReadValueType<T>());
}
return list;
}
public List<T> ReadMultipleObjecsList<T, R>() where T : MyReadableInterface {
dynamic size = ReadValueType<R>();
List<T> list = new List<T>(size);
for (dynamic i = 0; i < size; ++i) {
list.Add(ReadReferenceType<T>());
}
return list;
}
}
An idea that I came up with, that I don't really like, is to write generic class that boxes in the value types, like this one:
public class Value<T> : MyReadableInterface {
private T value;
public Value(T value) {
this.value = value;
}
internal Value(TypedBinaryReader reader) {
Read(reader);
}
public T Get() {
return value;
}
public void Set(T value) {
if (!this.value.Equals(value)) {
this.value = value;
}
}
public override string ToString() {
return value.ToString();
}
public void Read(TypedBinaryReader reader) {
value = reader.ReadValueType<T>();
}
}
This way, I can use ReadReferencTypes<T>() even on value types, as long as I pass the type parameter as Value<int> instead of just int.
But this is still ugly since I again have to remember what I'm reading, just instead of having to remember function signature, I have to remember to box in the value types.
Ideal solution would be when I could add a following method to TypedBinaryReader class:
public T ReadUniversal<T>() {
if ((T).IsSubclassOf(typeof(MyReadableInterface)) {
return ReadReferenceType<T>();
} else if (functorBindings.ContainsKey(typeof(T)) {
return ReadValueType<T>();
} else {
throw new SomeException();
}
}
However, due to different constraints on the generic argument T, this won't work. Any ideas on how to make it work?
Ultimate goal is to read any type that BinaryReader normally can or any type that implements the interface, using only a single method.