这是我的代码:
class Program
{
static void Main(string[] args)
{
dynamic param = null;
System.Diagnostics.Debug.Assert(whatever(param));
}
static bool whatever(object param)
{
return true;
}
}
当我运行它时,我得到带有以下消息的 RuntimeBinderException
:
Cannot dynamically invoke method 'Assert' because it has a Conditional attribute
是的,Assert()
上有 ConditionalAttribute
。然而,只有一个 whatever()
方法会返回 bool
,无论该方法接受什么。
运行时到底提示什么?为什么它不能使用 bool
并将其传递给 Assert()
?
Main
中的实际调用被转换为 CallSite
,因为您正在使用 动态参数
调用调用。 CallSite
完成所有需要的准备工作,以便在运行时 调用此方法。但是,问题是 Assert
有一个 Conditional
属性,这意味着它需要在编译时<将信息传递给编译器预处理器/强>
这blog post explains :
Conditional attributes can be placed on methods (and attributes in
whidbey) to instruct the compiler to conditionally remove calls to the
function if a symbol is not defined. This can be useful for debug-only
functionality, like Debug.Assert, which has a Conditional("DEBUG") on
it.
Conditional takes a string argument. If that string is defined (as
determined by the compiler's preprocessor), then the compiler emits
the method call. If the symbol is not defined, C# still compiles the
method, but does not compile the calls.
然后,为了加强我们的观点:
The Conditional attribute is entirely handled by the compiler without
any cooperation from the runtime. The method is still jitted normally,
but the compiler just doesn't emit the calls if the symbol is not
defined.
现在,我们发生了冲突。我们不能在运行时将参数传递给编译器预处理器(告诉它是否定义了“DEBUG”),只能在编译时传递,但该方法只会在运行时调用,因为那是当我们推断出我们的 dynamic
值的类型时。
这就是为什么 Binder 在运行时大喊这个方法实际上不能被调用,因为那样会破坏 ConditionalAttribute
。
奖励:
这就是实际调用的内容:
private static void Main(string[] args)
{
object param = null;
if (Program.<Main>o__SiteContainer0.<>p__Site1 == null)
{
Program.<Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, Type, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Assert", null, typeof(Program), new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
}));
}
Action<CallSite, Type, object> arg_CB_0 = Program.<Main>o__SiteContainer0.<>p__Site1.Target;
CallSite arg_CB_1 = Program.<Main>o__SiteContainer0.<>p__Site1;
Type arg_CB_2 = typeof(Debug);
if (Program.<Main>o__SiteContainer0.<>p__Site2 == null)
{
Program.<Main>o__SiteContainer0.<>p__Site2 = CallSite<Func<CallSite, Type, object, object>>.Create(Binder.InvokeMember(CSharpBinderFlags.None, "whatever", null, typeof(Program), new CSharpArgumentInfo[]
{
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.IsStaticType, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
}));
}
arg_CB_0(arg_CB_1, arg_CB_2, Program.<Main>o__SiteContainer0.<>p__Site2.Target(Program.<Main>o__SiteContainer0.<>p__Site2, typeof(Program), param));