在动态中是否存在valuetype运算符处理的错误?

时间:2010-10-12 15:39:52

标签: c# dynamic

动态应该能够处理数学而不必让我考虑它,但即使在琐碎的情况下我也遇到了一些问题。 考虑一下这个非常简单的函数::

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

这是有效的,但神圣的地狱是这个丑陋的,并要求我创建一个我计划实际使用的类型的常量,这至少可以说是糟糕的。

4 个答案:

答案 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并在此使用它而不是动态。