2

パラメータなしで2つのvoidメソッドを指定するインターフェイスがあるとします。System.Action(T)インターフェイスを実装するクラスの2つのメソッドを「プラグイン」するにはどうすればよいですか?以下の私の例では、これは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(); }
}

次に、あなたの例では:

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 に答える