c# - 从接口(interface)为静态类创建动态适配器

标签 c# reflection

问题:我想为一个有很多静态类和静态方法的应用程序编写测试,因此不可能一步将一个经常使用的类切换到依赖注入(inject)。

所以我想保留静态类,创建一个带有调用静态方法的接口(interface)的适配器类,这样我就可以逐步使用这个接口(interface)进行依赖注入(inject)。 (如此处解释:https://stackoverflow.com/a/2416447/1453662)

但我不想为所有静态类编写这么多适配器类,所以我的问题是是否可以编写一个工厂来为给定的接口(interface)类型和给定的目标静态类类型创建适配器类,例如:

// this is the problem
public static class CalculatorStatic {
     public static int ComplexCalculation(int a, int b) {
         return a + b;
     }
}

// I will write this
public interface ICalculator {
     int ComplexCalculation(int a, int b);
}

// I don't want to write this
public class CalculatorAdapter : ICalculator {
     public int ComplexCalculation(int a, int b) {
         return CalculatorStatic.ComplexCalculation(a, b);
     }
}

// This should create all adapters for me
public class AdapterFactory {
     public T CreateAdapter<T>(Type staticClassType) { // T is the InterfaceType
         // Do some magic and return a dynamically created adapter
         // that implements the interface and calls the static class
     }
}

最佳答案

我建议返回一个委托(delegate)作为适配器,而不是返回一个接口(interface)。

public static TFunc CreateAdapter<TFunc>(Type staticClass, string methodName)
{
    var method = staticClass.GetMethod(methodName,
        BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);

    var parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
    var methodParameters = new ParameterExpression[parameterTypes.Length];
    for (int i = 0; i < parameterTypes.Length; i++)
    {
        methodParameters[i] = Expression.Parameter(parameterTypes[i], "p" + i);
    }

    var lambda = Expression.Lambda<TFunc>(
        Expression.Call(null, method, methodParameters), methodParameters);
    return lambda.Compile();
}

然后像这样使用它:

var adapter = CreateAdapter<Func<int, int, int>>(typeof(CalculatorStatic),
    nameof(CalculatorStatic.ComplexCalculation));
Console.WriteLine(adapter(1, 2));

如果你真的非常想使用接口(interface)(因为它有不止一种方法),你应该为每个接口(interface)创建一个适配器工厂:

public ICalculator CreateAdapter(Type staticClassType)
{
    return new CalculatorAdapter(staticClassType);
}

// todo: factory methods for other interfaces, too

计算器适配器:

private class CalculatorAdapter: ICalculator
{
    private readonly Func<int, int, int> complexCalculationAdapter;

    internal CalculatorAdapter(Type staticClassType)
    {
        complexCalculationAdapter = CreateAdapter<Func<int, int, int>>(staticClassType,
            nameof(ICalculator.ComplexCalculation));
        // TODO: initialize the other fields if there are more interface methods
    }

    public int ComplexCalculation(int a, int b)
    {
        return complexCalculationAdapter(a, b);
    }
}

更新

如果您真的想为所有接口(interface)创建一个方法,您应该生成一个动态类。

请注意,这个例子并不完美。你应该缓存动态程序集和模块,而不是总是创建一个新的,如果你调用 ref/out 参数,你应该将它们分配回来,等等。但也许意图很明确。代码提示:编译直接实现接口(interface)的代码并将其反汇编以查看在生成器中发出什么代码。

public static T CreateAdapter<T>(Type staticClassType)
{
    AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(typeof(T).Name + "Adapter"),
        AssemblyBuilderAccess.RunAndSave);

    ModuleBuilder mb = ab.DefineDynamicModule(typeof(T).Name + "Adapter.dll");

    // public class TAdapter : T
    TypeBuilder tb = mb.DefineType(typeof(T).Name + "Adapter", TypeAttributes.Public | TypeAttributes.Class,
        typeof(object), new Type[] { typeof(T) });

    // creating methods
    foreach (var methodInfo in typeof(T).GetMethods())
    {
        var parameters = methodInfo.GetParameters();
        var parameterTypes = parameters.Select(p => p.ParameterType).ToArray();
        var method = tb.DefineMethod(methodInfo.Name,
            MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.HideBySig | MethodAttributes.NewSlot,
            methodInfo.ReturnType, parameterTypes);

        // adding parameters
        for (int i = 0; i <parameters.Length; i++)
        {
            method.DefineParameter(i + 1, parameters[i].Attributes, parameters[i].Name);
        }

        // calling the static method from the body and returning its result
        var staticMethod = staticClassType.GetMethod(methodInfo.Name, parameterTypes);
        var code = method.GetILGenerator();
        for (int i = 0; i < parameters.Length; i++)
        {
            code.Emit(OpCodes.Ldarg_S, i + 1);
        }
        code.Emit(OpCodes.Call, staticMethod);
        code.Emit(OpCodes.Ret);
    }

    return (T)Activator.CreateInstance(tb.CreateType());
}

关于c# - 从接口(interface)为静态类创建动态适配器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35334703/

相关文章:

C#初学者q关于dataGrid列

c# - 如何通过反射获取基本方法的 MethodInfo,而不是属性和事件?

c# - UserPasswordCredential .Net 标准

c# - 在服务器端生成私钥(证书)是个坏主意吗?

c# - c#中日志函数调用参数值和返回值

apache-flex - 如何在 Flex 3 中在运行时确定函数的参数计数?

Java反射,获取参数类型

go - 如何列出接口(interface)类型中的方法名称?

c# - WPF:从不同的 UserControls 访问 ObservableCollection

c# - 如何向表达式添加另一个条件?