我正在使用对象工厂通过接口在反射的帮助下动态创建实例。
我的代码:
public static class ObjectFactory
{
private static readonly ConcurrentDictionary<Type, Type> TypeCache = new ConcurrentDictionary<Type, Type>();
public static T CreateInstance<T>(Type parent = null)
{
if (!typeof(T).IsInterface) throw new ArgumentException($"Type {typeof(T).Name} must be an interface.");
var newType = TypeCache.GetOrAdd(typeof(T), t => BuildType(typeof(T), parent));
return (T)Activator.CreateInstance(newType);
}
private static Type BuildType(Type interfaceType, Type parent = null)
{
var assemblyName = new AssemblyName($"DynamicAssembly_{Guid.NewGuid():N}");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicModule");
var typeName = $"{RemoveInterfacePrefix(interfaceType.Name)}_{Guid.NewGuid():N}";
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
if (parent != null)
typeBuilder.SetParent(parent);
typeBuilder.AddInterfaceImplementation(interfaceType);
var properties = interfaceType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var methods = interfaceType.GetMethods(BindingFlags.Instance | BindingFlags.Public);
BuildProperties(typeBuilder, properties);
BuildMethods(typeBuilder, methods);
return typeBuilder.CreateType();
string RemoveInterfacePrefix(string name) => Regex.Replace(name, "^I", string.Empty);
}
private static void BuildMethods(TypeBuilder typeBuilder, IEnumerable<MethodInfo> methods)
{
foreach (var method in methods)
{
var paramTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
AddMethodDynamically(typeBuilder, method.Name, paramTypes, method.ReturnType, "A");
}
}
private static void BuildProperties(TypeBuilder typeBuilder, IEnumerable<PropertyInfo> properties)
{
foreach (var property in properties)
{
BuildProperty(typeBuilder, property);
}
}
private static PropertyBuilder BuildProperty(TypeBuilder typeBuilder, PropertyInfo property)
{
var fieldName = $"<{property.Name}>k__BackingField";
var propertyBuilder = typeBuilder.DefineProperty(property.Name, System.Reflection.PropertyAttributes.None, property.PropertyType, Type.EmptyTypes);
// Build backing-field.
var fieldBuilder = typeBuilder.DefineField(fieldName, property.PropertyType, FieldAttributes.Private);
var getSetAttributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual;
var getterBuilder = BuildGetter(typeBuilder, property, fieldBuilder, getSetAttributes);
var setterBuilder = BuildSetter(typeBuilder, property, fieldBuilder, getSetAttributes);
propertyBuilder.SetGetMethod(getterBuilder);
propertyBuilder.SetSetMethod(setterBuilder);
return propertyBuilder;
}
private static MethodBuilder BuildGetter(TypeBuilder typeBuilder, PropertyInfo property, FieldBuilder fieldBuilder, MethodAttributes attributes)
{
var getterBuilder = typeBuilder.DefineMethod($"get_{property.Name}", attributes, property.PropertyType, Type.EmptyTypes);
var ilGenerator = getterBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, fieldBuilder);
if (property.GetCustomAttribute<NotNullAttribute>() != null)
{
// Build null check
ilGenerator.Emit(OpCodes.Dup);
var isFieldNull = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Brtrue_S, isFieldNull);
ilGenerator.Emit(OpCodes.Pop);
ilGenerator.Emit(OpCodes.Ldstr, $"{property.Name} isn't set.");
var invalidOperationExceptionConstructor = typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) });
ilGenerator.Emit(OpCodes.Newobj, invalidOperationExceptionConstructor);
ilGenerator.Emit(OpCodes.Throw);
ilGenerator.MarkLabel(isFieldNull);
}
ilGenerator.Emit(OpCodes.Ret);
return getterBuilder;
}
private static void AddMethodDynamically(TypeBuilder myTypeBld,
string mthdName,
Type[] mthdParams,
Type returnType,
string mthdAction)
{
MethodBuilder myMthdBld = myTypeBld.DefineMethod(
mthdName,
MethodAttributes.Public,
returnType,
mthdParams);
ILGenerator ILout = myMthdBld.GetILGenerator();
int numParams = mthdParams.Length;
for (byte x = 0; x < numParams; x++)
{
ILout.Emit(OpCodes.Ldarg_S, x);
}
if (numParams > 1)
{
for (int y = 0; y < (numParams - 1); y++)
{
switch (mthdAction)
{
case "A":
ILout.Emit(OpCodes.Add);
break;
case "M":
ILout.Emit(OpCodes.Mul);
break;
default:
ILout.Emit(OpCodes.Add);
break;
}
}
}
ILout.Emit(OpCodes.Ret);
}
private static MethodBuilder BuildSetter(TypeBuilder typeBuilder, PropertyInfo property, FieldBuilder fieldBuilder, MethodAttributes attributes)
{
var setterBuilder = typeBuilder.DefineMethod($"set_{property.Name}", attributes, null, new Type[] { property.PropertyType });
var ilGenerator = setterBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_1);
// Build null check
if (property.GetCustomAttribute<NotNullAttribute>() != null)
{
var isValueNull = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Dup);
ilGenerator.Emit(OpCodes.Brtrue_S, isValueNull);
ilGenerator.Emit(OpCodes.Pop);
ilGenerator.Emit(OpCodes.Ldstr, property.Name);
var argumentNullExceptionConstructor = typeof(ArgumentNullException).GetConstructor(new Type[] { typeof(string) });
ilGenerator.Emit(OpCodes.Newobj, argumentNullExceptionConstructor);
ilGenerator.Emit(OpCodes.Throw);
ilGenerator.MarkLabel(isValueNull);
}
ilGenerator.Emit(OpCodes.Stfld, fieldBuilder);
ilGenerator.Emit(OpCodes.Ret);
return setterBuilder;
}
}
方法BuildMethods
并未按接口创建实现方法。
请帮助解决2个问题:
public class Parent
{
public T Get<T, Y>(string url, params object[] query) => httpClient.Get<T>(url, query);
public T Post<T, Y>(string url, params Y model) => httpClient.Post<T>(url, model);
}
创建界面:
public inteface IRest
{
[HttpGet("/api/user-ids")] List<string> GetUserIds(int count, int skip);
}
我最终想要通过接口创建实例时,我得到了实例GetUserIds
从父类执行Get<T, Y>
的实例
var rest = ObjectFactory.CreateInstance<IRest>(typeof(Parent));
var userIds = rest.GetUserIds(10, 0);