【发布时间】:2017-11-24 10:24:11
【问题描述】:
我可以轻松地将对内部方法的调用封装在委托中。然后,当我调用Invoke() 时,委托可以调用此方法。但是,委托位于mscorlib 程序集中。为什么它可以从我的程序集中调用内部方法?
显然,委托必须能够做到这一点,C# 才能正常工作。问题不是为什么它是允许的。这是如何。
我认为检查可见性是 C# 的一项功能,并且直接从 CIL 调用该方法应该可以正常工作。所以我尝试从动态定义的类型中调用该方法,从而直接使用 CIL 并跳过 C#。它惨败。代码如下:
public static class InternalCall
{
internal static void InternalMethod()
{
Debug.WriteLine("Successfully called an internal method from: " + typeof(InternalCall).Assembly.FullName);
}
public interface IMyAction
{
void MyInvoke();
}
private static IMyAction MakeMyAction()
{
var assembly = Thread.GetDomain().DefineDynamicAssembly(new AssemblyName("Outside"), AssemblyBuilderAccess.Run);
var module = assembly.DefineDynamicModule("Outside", false);
var customType = module.DefineType("MyAction",
TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit,
typeof(object),
new[] { typeof(IMyAction) });
var method = customType.DefineMethod("MyInvoke",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Virtual | MethodAttributes.Final);
var il = method.GetILGenerator();
il.Emit(OpCodes.Call, typeof(InternalCall).GetMethod("InternalMethod", BindingFlags.Static | BindingFlags.NonPublic));
il.Emit(OpCodes.Ret);
return (IMyAction)customType.CreateType().GetConstructor(Type.EmptyTypes).Invoke(null);
}
public static void RunTest()
{
var action = new Action(InternalMethod);
Debug.WriteLine("Calling via action from assembly: " + action.GetType().Assembly.FullName);
action.Invoke();
var myAction = MakeMyAction();
Debug.WriteLine("Calling via my type from assembly: " + myAction.GetType().Assembly.FullName);
myAction.MyInvoke(); // MethodAccessException
}
}
那么,假设委托仍然遵守 CIL 规则(因为这是 C# 编译的内容),他们使用什么 CIL 机制来调用任何方法而不考虑可见性?
【问题讨论】:
-
在创建委托时检查可访问性,而不是在调用委托时检查。如果它不那样工作,它会严重吸巨石,事件处理程序应该始终是私有的。这个细节在 C# 中是非常不可见的,它的语法强制你使委托的目标总是等于 this。因此,无论可访问性说明符如何,它始终是可访问的。但这不是其他语言的要求,例如 C++/CLI。
-
这里有两个完全不同的问题,一个是问你如何调用一个指向调用委托的代码无法访问的方法的委托,另一个是问如何创建一个实现接口并允许调用者通过接口获取和使用它的实例的动态类。它们真的应该在不同的问题中,因为它们的答案完全不相关。
-
@Servy 更改了最后一行。这样好一点吗?
-
@HansPassant 我知道在创建委托时会检查可访问性,我只是不明白这是如何完成的。另外,this 是什么意思? Target 可以是任何东西,包括 null,就像上面代码中的情况一样。无论如何,Target 只是存储在委托对象中的引用,不是吗?委托是最终调用目标方法的人。
-
请更好地解释您的问题。在我看来,委托必须能够引用任何方法,包括私有或内部方法,这似乎是不言而喻的。毕竟,绝大多数时候,使用委托的全部原因是为一段代码提供一种方法来调用另一段代码中无法访问的部分。另一方面,关于为什么不能 compile 尝试访问不可访问成员的代码的第二个问题似乎也不言自明:可访问性的全部意义在于对访问提供编译时限制.
标签: c# delegates clr cil reflection.emit