如何将委托分配为某些已实现接口的方法

时间:2012-07-12 08:14:08

标签: c# .net interface delegates

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

2 个答案:

答案 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提供接口方法的顺序。