c# – 动态运算符解析

我有一个泛型方法,通过将其中一个操作数转换为动态来调用运算符.
有两种不同的电话:
//array is T[][]
//T is MyClass
array[row][column] != default(T) as dynamic

这工作并调用静态bool运算符!=(MyClass a,MyClass b)(即使双方都为空).

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

//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运算符(MyClass a,对象b)

并不是
public static MyClass operator *(MyClass a,MyClass b)和
public static MyClass运算符(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>(T a,T b)方法而不是运算符的情况下调用正确的运算符吗?

更新

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

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

我用作类型参数的类是内部的,当我将其更改为public时问题消失,所以它似乎取决于类对方法的可见性.

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

解决方法

听起来你已经偶然发现了一个有趣的设计决定 – 而不是故意,这是故意的 – 动态功能.我一直想写这个关于这个的博客.

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

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

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

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)d1).M((Giraffe)d2));

而重载决议将选择Apple.M.

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

听起来你在同一条船上.如果在调用站点上无法访问动态表达式的运行时类型,那么为了运行时分析的目的,它将被视为最接近的可访问基类型.在您的情况下,最接近的可访问基类型可能是对象.

这一切都清楚了吗?

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...