C# 什么';实现处理不同整型集合的泛型方法的正确方法是什么?

C# 什么';实现处理不同整型集合的泛型方法的正确方法是什么?,c#,generics,collections,value-type,C#,Generics,Collections,Value Type,我正在写一种特殊的System.IO.BinaryWriter。此编写器应该能够处理整数类型,包括Enum,以及这些类型的集合 abstract class MyBinaryWriter { // ... #region Methods: Basic Types: Writing public abstract void Write(byte value); public abstract void Write(ushort value); public

我正在写一种特殊的
System.IO.BinaryWriter
。此编写器应该能够处理整数类型,包括
Enum
,以及这些类型的集合

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

    // ...
}
抽象类MyBinaryWriter
{
// ...
#区域方法:基本类型:书写
公共摘要无效写入(字节值);
公开摘要无效写入(ushort值);
公开摘要无效写入(uint值);
公开摘要无效书写(ulong值);
公共摘要空写(字符串值);
#端区
#区域方法:复杂类型:编写
公共虚拟无效写入(ICollection集合)
{
//首先写入32位无符号长度前缀
if(collection==null | | collection.Count==0)
{
写入((uint)0);
}
其他的
{
写入((uint)collection.Count);
//然后写下元素,如果有的话
foreach(集合中的var项)
;//这里是什么?显然写(项)不起作用。。。
}
}
// ...
}
处理这个问题的最佳方法是什么?使用泛型有比为我希望处理的每个整型类型和每个枚举类型编写重载更好的解决方案吗?一个可能的解决方案如下,但我不喜欢这么多,并有潜在的性能问题

    #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);
        }
    }
#区域方法:复杂类型:编写
公共虚拟空写入(ICollection集合),其中T:IConvertible
{
//首先写入32位无符号长度前缀
if(collection==null | | collection.Count==0)
{
写入((uint)0);
}
其他的
{
写入((uint)collection.Count);
//获取写入元素的方法
动作写入=null;
var类型=类型(T);
if(type.IsEnum)
type=Enum.getUnderlineType(类型);
开关(类型.GetTypeCode(类型))
{
大小写类型代码。字节:
案例类型代码.SByte:
write=(x=>write((字节)(IConvertible)x.ToByte(null));
打破
case TypeCode.Int16:
案例类型代码.UInt16:
write=(x=>write((ushort)(IConvertible)x.ToUInt16(null));
打破
case TypeCode.Int32:
案例类型代码.UInt32:
write=(x=>write((uint)(IConvertible)x.ToUInt32(null));
打破
case TypeCode.Int64:
案例类型代码.UInt64:
write=(x=>write((ulong)(IConvertible)x.ToUInt64(null));
打破
违约:
Debug.Fail(“仅支持整数类型”);
打破
}
//然后写下元素,如果有的话
foreach(集合中的var项)
写(项);
}
}

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

// 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);
    }
}
//helper类,它编译一个快速、类型安全的委托,用于编写各种类型
静态类MyBinaryWriterHelper{
公共静态只读操作WriteAction;
//这种初始化有点昂贵,但只会发生一次
//对于每个可写类型T和将延迟发生
静止的{
//查找MyBinaryWriter类型上的现有写入(T)
var writeMethod=typeof(MyBinaryWriter).GetMethods()
.FirstOrDefault(m=>m.Name==“写入”
&&m.GetArguments().Length==1
&&m.GetArguments()[0](p=>p.ParameterType==typeof(T)
);
//如果没有这样的方法,则失败
如果(writeMethod==null){throw…}
//建立一个表达式(writer,t)=>writer.Write(t)
var writerParam=Expression.Parameter(typeof(MyBinaryWriter));
var tParam=表达式参数(typeof(T));
var call=Expression.call(writeParam、writeMethod、tParam);
var lambda=Expression.lambda(调用,new[]{writeParam,tParam});
//将表达式编译为委托,在
//只读写操作字段
WriteAction=lambda.Compile();
}
}
//然后在你的写作课上
公共无效写入(IEnumerable集合){
//其他集合写入逻辑(例如写入计数)。。。
//要写出项目,只需使用static action字段
foreach(集合中的变量t){
MyBinaryWriterHelper.WriteAction(this,t);
}
}

虽然无法使用泛型强制类型为“数值”,但您可以使用IConvertible(如示例代码中所示)作为松散约束,以增加额外的编译时安全性。

我认为最好使用
动态

public void Write<T>(ICollection<T> collection) {
    dynamic self = this;
    foreach (var value in collection) {
        self.Write(value);
    }
}
公共作废写入(ICollection集合){
动态自我=此;
foreach(集合中的var值){
自写(值);
}
}

动态调用的性能。简而言之,它的性能相当于使用编译表达式树。

这是一篇非常棒的文章!值得注意的是,在另一篇文章的基准测试中,编译表达式树的速度仍然是动态方法的两倍多。在大多数情况下,这显然无关紧要,但是自定义二进制编写器似乎是一个可能的地方。dynamic的使用非常简单,并且提供了一个很好的原型和检查代码的方法。但是出于我的需要,正如@ChaseMedallion所写的,我认为我需要一个更好的执行策略。谢谢。我已经编辑了你的标题。请参见“”,其中的共识是“不,他们不应该”。这正是我想要的方法。感谢您提供了现成的代码。缓存编译后的lambda的方式非常有趣。在我看来,目前是最好的答案。