c#模板化整数方法?
是否可以为任何类型的整数大小设置模板方法 为了举例说明,想象一下这个非常简单的例子(在我的问题中,方法的主体并不重要): 现在,我想要支持任何类型整数的相同方法(当然不包括BigInteger)。我必须写出所有变体:c#模板化整数方法?,c#,C#,是否可以为任何类型的整数大小设置模板方法 为了举例说明,想象一下这个非常简单的例子(在我的问题中,方法的主体并不重要): 现在,我想要支持任何类型整数的相同方法(当然不包括BigInteger)。我必须写出所有变体: public long Mul(long a, long b) { return a*b; } public ulong Mul(ulong a, ulong b) { return a*b; } public short Mul(short a, sh
public long Mul(long a, long b) {
return a*b;
}
public ulong Mul(ulong a, ulong b) {
return a*b;
}
public short Mul(short a, short b) {
return a*b;
}
public ushort Mul(ushort a, ushort b) {
return a*b;
}
public byte Mul(byte a, byte b) {
return a*b;
}
虽然这个例子非常简单,实际上也不存在复制的问题,但如果我有更复杂的算法(复制所有整数类型):
我的结论是这是不可能的。。。这就是MS必须实现所有重载的原因
有没有人有任何设计通用整数方法而不必复制逻辑的技巧?考虑一下DLR:
static void Main(string[] args)
{
int i = Mul(2, 4);
Console.WriteLine(i);
Console.Read();
}
static dynamic Mul(dynamic x, dynamic y)
{
return x * y;
}
性能有待确定(我希望它比直接重载慢),但可读性要好得多。如果您提供的类型没有实现所需的运算符,或者不同的类型导致值被截断,那么可能会有点麻烦
根据评论更新:
如果性能如此关键,那么听起来您已经在复制/可读性和追求的性能之间进行了权衡。编码并继续前进。一些额外代码的任何维护问题都可能与性能本身的维护相形见绌。好吧,您可以在构建过程中使用,例如,生成重复的代码。没有干净的高性能解决方案。我能想到的选择是:
运算符
类或DLR(慢速)static Func<T,T,T> _multiply;
public static T Multiply(T n1,T n2)
{
return _multiply(n1, n2);
}
JIT编译器足够聪明,可以在必须采取的情况下实现这些中的哪一种,并将它们删除。虽然这提高了性能,但却增加了方法的IL表示。JIT编译器现在还不够聪明,无法内联这些方法,因为它们的IL表示很长,而内联启发式只查看方法的IL长度,而不是机器代码长度。我不记得这些投射是否会导致拳击,或者抖动是否足够聪明,可以优化它。不过,缺少内联的代价太高了
4)工作原理:
首先创建一个包含所需基本操作(算术运算符,…)的接口:
interface-IArithmetic
{
T乘(T n1,T n2);
}
使用结构为您需要的每种类型实现它:
public struct int32算术:IArithmetic
{
Int32乘法(Int32 n1,Int32 n2)
{
返回n1*n2;
}
}
然后将大部分实际代码设置为通用代码,并传入算术助手:
internal T MyOperation<T,TArithmetic>(T n1, T n2)
where TArithmetic:struct,IArithmetic<T>
{
return default(TArithmetic).Multiply(n1,n2);
}
内部T操作(T n1,T n2)
其中TArithmetic:struct,IArithmetic
{
返回默认值(TArithmetic).Multiply(n1,n2);
}
如果您想为多种类型创建一个干净的接口,请创建一个瘦重载包装器,转发到泛型方法:
public Int32 MyOperation(Int32 n1,Int32 n2)
{
return MyOperation<Int32,Int32Arithmetic>(n1, n2);
}
public Int32 MyOperation(Int32 n1、Int32 n2)
{
返回操作(n1,n2);
}
这可能很快,因为泛型会针对每种值类型进行专门化。它不使用间接方法,并且IL中的方法体不会太长,因此内联是可能的。但我自己还没有尝试过。publictmul(ta,tb){
public T Mul<T>(T a, T b){
dynamic x = a;
dynamic y = b;
return (T)x*y;
}
动态x=a;
动态y=b;
返回(T)x*y;
}
我曾经尝试过使用CodeDom实现类似的功能,以便在运行时生成程序集并动态加载它。这很好,但有一些局限性。例如,您的环境可能不允许您动态编译程序集,这是一个大问题:性能。尽管“calculator”类只生成一次,但调用虚拟方法的开销实际上使计算所需的时间增加了一倍
您可以尝试一下它在您的环境中的性能,我只是布置了类(因为这是很久以前的事了,我再也没有代码了)
接口计算器{
T加(T左,T右);
T乘(T左,T右);
}
内部静态类计算器{
静态计算器实例;
静态计算器(){
类型=类型(T);
//1.使用CodeDom设计一个类,该类使用
//内置+,-,*,/运算符
//2.在内存中编译程序集
//3.加载程序集并创建ICalculator类的实例
类型concreteType=GetTypeFromDynamicsSembly();//从程序集获取此值。
instance=Activator.CreateInstance(concreteType)作为iCalCalculator;
}
公共静态T添加(T左,T右){
返回实例.Add(左、右);
}
}
类MyClassUsingGenericMathType{
T总和(参数T[]值){
T总和=默认值(T);
foreach(值中的T值){
总和=计算器。加(总和,值);
}
回报金额;
}
}
其思想是在第一次使用实现时动态构建它(然后调用静态构造函数),然后计算器方法直接调用所使用的数值类型的相应运算符。正如我所说的,我记得每次执行操作时都会增加开销,但我从未分析过使用某些编译器属性是否有可能加快进程
另一件事:使用未实现相应运算符的类型将导致运行时异常,而不是编译错误。所以是
static Func<T,T,T> _multiply;
public static T Multiply(T n1,T n2)
{
return _multiply(n1, n2);
}
public static T Multiply(T n1,T n2)
{
if(typeof(T)==typeof(int))
return (T)(object)((int)(object)n1*(int)(object)n2);
...
return _multiply(n1, n2);
}
interface IArithmetic<T>
{
T Multiply(T n1,T n2);
}
public struct Int32Arithmetic:IArithmetic<Int32>
{
Int32 Multiply(Int32 n1,Int32 n2)
{
return n1*n2;
}
}
internal T MyOperation<T,TArithmetic>(T n1, T n2)
where TArithmetic:struct,IArithmetic<T>
{
return default(TArithmetic).Multiply(n1,n2);
}
public Int32 MyOperation(Int32 n1,Int32 n2)
{
return MyOperation<Int32,Int32Arithmetic>(n1, n2);
}
public T Mul<T>(T a, T b){
dynamic x = a;
dynamic y = b;
return (T)x*y;
}
interface ICalculator<T> {
T Add(T left, T right);
T Multiply(T left, T right);
}
internal static class Calculator<T> {
static ICalculator<T> instance;
static Calculator() {
Type type = typeof(T);
// 1. Use CodeDom to design a class that implements ICalculator<T> using the
// builtin +,-,*,/ operators
// 2. Compile assembly in memory
// 3. Load assembly and create an instance of the ICalculator<T> class
Type concreteType = GetTypeFromDynamicAssembly(); // Get this from the assembly.
instance = Activator.CreateInstance(concreteType) as ICalculator<T>;
}
public static T Add(T left, T right) {
return instance.Add(left, right);
}
}
class MyClassUsingGenericMathType<T> {
T Sum(params T[] values) {
T sum = default(T);
foreach (T value in values) {
sum = Calculator<T>.Add(sum, value);
}
return sum;
}
}