问题描述
我想创建以下类:
public class MultiDataOrTrigger : DataTrigger
{
public MultiDataOrTrigger()
{
}
// evaluate the current state of the trigger
internal override bool GetCurrentState(DependencyObject container,UncommonField<HybridDictionary[]> dataField)
{
bool retVal = false;
for (int i = 0; !retVal && i < TriggerConditions.Length; i++)
{
retVal = TriggerConditions[i].ConvertAndMatch(StyleHelper.GetDataTriggerValue(dataField,container,TriggerConditions[i].Binding));
}
return retVal;
}
}
如何调用内部方法,创建内部类型的实例 - 我或多或少地弄清楚了。为此,使用反射对我来说就足够了。
但我仍然无法通过覆盖另一个程序集中指定的虚拟内部方法来解决这个问题。
更新
回复评论:
我知道这可以在没有这样的课程的情况下解决 - 我知道还有许多其他解决方案或变通方法。我自己在实践中使用了其中的许多。我现在没有停止任何开发,因为我不知道如何解决这个问题。
解决方法
如果这是您真正想要做的,那么是的,可以使用反射发射覆盖另一个程序集中的内部方法。
如果您阅读 CLI specification (ECMA-335)(特别是第 II.10.3.3 节“可访问性和覆盖”),您会发现:
[注意:一个方法可以被覆盖,即使它可能不被派生类访问。
如果一个方法具有程序集可访问性,那么它应该具有公共可访问性,如果它被一个方法在不同的程序集中。类似的规则适用于 famandassem,其中 famorassem 也允许在集会之外。在这两种情况下,可以分别在同一程序集中使用 assembly 或 famandassem。尾注]
(此处,assembly
、famandassem
和 famorassem
分别对应于 C# internal
、protected private
和 protected internal
。)
但是有一个问题。在同一部分,您还会发现:
如果指定了严格标志(§II.23.1.10),则只能覆盖可访问的虚拟方法。
C# 编译器在所有非公共虚拟方法上设置此标志。因此,您不能从 C# 编译的程序集扩展类并覆盖在该程序集中声明的内部方法,即使使用反射发出,除非您能够从该方法中删除严格标志(也许使用二进制编辑器,并且如果程序集是强命名,那么这将使签名无效)。但是你可以用反射发射创建两个程序集,在第一个程序集中定义一个带有虚拟内部方法的基类,并在第二个程序集中扩展类并覆盖方法,这可以用这个来演示代码:
using System;
using System.Reflection;
using System.Reflection.Emit;
public interface IBase {
void X();
}
class Program {
public static void Main() {
ILGenerator ilGenerator;
var assembly1 = AssemblyBuilder.DefineDynamicAssembly(
new AssemblyName("EmittedAssembly1"),AssemblyBuilderAccess.Run
);
var module1 = assembly1.DefineDynamicModule("EmittedModule1");
// Define the base class.
var typeBuilderBase = module1.DefineType("Base",TypeAttributes.Public);
typeBuilderBase.DefineDefaultConstructor(MethodAttributes.Public);
typeBuilderBase.AddInterfaceImplementation(typeof(IBase));
// This is the internal method that will be overridden.
var methodBuilderBaseX = typeBuilderBase.DefineMethod(
"X",MethodAttributes.Assembly | MethodAttributes.Virtual | MethodAttributes.NewSlot,typeof(void),Array.Empty<Type>()
);
ilGenerator = methodBuilderBaseX.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldstr,"X from Base");
ilGenerator.Emit(OpCodes.Call,typeof(Console).GetMethod("WriteLine",new[] {typeof(string)}));
ilGenerator.Emit(OpCodes.Ret);
// Define an explicit interface implementation that will be used to call
// Base.X() from the created instance of Derived.
var methodBuilderBaseInterfaceX = typeBuilderBase.DefineMethod(
"IBase.X",MethodAttributes.Private | MethodAttributes.Virtual,Array.Empty<Type>()
);
ilGenerator = methodBuilderBaseInterfaceX.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Callvirt,methodBuilderBaseX);
ilGenerator.Emit(OpCodes.Ret);
typeBuilderBase.DefineMethodOverride(methodBuilderBaseInterfaceX,typeof(IBase).GetMethod("X"));
typeBuilderBase.CreateType();
// This is the assembly in which the internal method will be overridden.
var assembly2 = AssemblyBuilder.DefineDynamicAssembly(
new AssemblyName("EmittedAssembly2"),AssemblyBuilderAccess.Run
);
var module2 = assembly2.DefineDynamicModule("EmittedModule2");
var typeBuilderDerived = module2.DefineType("Derived",TypeAttributes.Public);
typeBuilderDerived.SetParent(typeBuilderBase);
typeBuilderDerived.DefineDefaultConstructor(MethodAttributes.Public);
// Override the internal method in Base. Note that the accessibility of the overridden
// method must be public.
var methodBuilderDerivedX = typeBuilderDerived.DefineMethod(
"X",MethodAttributes.Public | MethodAttributes.Virtual,Array.Empty<Type>()
);
ilGenerator = methodBuilderDerivedX.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldstr,"X from Derived");
ilGenerator.Emit(OpCodes.Call,new[] {typeof(string)}));
ilGenerator.Emit(OpCodes.Ret);
var typeDerived = typeBuilderDerived.CreateType();
// Create an instance of the emitted Derived type.
var instance = (IBase)typeDerived.GetConstructor(Array.Empty<Type>()).Invoke(Array.Empty<object>());
// Call the overridden method. This outputs "X from Derived"!
instance.X();
}
}
如果在 MethodAttributes.CheckAccessOnOverride
中的 strict
的定义中添加 X
(即 Base
标志),您将收到此错误(与尝试使用 C# 编译类型执行此操作时会得到):
未处理的异常。 System.TypeLoadException:来自程序集“EmittedAssembly2,版本=0.0.0.0,Culture=neutral,PublicKeyToken=null”的类型“Derived”上的方法“X”正在覆盖该程序集中不可见的方法。