动态操作员解析

我有一个generics方法,通过将其中一个操作数转换为dynamic来调用运算符。 有两种不同的电话:

 //array is T[][] //T is MyClass array[row][column] != default(T) as dynamic 

这工作并调用static bool operator !=(MyClass a, MyClass b) (即使双方都为null )。

让我感到惊讶的是以下行的行为:

 //array, a and b are T[][] //T is MyClass array[row][column] += a[line][i] * (b[i][column] as dynamic); 

这叫
public static MyClass operator *(MyClass a, object b)
public static MyClass operator +(MyClass a, object b)

并不是
public static MyClass operator *(MyClass a, MyClass b)
public static MyClass operator +(MyClass a, MyClass b)

删除(MyClass, object)运算符会导致

 Microsoft.CSharp.RuntimeBinder.RuntimeBinderException wurde nicht behandelt. HResult=-2146233088 Message=Der *-Operator kann nicht auf Operanden vom Typ "[...].MyClass" und "object" angewendet werden. Source=Anonymously Hosted DynamicMethods Assembly StackTrace: bei CallSite.Target(Closure , CallSite , MyClass , Object ) bei System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1) bei [...].MatrixMultiply[T](T[][] a, T[][] b) in [...] InnerException: 

(椭圆形)。

为什么?
我可以在没有显式调用T Operators.Add(T a, T b)方法而不是运算符的情况下调用正确的运算符吗?

更新

 public static T TestMethod(this T a, T b) { return (T)(a * (b as dynamic)); } 

此方法在单独的程序集中调用(或尝试调用) operator *(T, object) ,如果主程序集中的相同方法正确调用operator *(T, T)

我用作类型参数的类是internal ,当我将其更改为public时问题消失,所以它似乎依赖于类对方法的可见性。

即使该类不可见,也会成功调用operator *(T, object)

听起来你已经偶然发现了一个有趣的设计决定 – 而不是故意,这是故意的 – 动态function。 一段时间以来,我一直想博客谈论这个。

首先,让我们退后一步。 动态特性的基本思想是包含动态类型的操作数的表达式将其类型分析推迟到运行时。 在运行时,通过启动新版本的编译器并重新进行分析来完成类型分析,这次将动态表达式视为其实际运行时类型的表达式。

所以如果你有一个加法表达式,它在编译时有一个左手编译时类型的对象,和一个右手编译时类型的动态,并且在运行时动态表达式实际上是一个字符串,那么分析是重新完成,左手侧是对象,右手侧是弦。 请注意,不考虑左侧的运行时类型 。 它的编译时类型是对象,而不是动态的。 只有动态类型的表达式具有在运行时分析中使用其运行时类型的属性。

只是为了确保清楚:如果你有:

 void M(Giraffe g, Apple a) {...} void M(Animal a, Fruit f) { ... } ... Animal x = new Giraffe(); dynamic y = new Apple(); M(x, y); 

然后在运行时调用第二个覆盖。 在运行时x是Giraffe的事实被忽略了 ,因为它不是动态的。 它在编译时是Animal,因此在运行时它继续被分析为Animal类型的表达式。 也就是说,分析就像你说过的那样:

 M(x, (Apple)y); 

而这显然是第二次超载。

我希望这很清楚。

现在我们来讨论这个问题。 当运行时类型无法访问时会发生什么? 我们实际上是一个例子:

 public class Fruit {} public class Apple : Fruit { public void M(Animal a) {} private class MagicApple : Apple { public void M(Giraffe g) {} } public static Apple MakeMagicApple() { return new MagicApple(); } } ... dynamic d1 = Apple.MakeMagicApple(); dynamic d2 = new Giraffe(); d1.M(d2); 

好的,会发生什么? 我们有两个动态表达式,所以根据我之前的声明,在运行时我们再次进行分析,但假装你说

 ((Apple.MagicApple)d1).M((Giraffe)d2)); 

所以你会认为重载Apple.MagicApple.M会选择Apple.MagicApple.M完全匹配的方法。 但事实并非如此! 我们不能假装上面的代码就是您所说的,因为该代码访问其可访问域之外的私有嵌套类型! 该代码无法完全编译。 但同样明显的是,我们不能允许此代码失败,因为这是一种常见的情况。

所以我必须修改我之前的发言。 运行时分析引擎实际执行的操作是假装您插入了可以合法插入的强制转换。 在这种情况下,它意识到用户可以插入:

 ((Apple)d1).M((Giraffe)d2)); 

而重载决议将选择Apple.M

而且:假装演员阵容总是属于class级类型。 可能存在可能已插入的接口类型或类型参数类型转换,这将导致重载解析成功,但通过使用“dynamic”,您指示您希望使用运行时类型 ,以及运行时类型对象永远不是接口或类型参数类型。

听起来你在同一条船上。 如果在调用站点上无法访问动态表达式的运行时类型,则将其视为最接近的可访问基类型,以便进行运行时分析。 在您的情况下,最接近的可访问基类型可能是对象。

这一切都清楚了吗?