2

假设我有一个接口,它指定了两个没有参数的 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);
    }
}
4

2 回答 2

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(); }
}

Then , in your example:

public void PushFoo(Action bar1, Action bar2)
{
    IFoo foo = new ActionableFoo(bar1, bar2);
    _fooStack.Push(foo);
}
于 2012-07-12T08:18:04.657 回答
1

这激起了我的兴趣,所以这里有一个方法,它使用反射来围绕一组也实现给定接口的委托(或者更确切地说Func/ s)构建一个包装器。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.

于 2012-07-12T13:07:02.467 回答