动态中值类型运算符处理是否存在错误?

发布于 2024-09-26 19:41:24 字数 799 浏览 2 评论 0原文

动态应该能够处理数学而不需要我去思考它,但即使在微不足道的情况下我也会遇到一些问题。 考虑这个非常简单的函数::

 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 '*='无法应用于类型'int'和'ulong'的操作数 code>

请不要说我可以将此代码变成递归调用,因为这是一个示例。我的问题是,如果您尝试使用动态来使用一元赋值运算符,那么强迫我知道在编译时计算的类型是没有意义的。一个“潜在”的解决方案是这样做::

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

这可行,但是天哪太丑了,需要我创建一个我计划实际使用的类型的常量,至少可以说这是蹩脚的。

Dynamic should be able to handle math without making me have to think about it but even in trivial cases I'm running into some issues.
Consider this really simple function::

 public static T DynamicFactorial<T>(T input)
  {
   dynamic num = input;
   dynamic res = 1; 
   for (; num > 1; res *= num, num -=1) ;
   return res;
  }

This is a function that should handle taking any numeric type and performing a factorial on it. Unfortunately, this gives me the following exception, when I try to compute DynamicFactorial(5UL):

Operator '*=' cannot be applied to operands of type 'int' and 'ulong'

Please don't say I can turn this code into a recursive call, as this is an example. My problem is that if you are trying to use dynamic to use unary assignment operator, it doesn't make sense to force me to know my types being computed at compile time. A "potential" solution is to do this::

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

This works, but holy hell is this ugly and requires me to create a constant of the type I plan on actually using, which is crappy to say the least.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

笑叹一世浮沉 2024-10-03 19:41:24

“动态”的基本设计原则是运行时的分析与如果给编译器指定了运行时类型的话编译时的分析完全相同

因此,让我们采用代码的修改版本:

 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# 成为一种动态语言,这似乎就是您所认为的那样。 Dynamic 是关于使 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 不适合,因为int 可能不适合。浮点型、双精度型和小数型都比双精度型好(因为它更具体),所以将 ulong 转换为浮点型并不比将 ulong 转换为小数型更好,也不差。如果由于某种奇怪的原因你必须向 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 的阶乘吗?阶乘是一个很容易定义的函数,您可能不需要超过六个版本。我说只写六次,而不是通过滥用泛型和动态来节省少量的击键次数。

The fundamental design principle of "dynamic" is that the analysis at runtime is exactly the same as the analysis would have been at compile time if the compiler had been given the runtime types.

So let's take a modified version of your code:

 ulong input = whatever;
 dynamic num = input; 
 dynamic res = 1;  
 res = res * num;

That should behave at runtime exactly as though the compiler had known the types of everything marked as "dynamic". It should behave exactly as

 ulong input = whatever;
 object num = input; 
 object res = 1;  
 res = (int)res * (ulong)num;

And that program gives an error at compile time, so logically the dynamic version must give the same error at runtime.

Dynamic should be able to handle math without making me have to think about it

Absolutely not. That is not a design principle of the dynamic feature. The purpose of the dynamic feature is to simplify interaction of C# code with code in libraries designed to interact with dynamic languages, either modern libraries (like those designed for Python and Ruby), or legacy libraries (like those designed for COM automation via VB6 or VBScript). Doing VB-style type promotion on results of arithmetic expressions was not at all on our minds when we designed this feature, and as you've discovered, it does so badly.

Let me make this perfectly clear: dynamic is not about making C# a dynamic language, which seems to be what you think it is about. Dynamic is about making C# a statically typed language that interoperates well with libraries designed for dynamic languages. If what you want is a language with dynamic arithmetic, consider Visual Basic or Python.

(Incidentally, some might wonder why int + ulong is not legal in C#. There are seven nonlifted numeric addition operators in C#: int+int, uint+uint, long+long, ulong+ulong, float+float, double+double and decimal+decimal. Of these seven, which one is the best? int+int, uint+uint and long+long are out because the ulong might not fit. ulong+ulong is out because the int might be negative. That leaves float, double and decimal. Float is better than double (because it is more specific) so double goes away. But converting a ulong to float is neither better nor worse than converting a ulong to decimal. Since we have an ambiguity here we produce an error. Insert a cast to resolve the ambiguity if for some bizarre reason you have to add an int to a ulong.)

Finally, I note that there are ways to do what you want. I haven't actually tried it but this would probably work:

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

This will work for any type where the default is zero and there are ++, --, and * operators defined on it.

However, this is gross, slow, and an abuse of generics. Are you really going to want to compute factorials of ushort? Factorial is an easy function to define and you're probably not going to need more than half a dozen versions of it, tops. I say just write it half a dozen times rather than saving a tiny number of keystrokes by abusing generics and dynamic.

别再吹冷风 2024-10-03 19:41:24

看来您误解了 dynamic 的工作原理。它为 C# 添加了一些动态功能,但这并不意味着 C# 不再忘记类型严格性。 dynamic 意味着调度将在运行时进行。但变量的真实类型根本不是动态的。它像往常一样在编译时推断,但是有一些特殊类型,例如 ExpandoObject 可以利用动态分派。原始类型不会并且表现得像往常一样。 1 被推断为 int,因此您的 dynamic 只是 int。从这一点来看,例外是非常有效的。您的示例中的 dynamic 没有用,它不会扩展。您应该在编译时定义 sum 变量,或者如果您想使其可扩展,请使用 BigNum。

Looks like you misunderstand how dynamic works. It adds some dynamic features to C#, but it doesn't mean that C# forgot about type strictness anymore. dynamic means that dispatch will be in runtime. But real type of variable is not dynamic at all. It is as usual inferred in compile time, but there are special types like ExpandoObject that can make use of dynamic dispatch. Primitive types do not and behave as usual. 1 is inferred to be int, so then your dynamic is just int. And from that point exception is pretty valid. dynamic in your example is useless, it will not expand. You should define your sum variable at compile time or use BigNum if you want to make it expandable.

伴我心暖 2024-10-03 19:41:24

我不认为这是一个错误。 res 的类型是从 1 推断出来的,默认情况下,1 类型为 int

I don't think this is a bug. The type of res is inferred from 1 which is, by default, of type int.

篱下浅笙歌 2024-10-03 19:41:24

这里的动态并不是令人痛苦的,因为它们按其应有的方式工作。第一个示例中的 res 类型是从整数常量 1 推断出来的。

这里的问题是,在 .Net 中,没有所有数字的基本类型,因此您不能只限制泛型参数 T 并在此处使用它而不是动态。

Its not dynamics that are painful here, because they work as they should. The type of res in your first example is inferred from the constant 1 which is an integer.

The problem here is that in .Net there is no base type for all the numbers, so you can't just restrict the generic parameter T and use it instead of dynamics here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文