首页 > 解决方案 > 在泛型参数上编写具有两组可能约束的泛型方法

问题描述

我正在寻求编写一个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 通常可以读取的任何类型或实现接口的任何类型。

标签: c#genericsgeneric-programming

解决方案


如果您需要一个处理引用类型的方法和一个处理值类型的方法,那是拥有两种方法的完全正当理由。

从调用此类方法的代码的角度来看,这可能会有所帮助。从他们的角度来看,如果他们可以只调用一种方法而不必为值类型调用一种方法,而为值类型调用另一种方法,他们是否会受益?可能不是。

发生的事情(我已经做了很多很多次了)是我们陷入了我们希望某个类的外观或行为方式与我们正在尝试编写的实际软件无关的原因. 以我的经验,当我们尝试编写泛型类时,这种情况经常发生。当我们在使用的类型无关紧要的情况下看到不必要的代码重复时,泛型类可以帮助我们(例如,如果我们有一个类用于整数列表,另一个类用于双精度列表等)

然后,当我们开始实际使用我们创建的类时,我们可能会发现我们的需求并不像我们想象的那样,而我们花在完善该泛型类上的时间就白费了。

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

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


推荐阅读