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