C# 从接口为静态类创建动态适配器

C# 从接口为静态类创建动态适配器,c#,reflection,C#,Reflection,问题:我想为一个有许多静态类和静态方法的应用程序编写测试,所以在一个步骤中将一个常用的类切换到依赖注入是不可能的 所以我想保留静态类,创建一个适配器类,该类带有调用静态方法的接口,这样我就可以一步一步地使用该接口进行依赖项注入。(如本文所述:) 但我不想为所有静态类编写这么多适配器类,所以我的问题是是否可以编写一个工厂,为给定的接口类型和给定的目标静态类类型创建适配器类,例如: // this is the problem public static class CalculatorStatic

问题:我想为一个有许多静态类和静态方法的应用程序编写测试,所以在一个步骤中将一个常用的类切换到依赖注入是不可能的

所以我想保留静态类,创建一个适配器类,该类带有调用静态方法的接口,这样我就可以一步一步地使用该接口进行依赖项注入。(如本文所述:)

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

// 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
     }
}
//这就是问题所在
公共静态类计算器静态{
公共静态整数复杂计算(整数a、整数b){
返回a+b;
}
}
//我会写这个
公共接口计算器{
int ComplexCalculation(int a,int b);
}
//我不想写这个
公共类计算器适配器:ICalculator{
公共整数复杂计算(整数a、整数b){
返回计算器静态复杂计算(a,b);
}
}
//这将为我创建所有适配器
公共级适配器工厂{
公共T CreateAdapter(类型staticClassType){//T是接口类型
//执行一些魔术并返回一个动态创建的适配器
//实现接口并调用静态类的
}
}

我建议将委托作为适配器返回,而不是返回接口

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();
}
和计算器适配器:

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);
    }
}
私有类计算器适配器:ICalculator
{
专用只读函数complexCalculationAdapter;
内部计算器适配器(类型staticClassType)
{
complexCalculationAdapter=CreateAdapter(staticClassType,
名称(ICalculator.ComplexCalculation));
//TODO:如果有更多接口方法,则初始化其他字段
}
公共整数复杂计算(整数a、整数b)
{
返回complexCalculationAdapter(a,b);
}
}
更新

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

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

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());
}
公共静态T CreateAdapter(类型staticClassType)
{
AssemblyBuilder ab=AppDomain.CurrentDomain.DefinedDynamicAssembly(新的AssemblyName(类型为(T).Name+“适配器”),
AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb=ab.definedDynamicModule(typeof(T).Name+“Adapter.dll”);
//公共类TAdapter:T
TypeBuilder tb=mb.DefineType(typeof(T).Name+“适配器”,TypeAttributes.Public | TypeAttributes.Class,
typeof(object),新类型[]{typeof(T)};
//创建方法
foreach(typeof(T).GetMethods()中的var methodInfo)
{
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.hidebysing | MethodAttributes.NewSlot,
methodInfo.ReturnType、ParameterType);
//添加参数

对于(int i=0;您考虑过代码生成吗?对我来说,这似乎是一个完美的解决方案-非常简单(使用反射或Roslyn),并且它维护编译时检查和完整的IntelliSense支持。是的,我目前正在寻找PostSharp,用于类型生成。我并不真正需要适配器类的编译时检查,因为我将只使用引用接口。也许Castle Dynamic Proxy可能是一个解决方案,但我愿意接受任何可以很好工作的解决方案r、 但是我认为OP明确地想要使用接口,这样他就可以用这个接口的实现来代替静态方法调用,并在长期内使用DI来改进面向对象的设计。好的,我编辑了答案。仍然没有OP想要的那么普遍,但现在每个接口实现一个工厂就足够了。答案不错,但我ant为所有接口使用一个接口和一个适配器,而不是为每个静态接口编写两个额外的文件class@JanS:不,您不需要为每个静态类编写两个附加文件!只要它们“实现”同一接口,您可以使用同一适配器。您只需要为新接口编写新适配器。@taffer但每个静态类都有不同的方法,需要自己的接口
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);
    }
}
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());
}