मैं इसे यहां डंप करने जा रहा हूं। मैंने इसे थोड़ी देर पहले लिखा था, लेकिन आईआईआरसी यह ठीक काम करता है।
एक सहायक समारोह एक MethodInfo
लेने के लिए और एक मिलान Func
या Action
के Type
वापस जाने के लिए सबसे पहले। दुर्भाग्यवश, आपको प्रत्येक पैरामीटर के लिए एक शाखा की आवश्यकता है, और मैं स्पष्ट रूप से तीन पर रुक गया।
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
कि इंटरफ़ेस को लागू करता है और एक निर्माता है रिटर्न प्रत्येक विधि/गेटर के लिए एक Func
या Action
लेने (Activator.CreateInstance
के माध्यम से कॉल किया जाना चाहिए)/सेटर। हालांकि, उन्हें कन्स्ट्रक्टर में रखने के लिए आपको सही क्रम जानने की जरूरत है। वैकल्पिक रूप से (टिप्पणी-आउट कोड) यह एक डीएलएल उत्पन्न कर सकता है जिसे आप संदर्भित कर सकते हैं और सीधे प्रकार का उपयोग कर सकते हैं।
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
);
यह विचार कल मेरे मन के लिए आया था और मैं एक वर्ग 'बेनामी' बनाने के लिए और Reflection.Emit' का उपयोग 'इस तरह के एक वर्ग इंटरफ़ेस टी को लागू करने का निर्माण करने की कोशिश की लेकिन यह tooooo महंगा है! तब मैंने एसओ पर अपनी किस्मत की कोशिश की और यह सवाल पाया, क्या अब तक आपकी कोई प्रगति है? –