使用C# dynamic关键字的操作符?

8

我有这个函数:

static void Func1<T>(T x, T y)
{
    dynamic result = ((dynamic)x + y); //line 1
    dynamic result2 = (x + y);         //line 2
}

这个函数可以执行为Func(1,2);。然而,第1行没问题,而第2行在编译时就报错了。

从第2行抛出的异常是:

运算符“+”不能应用于类型“T”和“T”的操作数

所以,我们需要创建一个运算符重载。好的,到目前为止一切顺利。

但第1行呢?y岂不也需要动态转换?

((dynamic)x + (dynamic)y);

我知道它在运行时被计算,但为什么C#编译器接受第1行中的+运算符(即错误地假设T可以与其他东西+)?


执行代码后,result的值是多少?是“3”还是“12”?我猜它在进行字符串拼接。 - Servy
1
@Servy:你为什么认为当Tint时它会进行字符串连接? - LukeH
2个回答

7
在您的第一个示例中,通过将x设置为动态类型,您实际上也使operator+操作变成了动态的。这消除了对T的类型说明符的需求,从而消除了T没有有效operator+的问题。
在运行时,动态绑定将发生并评估两个操作数以确保可以使用operator+
如果算术运算符的操作数具有编译时类型dynamic,则表达式是动态绑定的(§7.2.2)。在这种情况下,表达式的编译时类型是dynamic,并且使用具有编译时类型dynamic的那些操作数的运行时类型将在运行时进行下述解析。
在您的第二个示例中,编译器知道x + y的类型,并简单地将结果存储到一个dynamic变量中。对result2的进一步使用将是动态绑定的。这是有意义的,因为赋值运算符右侧没有动态操作。
在没有涉及动态表达式的情况下,C#默认使用静态绑定,这意味着在选择过程中使用组成表达式的编译时类型。

此外,我不禁想知道为什么您会让一个通用方法尝试使用类型特定的运算符而没有约束条件。 - Tejs
对于像数字类型这样的东西,您无法想出任何类型约束,允许算术操作。 - user7116
我认为首选的方法应该是像框架一样为那些特定的值类型创建重载。否则,如果你想让特定的类型执行某种操作,可以让它们实现一个特定的接口,然后将其约束到该接口。 - Tejs
一个刻意的例子是尝试实现Complex<T>。无论是接口、重载还是泛型,都无法仅通过语法来实现算术操作。 - user7116
所以只要有一个参数是动态的,整个表达式就会被动态地计算吗? - Royi Namir
根据我的理解,这是标准的要求。然而,在存在互斥子表达式的情况下,例如 ((dynamic)x + y) * (x + y),编译器知道它可以静态地绑定 (x + y) 并报告类似于您第二个示例中的 CS0019 错误。 - user7116

3
dynamic基本上告诉编译器:“不要试图确保我正在做的合法;在运行时,我确定它是合法的”。对于任何你尝试在动态类型的变量上执行的操作,都会被编译。但是如果分配给动态变量的类型实际上没有实现该操作,则它将无法成功运行。

至于为什么两者都不必是动态的,编译器基本上将尝试在参与操作的两种类型中找到与签名匹配的运算符(静态方法),从LValue开始。由于LValue是动态的,编译器必须假设该操作存在于任何将用作X的内容中,即使X与Y是相同的占位符类型,并且未知是否具有+运算符。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接