C# 为什么我会在短、长而不是int的通用方法中遇到异常

C# 为什么我会在短、长而不是int的通用方法中遇到异常,c#,generics,C#,Generics,我有这些方法来寻找最伟大的共同创造者 private static T GenGCD(动态a、动态b) { //绝对值用于说明:a 0, (0,)=>数学Abs(b), (u,0)=>Math.Abs(a), _=>GCD(b,a%b) }; } 公共静态短GCD(这个短a,短b){返回GenGCD(a,b);} 公共静态intgcd(这个inta,intb){返回GenGCD(a,b);} 公共静态长GCD(这个长a,长b){返回GenGCD(a,b);} 像 短a=270; 短b=192;

我有这些方法来寻找最伟大的共同创造者

private static T GenGCD(动态a、动态b)
{
//绝对值用于说明:a<0和/或b<0;b>a
返回(a,b)开关
{
(0, 0) => 0,
(0,)=>数学Abs(b),
(u,0)=>Math.Abs(a),
_=>GCD(b,a%b)
};
}
公共静态短GCD(这个短a,短b){返回GenGCD(a,b);}
公共静态intgcd(这个inta,intb){返回GenGCD(a,b);}
公共静态长GCD(这个长a,长b){返回GenGCD(a,b);}

短a=270;
短b=192;
短r=a.GCD(b)

inta=270;
int b=192;
int r=a.GCD(b)

长a=270;
长b=192;
长r=a.GCD(b)
a
b
int
时,它工作,但当它们是
long
时,我得到一个异常

未处理的异常。System.DivideByZeroException:尝试除以零。
目标(闭包、调用站点、对象、对象)
在MathExtensions.GenGCD[T](对象a、对象b)
在MathExtensions.GCD(int64a,int64b)
目标(闭包、调用站点、类型、对象、对象)
在System.Dynamic.UpdateDelegates.UpdateAndExecute3[T0,T1,T2,TRet](调用站点,T0 arg0,T1 arg1,T2 arg2)
在MathExtensions.GenGCD[T](对象a、对象b)
在MathExtensions.GCD(int64a,int64b)
在Program.Main()中
当它们
short
时,我会得到一个不同的异常

未处理的异常。Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:无法将类型“int”隐式转换为“short”。存在显式转换(是否缺少强制转换?)
目标(闭包、调用站点、对象)
在System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](调用站点,T0 arg0)
在MathExtensions.GenGCD[T](对象a、对象b)
在MathExtensions.GCD(int16a,int16b)
在Program.Main()中
为什么它适用于
int
,但不适用于
long
short

为什么它适用于int而不是long或short

因为
0
特别是一个
int
,因此如果
dynamic
是,则
0L
将不匹配它。您可以检查IL,如果
0
打开
动态
,它会专门检查值是否为int 0,而不是任何旧的0

我建议你做一些如下的事情。主要的变化是:

  • 不要使用
    开关
    (这避免了主要问题-由于类型不对齐,
    开关
    不能按您希望的方式工作)
  • 更改递归,使方法调用自身(而不是通过
    GCD
  • 另外,如其他人建议的,考虑避免<代码>动态< /代码> -并且为每个类型实现单独的方法(例如一个用于代码> int <代码>,另一个用于<代码>长)。

    使用系统;
    公共静态类程序
    {
    私有静态T GenGCD(动态a,动态b),其中T:struct
    {
    如果(a==0){
    如果(b==0){
    返回(T)(对象)0;
    }
    返回Math.Abs(b);
    } 
    else如果(b==0)
    {
    返回Math.Abs(a);
    }
    返回GenGCD((T)b,(T)(a%b));
    }
    公共静态短GCD(这个短a,短b){返回GenGCD(a,b);}
    公共静态intgcd(这个inta,intb){返回GenGCD(a,b);}
    公共静态长GCD(这个长a,长b){返回GenGCD(a,b);}
    公共静态单GCD(这个单a,单b){返回GenGCD(a,b);}
    公共静态void Main()
    {
    单个a=99;
    单个b=87;
    单个r=a.GCD(b);
    控制台写入线(r);
    }
    }
    
    为什么要使用
    动态
    作为输入类型,而不是
    T
    ?(泛型的要点是什么?)您肯定不应该使用
    dynamic
    作为编写适用于三种特定已知类型之一的代码的方法。只需编写三个重载,每种类型一个。您的代码将更容易理解,静态类型化,性能更好,而这一切都不糟糕。@Franz Gleichmann动态的
    是因为你不能在
    t
    上使用数学运算符,
    math
    不接受
    t
    @Servy我只放了这三种类型,因为这是我决定测试代码的地方。我希望它适用于所有c#的整数类型。@Servy我这样做是因为我不希望每个整数类型都有一堆重复的代码。对于
    long
    只使用一个方法可能就足够99%的情况了(可能有两种情况下,性能非常重要,
    Int32
    Int64
    之间的差异非常明显)…如果关键的话,您仍然可以使用包装器将结果
    long
    转换为较短的类型…@AlexeiLevenkov Yep,假设您不需要浮点/十进制。当然,我也不知道任何浮点类型对GCD有何用处…因为它们不能表示大多数有理数(即1/3)无论如何,它将被限制在整个部分……而对于那个大整数来说,会安全得多。@AlexeiLevenkov可以说是正确的,是的。但鉴于etc确实存在,显然有些人会使用它们。:)
    using System;
                        
    public static class Program
    {
        private static T GenGCD<T> (dynamic a, dynamic b) where T:struct
        {
            if (a == 0) {
                if (b == 0) {
                    return (T)(object)0;
                }
    
                return Math.Abs(b);
            } 
            else if (b == 0)
            {
                return Math.Abs(a);
            }
    
            return GenGCD<T>((T)b, (T)(a % b));
        }
    
        public static short GCD(this short a, short b) { return GenGCD<short>(a, b); }
        public static int GCD(this int a, int b) { return GenGCD<int>(a, b); }
        public static long GCD(this long a, long b) { return GenGCD<long>(a, b); }
        public static Single GCD(this Single a, Single b) { return GenGCD<Single>(a, b); }
        
        public static void Main()
        {
            Single a= 99;
            Single b = 87;
            Single r = a.GCD(b);
            
            Console.WriteLine(r);
        }
    }