编写一个泛型方法,在泛型参数上有两组可能的约束



我正在寻找一个TypedBinaryReader,它能够读取BinaryReader通常支持的任何类型,以及实现特定接口的类型。我已经非常接近了,但我还没有完全达到。

对于值类型,我将这些类型映射到调用相应函数的函子。

对于引用类型,只要它们继承了我指定的接口并且可以构造,下面的函数就可以工作。

但是,我想创建一个通用的泛型方法调用ReadUniversal<T>(),它既适用于值类型,也适用于上面指定的引用类型。

这是第一次尝试,它有效,但它不够通用,我还有很多案例。

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;
}
}

我提出了一个我并不喜欢的想法,那就是编写一个在值类型中装箱的泛型类,比如这个:

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>();
}
}

这样,我甚至可以在值类型上使用ReadReferencTypes<T>(),只要我将类型参数作为Value<int>而不仅仅是int传递即可。

但这仍然很难看,因为我必须再次记住我所读的内容,而不是必须记住函数签名,我必须记住在值类型中加框。

理想的解决方案是,我可以向TypedBinaryReader类添加以下方法:

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();
}
}

然而,由于对泛型参数T的不同约束,这将不起作用。关于如何让它发挥作用有什么想法吗?

最终目标是只使用一个方法读取BinaryReader通常可以读取的任何类型或实现接口的任何类型。

如果您需要一个处理引用类型的方法和一个处理值类型的方法,那么有两个方法是完全合理的。

从将调用此类中方法的代码的角度来看这一点可能会有所帮助。从他们的角度来看,如果他们可以只调用一个方法而不管类型如何,而不必为值类型调用一个和为值类型另一个方法,他们会受益吗?可能不会。

发生的事情(我已经做了很多次了)是,由于与我们试图编写的实际软件无关的原因,我们陷入了希望某个类的外观或行为的困境。根据我的经验,当我们试图编写泛型类时,这种情况经常发生。当我们在处理的类型无关紧要的情况下看到不必要的代码重复时,泛型类会帮助我们(比如我们有一个用于int列表的类,另一个用于double列表的类等)

然后,当我们使用创建的类来实际时,我们可能会发现我们的需求与我们想象的不完全一样,并且我们花在打磨泛型类上的时间被浪费了。

如果我们正在处理的类型确实需要完全不同的代码,那么将多个不相关的类型强制处理到一个通用方法中会使代码更加复杂。(每当我们觉得必须使用dynamic时,这是一个好迹象,表明某些事情可能变得过于复杂。)

我的建议只是编写您需要的代码,如果您需要调用不同的方法,请不要担心。看看它是否真的造成了问题。它可能不会。在问题出现之前不要试图解决它。

最新更新