加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 实现泛型方法处理不同整数类型集合的正确方法是什么?

发布时间:2020-12-15 21:52:46 所属栏目:百科 来源:网络整理
导读:我正在编写一种特殊的System.IO.BinaryWriter.本作者应该能够处理整数类型,包括枚举,以及这些类型的集合. abstract class MyBinaryWriter{ // ... #region Methods: Basic Types: Writing public abstract void Write(byte value); public abstract void Wri
我正在编写一种特殊的System.IO.BinaryWriter.本作者应该能够处理整数类型,包括枚举,以及这些类型的集合.

abstract class MyBinaryWriter
{
    // ...

    #region Methods: Basic Types: Writing
    public abstract void Write(byte value);
    public abstract void Write(ushort value);
    public abstract void Write(uint value);
    public abstract void Write(ulong value);
    public abstract void Write(string value); 
    #endregion

    #region Methods: Complex Types: Writing
    public virtual void Write<T>(ICollection<T> collection)
    {
        // first write the 32-bit-unsigned-length prefix
        if (collection == null || collection.Count == 0)
        {
            Write((uint)0);
        }
        else
        {
            Write((uint)collection.Count);

            // then write the elements,if any
            foreach (var item in collection)
                ; // What here? Obviously Write(item) doesn't work...
        }
    }

    // ...
}

处理这个问题的最佳方法是什么?使用泛型比使用每个整数类型和我希望处理的每个枚举类型编写重载更好的解决方案?接下来是一个可能的解决方案,但我不太喜欢并且存在潜在的性能问题.

#region Methods: Complex Types: Writing
    public virtual void Write<T>(ICollection<T> collection) where T : IConvertible
    {
        // first write the 32-bit-unsigned-length prefix
        if (collection == null || collection.Count == 0)
        {
            Write((uint)0);
        }
        else
        {
            Write((uint)collection.Count);

            // get the method for writing an element
            Action<T> write = null;
            var type = typeof(T);
            if (type.IsEnum)
                type = Enum.GetUnderlyingType(type);

            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Byte:
                case TypeCode.SByte:
                    write = (x => Write((byte)(IConvertible)x.ToByte(null)));
                    break;

                case TypeCode.Int16:
                case TypeCode.UInt16:
                    write = (x => Write((ushort)(IConvertible)x.ToUInt16(null)));
                    break;

                case TypeCode.Int32:
                case TypeCode.UInt32:
                    write = (x => Write((uint)(IConvertible)x.ToUInt32(null)));
                    break;

                case TypeCode.Int64:
                case TypeCode.UInt64:
                    write = (x => Write((ulong)(IConvertible)x.ToUInt64(null)));
                    break;

                default:
                    Debug.Fail("Only supported for integral types.");
                    break;
            }

            // then write the elements,if any
            foreach (var item in collection)
                write(item);
        }
    }

解决方法

一种方法是使用编译的表达式:

// helper classes which compiles a fast,type-safe delegate for writing various types
static class MyBinaryWriterHelper<T> {
    public static readonly Action<MyBinaryWriter,T> WriteAction;

    // this initialization is a bit expensive,but it will occur only once
    // for each writable type T and will occur lazily
    static {
        // find the existing Write(T) on the MyBinaryWriter type
        var writeMethod = typeof(MyBinaryWriter).GetMethods()
            .FirstOrDefault(m => m.Name == "Write" 
                && m.GetArguments().Length == 1
                && m.GetArguments()[0](p => p.ParameterType == typeof(T)
        );

        // if there is no such method,fail
        if (writeMethod == null) { throw ... }

        // build up an expression (writer,t) => writer.Write(t)
        var writerParam = Expression.Parameter(typeof(MyBinaryWriter));
        var tParam = Expression.Parameter(typeof(T));
        var call = Expression.Call(writerParam,writeMethod,tParam);
        var lambda = Expression.Lambda<Action<MyBinaryWriter,T>>(call,new[] { writerParam,tParam });

        // compile the expression to a delegate,caching the result statically in the
        // readonly WriteAction field
        WriteAction = lambda.Compile();
    }
}

// then in your writer class
public void Write<T>(IEnumerable<T> collection) {
    // other collection writing logic (e. g. writing the count) ...

    // to write out the items,just use the static action field
    foreach (var t in collection) {
        MyBinaryWriterHelper<T>.WriteAction(this,t);
    }
}

虽然没有办法使用泛型来强制类型为“数字”,但您可以使用IConvertible(如示例代码中)作为松散约束来实现此目的,以增加额外的编译时安全性.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读