Tam*_*chi 6 c# dynamic .net-4.5
我有一个泛型方法,通过将一个操作数强制转换来调用运算符dynamic.有两种不同的电话:
//array is T[][]
//T is MyClass
array[row][column] != default(T) as dynamic
Run Code Online (Sandbox Code Playgroud)
这是工作和调用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);
Run Code Online (Sandbox Code Playgroud)
这个电话
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:
Run Code Online (Sandbox Code Playgroud)
(省略号).
为什么?
我可以在没有显式调用T Operators.Add<T>(T a, T b)方法而不是运算符的情况下调用正确的运算符吗?
public static T TestMethod<T>(this T a, T b)
{
return (T)(a * (b as dynamic));
}
Run Code Online (Sandbox Code Playgroud)
operator *(T, object)如果相同的方法在主程序集中正确调用,则此方法在单独的程序集中调用(或尝试调用)operator *(T, T).
我用作类型参数的类是internal当我将其更改为时问题消失public,因此它似乎取决于类对方法的可见性.
operator *(T, object) 即使该类不可见,也会成功调用.
Eri*_*ert 13
听起来你已经偶然发现了一个有趣的设计决定 - 而不是故意,这是故意的 - 动态功能.我一直想写这个关于这个的博客.
首先,让我们退后一步.动态特性的基本思想是包含动态类型的操作数的表达式将其类型分析推迟到运行时.在运行时,通过启动新版本的编译器并重新进行分析来完成类型分析,这次将动态表达式视为其实际运行时类型的表达式.
所以如果你有一个加法表达式,它在编译时有一个左手编译时类型的对象,和一个右手编译时类型的动态,并且在运行时动态表达式实际上是一个字符串,那么分析是重新完成左侧是对象,右侧是字符串. 请注意,不考虑左侧的运行时类型.它的编译时类型是对象,而不是动态的.只有动态类型的表达式具有在运行时分析中使用其运行时类型的属性.
只是为了确保清楚:如果你有:
void M(Giraffe g, Apple a) {...}
void M(Animal a, Fruit f) { ... }
...
Animal x = new Giraffe();
dynamic y = new Apple();
M(x, y);
Run Code Online (Sandbox Code Playgroud)
然后在运行时调用第二个覆盖.在运行时x是Giraffe的事实被忽略了,因为它不是动态的.它在编译时是Animal,因此在运行时它继续被分析为Animal类型的表达式.也就是说,分析就像你说过的那样:
M(x, (Apple)y);
Run Code Online (Sandbox Code Playgroud)
而这显然是第二次超载.
我希望这很清楚.
现在我们来讨论这个问题.如果无法访问运行时类型会发生什么? 我们实际上是一个例子:
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);
Run Code Online (Sandbox Code Playgroud)
好的,会发生什么?我们有两个动态表达式,所以根据我之前的声明,在运行时我们再次进行分析,但假装你说
((Apple.MagicApple)d1).M((Giraffe)d2));
Run Code Online (Sandbox Code Playgroud)
所以你会认为重载决策会选择Apple.MagicApple.M与之完全匹配的方法.但事实并非如此!我们不能假装上面的代码就是您所说的,因为该代码访问其可访问域之外的私有嵌套类型!该代码无法完全编译.但同样明显的是,我们不能允许此代码失败,因为这是一种常见的情况.
所以我必须修改我之前的发言.运行时分析引擎实际上做的是假装您插入了可以合法插入的强制转换.在这种情况下,它意识到用户可以插入:
((Apple)d1).M((Giraffe)d2));
Run Code Online (Sandbox Code Playgroud)
并且会选择重载决策Apple.M.
而且:假装演员阵容总是属于班级类型.可能存在可能已插入的接口类型或类型参数类型转换,这将导致重载解析成功,但是通过使用"dynamic",您指示您希望使用运行时类型,以及运行时类型对象永远不是接口或类型参数类型.
听起来你在同一条船上.如果在调用站点上无法访问动态表达式的运行时类型,那么为了运行时分析的目的,它将被视为最接近的可访问基类型.在您的情况下,最接近的可访问基类型可能是对象.
这一切都清楚了吗?