在C#中使用泛型时如何避免装箱/取消装箱?
我想让以下类与在C#中使用泛型时如何避免装箱/取消装箱?,c#,c#-4.0,generics,dynamic,boxing,C#,C# 4.0,Generics,Dynamic,Boxing,我想让以下类与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){r
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
}
公共类agg{
公共静态T add(ta,tb){返回a+b;}//编译错误:运算符“+”不能应用于“T”和“T”类型的操作数
公共静态T add(ta,tb){return(dynamic)a+b;}//编译,但在运行时涉及装箱和取消装箱
}
我怎样才能做到这一点
如果我为每个类型显式定义了方法,那么如果不在该类中为每个类型显式定义方法,我就无法从另一个泛型类型test
使用该类:
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
}
}
公共类agg{
//公共静态T add(ta,tb){返回a+b;}//编译错误:运算符“+”不能应用于“T”和“T”类型的操作数
//公共静态T add(ta,tb){return(dynamic)a+b;}//编译,但在运行时涉及装箱和取消装箱
公共静态intadd(inta,intb){returna+b;}//将不会被test agg.add(a,b)调用匹配
}
公开课考试{
公共测试(TA、TB){
var c=agg.add(a,b);//编译错误:“agg.add(int,int)”的最佳重载方法匹配有一些无效参数
}
}
我认为你不会找到一个好方法来做你想做的事情。一些可能性:
T
(例如if(typeof(T)==typeof(int))add((int)a,(int)b);
等)newtest
,而是创建一个调用正确方法的testInt:test
类test
中使用dynamic
casting调用add方法,而不是在agg
中调用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);
}
公共静态类agg{
公共静态intadd(inta,intb){返回a+b;}
公共静态字节加法(字节a,字节b){返回(字节)(a+b);}
公共静态十进制加法(十进制a,十进制b){返回a+b;}
//等
}
公共类测试:测试
{
公共测试(inta,intb):基(a,b){}
受保护的覆盖整数添加(整数a、整数b)
{
返回加总(a、b);
}
}
公共抽象类测试{
公共测试(TA、TB){
tc=添加(a,b);
}
受保护的摘要T添加(T a,T b);
}
示例4:
public class test<T>{
public test(T a,T b){
T c = agg.add((dynamic)a, (dynamic)b);
}
}
公共类测试{
公共测试(TA、TB){
tc=agg.add((动态)a,(动态)b);
}
}
你为什么关心装箱/拆箱?这是一项对性能高度敏感的任务吗?如果是这样,任何涉及动态的事情都可能是不可行的。如果您不确定此代码是否需要尽可能快地运行,请不要过早地进行优化:暂时忘记性能问题,并以您能用的最好、最可读的方式解决问题。我的解决方案使用纯C(否或):
内部类agginit{internal static bool start=false;}
公共类agg{
//公共静态T add(ta,tb){返回a+b;}//编译错误:运算符“+”不能应用于“T”和“T”类型的操作数
//公共静态T add(ta,tb){return(dynamic)a+b;}//编译,但在运行时涉及装箱和取消装箱
//公共静态intadd(inta,intb){returna+b;}//将不会被test agg.add(a,b)调用匹配
公共静态T add(ta,tb){return_add(a,b);}
静态函数_add=null;
公共静态void setAdd(Func f){if(_add==null)_add=f;else抛出新异常(“不能两次初始化”);}
静态agg(){
如果(!agginit.started){//以防止递归操作
agginit.started=true;
加总=(a,b)=>a+b;
加总=(a,b)=>a+b;
//下面我们初始化所有其他可能使用的加法类型只是为了好玩,如果这里没有列出类型,则不支持它
加总=(a,b)=>a+b;
agg._add=(a,b)=>{return(byte)(a+b);};//脏向下转换,需要使用返回类型泛型参数进行增强
加总=(a,b)=>a+b;
加总=(a,b)=>a+b;
agg._add=(a,b)=>{var ret=new StringBuilder();ret.Append(a.ToString());ret.Append(b.ToString());return ret;};
加总=(a,b)=>a.Concat(b);
agg._add=(a,b)=>{var ret=new HashSet(a);ret.UnionWith(b);return ret;};
agg._add=(a,b)=>{var ret=new SortedSet(a);ret.UnionWith(b);ret ret;};
agg._add=(a,b)=>{var ret=新字节[a.Length+b.Length];Buffer.BlockCopy(a,0,ret,0,a.Length);Buffer.BlockCopy(b,0,ret,a.Length,b.Length);ret;};
agg._add=(a,b)=>{var ret=new System.IO.MemoryStream(新字节[a.Length+b.Length]);a.WriteTo(ret);b.WriteTo(ret);ret;};
}
}
}
公开课考试{
公共物品;
公共测试(TA、TB){
res=加总(a,b);
}
}
公共A类{
公共INTZ;
静态A(){
agg.setAdd((a,b)=>newa{z=a.z+b.z});//任何类都可以定义自己的add实现
}
公开无效测试(){
var t1=agg.add(新的A{z=1},新的A{z=2});
如果(t1.z!=3)抛出新异常(“测试失败”);
var t2=新测试(新A{z=1},新A{z=2});
如果(t2.res.z!=3)抛出新异常(“测试失败”);
}
}
如果它是double,那么为什么要使用泛型呢?您可以有两个Add方法。double Add(double a,double b)和int Add(int a,int b)。查看与运算符讨论这个问题的内容。@Alexei:我在这里看到4件事:1)可下载源代码的引用,2)动态,3)ICalc,4)IL#2和#3不是很好,#4如果有效的话会很有趣,但我想知道更多关于纯C的解决方案#1-可下载的源代码做了很多事情,我想要一些简单的例子。我找到了比你提到的4个更好的方法(见我的答案),它不涉及强制转换、为每种模板类型定义单独的类,或者为每种类型定义if语句。在某些应用领域(例如嵌入式系统
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");
}
}