动态应该能够处理数学而不必让我考虑它,但即使在琐碎的情况下我也遇到了一些问题。 考虑一下这个非常简单的函数::
public static T DynamicFactorial<T>(T input)
{
dynamic num = input;
dynamic res = 1;
for (; num > 1; res *= num, num -=1) ;
return res;
}
这是一个应该处理任何数字类型并对其执行阶乘的函数。不幸的是,当我尝试计算DynamicFactorial(5UL)
:
Operator '*=' cannot be applied to operands of type 'int' and 'ulong'
请不要说我可以将此代码转换为递归调用,因为这是一个示例。我的问题是,如果你试图使用动态来使用一元赋值运算符,强迫我知道我在编译时计算的类型是没有意义的。 “潜在的”解决方案就是这样做:
public static T DynamicFactorial<T>(T input)
{
dynamic num = input;
T ONE = (T)(1 as dynamic);
dynamic res = ONE;
for (; num > ONE; res *= num, num -=ONE) ;
return res;
}
这是有效的,但神圣的地狱是这个丑陋的,并要求我创建一个我计划实际使用的类型的常量,这至少可以说是糟糕的。
答案 0 :(得分:16)
“动态”的基本设计原则是运行时的分析与编译时的分析完全相同,如果编译器已经给出了运行时类型。
让我们来看一下代码的修改版本:
ulong input = whatever;
dynamic num = input;
dynamic res = 1;
res = res * num;
这应该在运行时完全,就像编译器知道标记为“动态”的所有类型一样。它的行为完全为
ulong input = whatever;
object num = input;
object res = 1;
res = (int)res * (ulong)num;
该程序在编译时出错,因此逻辑上动态版本必须在运行时给出相同的错误。
动态应该能够处理数学而不必让我考虑它
绝对不是。那是不动态特征的设计原则。动态特性的目的是简化C#代码与设计用于与动态语言交互的库中的代码的交互,现代库(如为Python和Ruby设计的那些)或遗留库(如为COM自动化设计的那些库)通过VB6或VBScript)。在我们设计此功能时,对算术表达式的结果进行VB风格类型提升是根本不是,而且正如您所发现的那样,它的表现非常糟糕。
让我清楚地说明这一点:动态不是要让C#成为一种动态语言,这似乎就是你的想法。动态是使C#成为静态类型语言,可以与为动态语言设计的库良好地互操作。如果您想要的是具有动态算术的语言,请考虑使用Visual Basic或Python。
(顺便说一句,有些人可能想知道为什么int + ulong
在C#中是不合法的。在C#中有七个非提升数字加法运算符:int + int,uint + uint,long + long,ulong + ulong,float + float ,double + double和decimal + decimal。在这七个中,哪个是最好的?int + int,uint + uint和long + long是因为ulong可能不适合.ulong + ulong因为int可能是负的那留下了float,double和decimal.Float比double更好(因为它更具体)所以double消失了。但是将ulong转换为float既不比将ulong转换为decimal更好也不差。因为我们在这里有歧义我们产生了一个错误。如果由于某些奇怪的原因你必须在ulong中添加一个int,插入一个强制转换来解决歧义。)
最后,我注意到有办法做你想做的事。我还没有尝试过,但这可能有用:
public static T DynamicFactorial<T>(T input)
{
dynamic num = input;
dynamic one = default(T);
one++;
dynamic res = one;
while (num > one)
{
res *= num;
num--;
}
return res;
}
这适用于默认值为零并且在其上定义了++, - 和*运算符的任何类型。
然而,这是严重的,缓慢的,滥用仿制药。你真的想要计算ushort的阶乘吗? Factorial是一个很容易定义的函数,你可能不需要超过六个版本的tops。我说只是写了六次,而不是通过滥用泛型和动态来节省少量击键。
答案 1 :(得分:4)
您似乎误解了dynamic
的工作原理。它为C#添加了一些动态功能,但这并不意味着C#忘记了类型严格性了。 dynamic
表示调度将在运行时。但真正的变量类型根本不是动态的。它在编译时通常推断,但有一些特殊类型,如ExpandoObject
,可以使用动态调度。原始类型不像往常那样表现。 1被推断为int
,因此您的dynamic
只是int
。从那时起,异常非常有效。你的例子中的dynamic
没用,它不会扩展。您应该在编译时定义sum变量,或者如果要使其可扩展,请使用BigNum
。
答案 2 :(得分:2)
我不认为这是一个错误。 res
的类型是从1
推断出来的,默认情况下是int
类型。
答案 3 :(得分:1)
这不是动力在这里是痛苦的,因为它们应该按照自己的意愿行事。第一个示例中res
的类型是从常量1
推断的,它是一个整数。
这里的问题是.Net中没有所有数字的基类型,因此您不能仅限制泛型参数T并在此使用它而不是动态。