假设我有一个接口,指定两个没有参数的void方法。如何在实现接口的某个类中“插入”两个System.Action(T)
方法?在下面的示例中,这将采用void PushFoo(Action bar1, Action bar2)
方法:
public interface IFoo
{
void Bar1();
void Bar2();
}
public class Bla
{
Stack<IFoo> _fooStack = new Stack<IFoo>();
public void PushFoo(IFoo foo)
{
_fooStack.Push(foo);
}
public void PushFoo(Action bar1, Action bar2)
{
IFoo foo = null;
// assign bar1 and bar2 to foo
//foo = ... ;
_fooStack.Push(foo);
}
}
答案 0 :(得分:6)
public Class ActionableFoo : IFoo
{
Action _bar1, _bar2;
public ActionableFoo(Action b1, Action b2)
{
_bar1 = b1;
_bar2 = b2;
}
public void Bar1() { if(_bar1 != null) _bar1(); }
public void Bar2() { if(_bar2 != null) _bar2(); }
}
然后,在你的例子中:
public void PushFoo(Action bar1, Action bar2)
{
IFoo foo = new ActionableFoo(bar1, bar2);
_fooStack.Push(foo);
}
答案 1 :(得分:1)
这引起了我的兴趣,所以这里有一个方法,它使用反射来构建一组代理(或者更确切地说是Func
/ Action
)的包装器,它也实现了给定的接口。
Type GenerateInterfaceImplementator<TInterface>()
{
var interfaceType = typeof(TInterface);
var funcTypes = interfaceType.GetMethods()
.Select(GenerateFuncOrAction).ToArray();
AssemblyName aName =
new AssemblyName("Dynamic" + interfaceType.Name + "WrapperAssembly");
var assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
aName,
AssemblyBuilderAccess.Run);
var modBuilder = assBuilder.DefineDynamicModule(aName.Name);
TypeBuilder typeBuilder = modBuilder.DefineType(
"Dynamic" + interfaceType.Name + "Wrapper",
TypeAttributes.Public);
// Define a constructor taking the same parameters as this method.
var ctrBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
CallingConventions.Standard,
funcTypes);
// Start building the constructor.
var ctrGenerator = ctrBuilder.GetILGenerator();
ctrGenerator.Emit(OpCodes.Ldarg_0);
ctrGenerator.Emit(
OpCodes.Call,
typeof(object).GetConstructor(Type.EmptyTypes));
// For each interface method, we add a field to hold the supplied
// delegate, code to store it in the constructor, and an
// implementation that calls the delegate.
byte methodIndex = 0;
foreach (var interfaceMethod in interfaceType.GetMethods())
{
ctrBuilder.DefineParameter(
methodIndex + 1,
ParameterAttributes.None,
"del_" + interfaceMethod.Name);
var delegateField = typeBuilder.DefineField(
"del_" + interfaceMethod.Name,
funcTypes[methodIndex],
FieldAttributes.Private);
ctrGenerator.Emit(OpCodes.Ldarg_0);
ctrGenerator.Emit(OpCodes.Ldarg_S, methodIndex + 1);
ctrGenerator.Emit(OpCodes.Stfld, delegateField);
var metBuilder = typeBuilder.DefineMethod(
interfaceMethod.Name,
MethodAttributes.Public | MethodAttributes.Virtual |
MethodAttributes.Final | MethodAttributes.HideBySig |
MethodAttributes.NewSlot,
interfaceMethod.ReturnType,
interfaceMethod.GetParameters()
.Select(p => p.ParameterType).ToArray());
var metGenerator = metBuilder.GetILGenerator();
metGenerator.Emit(OpCodes.Ldarg_0);
metGenerator.Emit(OpCodes.Ldfld, delegateField);
// Generate code to load each parameter.
byte paramIndex = 1;
foreach (var param in interfaceMethod.GetParameters())
{
metGenerator.Emit(OpCodes.Ldarg_S, paramIndex);
paramIndex++;
}
metGenerator.EmitCall(
OpCodes.Callvirt,
funcTypes[methodIndex].GetMethod("Invoke"),
null);
metGenerator.Emit(OpCodes.Ret);
methodIndex++;
}
ctrGenerator.Emit(OpCodes.Ret);
// Add interface implementation and finish creating.
typeBuilder.AddInterfaceImplementation(interfaceType);
var wrapperType = typeBuilder.CreateType();
// Return an instance using the constructor we created.
return wrapperType;
}
此处未显示函数Type GenerateFuncOrAction(MethodInfo method)
,因为它太可怕了 - 您必须打开方法所具有的参数数量,以及是否返回void。
生成器的调用如下:
public interface ITest
{
void M1();
string M2(int m2, string n2);
string P { get; set; }
}
...
var iType = GenerateInterfaceImplementator<ITest>();
var instance = (ITest)Activator.CreateInstance(iType,
new Action(() => { Console.WriteLine("M1 called"); return; }),
new Func<int, string, string>((ij, xjx) => xjx + ij.ToString()),
new Func<String>(() => "P getter called"),
new Action<string>(s => { Console.WriteLine(s); }));
instance.M1();
Console.WriteLine(instance.M2(6, "you are number "));
instance.P = "P setter called";
Console.WriteLine(instance.P);
这是我第一次真正使用Reflection.Emit
所以欢迎所有评论。
问题是您必须知道GetMethods
提供接口方法的顺序。