在C#中使用Generics时如何避免装箱/拆箱?

时间:2013-04-28 03:08:49

标签: c# c#-4.0 generics dynamic boxing

我想让以下类与intdouble和其他添加类型一起使用,而不会在装箱/拆箱时运行时间开销,但可以从其他泛型类型重用:

public class agg<T>{
    public static T add(T a,T b){return a+b;} // compile error: Operator '+' cannot be applied to operands of type 'T' and 'T'
    public static T add(T a,T b){return (dynamic)a+b;} // compiles, but involves boxing and unboxing at run-time
}

我怎样才能做到这一点?

如果我明确地为每种类型定义方法,我不能使用来自另一种泛型test<T>的那个类,而不是在该类中明确定义每种类型的方法:

public class agg<T>{
    //public static T add(T a,T b){return a+b;} // compile error: Operator '+' cannot be applied to operands of type 'T' and 'T'
    //public static T add(T a,T b){return (dynamic)a+b;} // compiles, but involves boxing and unboxing at run-time
    public static int add(int a,int b){return a+b;} // won't be matched by test agg<T>.add(a,b) invokation
}
public class test<T>{
    public test(T a,T b){
        var c=agg<T>.add(a,b); //compile error: The best overloaded method match for 'agg<T>.add(int, int)' has some invalid arguments
    }
}

2 个答案:

答案 0 :(得分:3)

我的解决方案使用纯C#(无IL3rd party libraries):

internal class agginit{internal static bool started=false;}
public class agg<T>{
    //public static T add(T a,T b){return a+b;} // compile error: Operator '+' cannot be applied to operands of type 'T' and 'T'
    //public static T add(T a,T b){return (dynamic)a+b;} // compiles, but involves boxing and unboxing at run-time
    //public static int add(int a,int b){return a+b;} // won't be matched by test agg<T>.add(a,b) invokation
    public static T add(T a,T b){return _add(a,b);}
    static Func<T,T,T> _add=null;
    public static void setAdd(Func<T,T,T> f){if(_add==null)_add=f;else throw new Exception("Can't init twice");}
    static agg(){
        if(!agginit.started){ // to prevent recursive actions
            agginit.started=true;
            agg<int>._add=(a,b)=>a+b;
            agg<double>._add=(a,b)=>a+b;
            // below we initialize all other potentially used additive types just for fun, if type is not listed here, it's not supported
            agg<string>._add=(a,b)=>a+b;
            agg<byte>._add=(a,b)=>{return (byte)(a+b);}; // dirty down-cast, needs to be enhanced with return type generic parameter
            agg<long>._add=(a,b)=>a+b;
            agg<System.Numerics.BigInteger>._add=(a,b)=>a+b;
            agg<StringBuilder>._add=(a,b)=>{var ret=new StringBuilder();ret.Append(a.ToString());ret.Append(b.ToString());return ret;};
            agg<IEnumerable<T>>._add=(a,b)=>a.Concat(b);
            agg<HashSet<T>>._add=(a,b)=>{var ret=new HashSet<T>(a);ret.UnionWith(b);return ret;};
            agg<SortedSet<T>>._add=(a,b)=>{var ret=new SortedSet<T>(a);ret.UnionWith(b);return ret;};
            agg<byte[]>._add=(a,b)=>{var ret=new byte[a.Length+b.Length];Buffer.BlockCopy(a,0,ret,0,a.Length);Buffer.BlockCopy(b,0,ret,a.Length,b.Length); return ret;};
            agg<System.IO.MemoryStream>._add=(a,b)=>{var ret=new System.IO.MemoryStream(new byte[a.Length+b.Length]);a.WriteTo(ret);b.WriteTo(ret);return ret;};
        }
    }
}
public class test<T>{
    public T res;
    public test(T a,T b){
        res=agg<T>.add(a,b);
    }
}
public class A{
    public int z;
    static A(){
        agg<A>.setAdd((a,b)=>new A{z=a.z+b.z}); // any class can define own add implementation
    }
    public void test(){
        var t1=agg<A>.add(new A{z=1},new A{z=2});
        if(t1.z!=3)throw new Exception("test failed");
        var t2=new test<A>(new A{z=1},new A{z=2});
        if(t2.res.z!=3)throw new Exception("test failed");
    }
}

答案 1 :(得分:1)

我认为你不会找到一个好的方法去做你想做的事情。一些可能性:

  1. 以您展示的方式使用动态。
  2. 拥有if / else链,或启用完整类型名称,以将已知类型列表标识为等于T(例如if (typeof(T) == typeof(int)) add((int)a,(int)b);等。)
  3. 创建一个调用正确方法的new test<int>类,而不是使用testInt : test<int>
  4. 使用dynamic中的test<T>投射来调用添加方法,而不是agg<T>
  5. 3的例子:

    public static class agg{
        public static int add(int a,int b){return a+b;}
        public static byte add(byte a,byte b){return (byte)(a+b);}
        public static decimal add(decimal a,decimal b){return a+b;}
        // etc
    }
    public class testInt:test<int>
    {
        public testInt(int a, int b) : base(a, b) { }
        protected override int add(int a, int b)
        {
            return agg.add(a, b);
        }
    }
    public abstract class test<T>{
        public test(T a,T b){
            T c = add(a, b);
        }
        protected abstract T add(T a, T b);
    }
    

    4的例子:

    public class test<T>{
        public test(T a,T b){
            T c = agg.add((dynamic)a, (dynamic)b);
        }
    }
    

    你为什么关心拳击/拆箱?这是一项对性能敏感的高级任务吗?如果是这样,涉及动态的任何事情都可能是不可行的。如果您不确定此代码是否需要尽快运行,不要过早优化:暂时忘掉性能并以最佳,最易读的方式解决问题。< / p>