4

我想让以下类与int,double和其他附加类型一起使用,而不会在装箱/拆箱时产生运行时开销,但有可能从另一个泛型类型中重用:

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

2 回答 2

3

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

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");
    }
}
于 2013-04-28T05:32:04.727 回答
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使用强制转换调用 add 方法test<T>,而不是 in agg<T>

示例 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);
    }
}

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

于 2013-04-28T03:24:56.283 回答