Я собираюсь сбросить это здесь. Я написал это некоторое время назад, но IIRC работает нормально.
Сначала вспомогательная функция берет MethodInfo
и возвращает Type
соответствующего Func
или Action
. К сожалению, вам нужна ветвь для каждого количества параметров, и я, по-видимому, остановился на трех.
static Type GenerateFuncOrAction(MethodInfo method)
{
var typeParams = method.GetParameters().Select(p => p.ParameterType).ToArray();
if (method.ReturnType == typeof(void))
{
if (typeParams.Length == 0)
{
return typeof(Action);
}
else if (typeParams.Length == 1)
{
return typeof(Action<>).MakeGenericType(typeParams);
}
else if (typeParams.Length == 2)
{
return typeof(Action<,>).MakeGenericType(typeParams);
}
else if (typeParams.Length == 3)
{
return typeof(Action<,,>).MakeGenericType(typeParams);
}
throw new ArgumentException("Only written up to 3 type parameters");
}
else
{
if (typeParams.Length == 0)
{
return typeof(Func<>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray());
}
else if (typeParams.Length == 1)
{
return typeof(Func<,>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray());
}
else if (typeParams.Length == 2)
{
return typeof(Func<,,>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray());
}
else if (typeParams.Length == 3)
{
return typeof(Func<,,,>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray());
}
throw new ArgumentException("Only written up to 3 type parameters");
}
}
А теперь метод, который принимает интерфейс в качестве универсального параметра и возвращает Type
, который реализует интерфейс и имеет конструктор (должен вызываться через Activator.CreateInstance
), принимая Func
или Action
для каждого метод / получатель / установщик. Вы должны знать правильный порядок, чтобы поместить их в конструктор. В качестве альтернативы (закомментированный код) он может сгенерировать DLL, на которую вы затем можете ссылаться и напрямую использовать тип.
static Type GenerateInterfaceImplementation<TInterface>()
{
var interfaceType = typeof(TInterface);
var funcTypes = interfaceType.GetMethods().Select(GenerateFuncOrAction).ToArray();
AssemblyName aName =
new AssemblyName("Dynamic" + interfaceType.Name + "WrapperAssembly");
var assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
aName,
AssemblyBuilderAccess.Run/*AndSave*/); // to get a DLL
var modBuilder = assBuilder.DefineDynamicModule(aName.Name/*, aName.Name + ".dll"*/); // to get a DLL
TypeBuilder typeBuilder = modBuilder.DefineType(
"Dynamic" + interfaceType.Name + "Wrapper",
TypeAttributes.Public);
// Define a constructor taking the same parameters as this method.
var ctrBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
CallingConventions.Standard,
funcTypes);
// Start building the constructor.
var ctrGenerator = ctrBuilder.GetILGenerator();
ctrGenerator.Emit(OpCodes.Ldarg_0);
ctrGenerator.Emit(
OpCodes.Call,
typeof(object).GetConstructor(Type.EmptyTypes));
// For each interface method, we add a field to hold the supplied
// delegate, code to store it in the constructor, and an
// implementation that calls the delegate.
byte methodIndex = 0;
foreach (var interfaceMethod in interfaceType.GetMethods())
{
ctrBuilder.DefineParameter(
methodIndex + 1,
ParameterAttributes.None,
"del_" + interfaceMethod.Name);
var delegateField = typeBuilder.DefineField(
"del_" + interfaceMethod.Name,
funcTypes[methodIndex],
FieldAttributes.Private);
ctrGenerator.Emit(OpCodes.Ldarg_0);
ctrGenerator.Emit(OpCodes.Ldarg_S, methodIndex + 1);
ctrGenerator.Emit(OpCodes.Stfld, delegateField);
var metBuilder = typeBuilder.DefineMethod(
interfaceMethod.Name,
MethodAttributes.Public | MethodAttributes.Virtual |
MethodAttributes.Final | MethodAttributes.HideBySig |
MethodAttributes.NewSlot,
interfaceMethod.ReturnType,
interfaceMethod.GetParameters()
.Select(p => p.ParameterType).ToArray());
var metGenerator = metBuilder.GetILGenerator();
metGenerator.Emit(OpCodes.Ldarg_0);
metGenerator.Emit(OpCodes.Ldfld, delegateField);
// Generate code to load each parameter.
byte paramIndex = 1;
foreach (var param in interfaceMethod.GetParameters())
{
metGenerator.Emit(OpCodes.Ldarg_S, paramIndex);
paramIndex++;
}
metGenerator.EmitCall(
OpCodes.Callvirt,
funcTypes[methodIndex].GetMethod("Invoke"),
null);
metGenerator.Emit(OpCodes.Ret);
methodIndex++;
}
ctrGenerator.Emit(OpCodes.Ret);
// Add interface implementation and finish creating.
typeBuilder.AddInterfaceImplementation(interfaceType);
var wrapperType = typeBuilder.CreateType();
//assBuilder.Save(aName.Name + ".dll"); // to get a DLL
return wrapperType;
}
Вы можете использовать это, например,
public interface ITest
{
void M1();
string M2(int m2, string n2);
string prop { get; set; }
event test BoopBooped;
}
Type it = GenerateInterfaceImplementation<ITest>();
ITest instance = (ITest)Activator.CreateInstance(it,
new Action(() => {Console.WriteLine("M1 called"); return;}),
new Func<int, string, string>((i, s) => "M2 gives " + s + i.ToString()),
new Func<String>(() => "prop value"),
new Action<string>(s => {Console.WriteLine("prop set to " + s);}),
new Action<test>(eh => {Console.WriteLine(eh("handler added"));}),
new Action<test>(eh => {Console.WriteLine(eh("handler removed"));}));
// or with the generated DLL
ITest instance = new DynamicITestWrapper(
// parameters as before but you can see the signature
);