gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 16:50:14 25 4
gpt4 key购买 nike

问题:我想为一个有很多静态类和静态方法的应用程序编写测试,因此不可能一步将一个经常使用的类切换到依赖注入(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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com