publicinterfaceIFooService { voidMethodWithNoReturn(); intMethodTakeParameterAndReturn(int a, int b); }
对于不同的后端,需要有具体的调用实现:
1 2 3 4 5 6 7 8 9
publicclassFooProxyBase : ProxyBase { protectedoverrideobjectInvoke(object someMethodRelatedInfo, object[] arguments) { // Pack to JSON and send via http // Or adapte and call other classes // Or whatever } }
最终的Proxy类通过继承调用实现类,同时实现服务约定接口实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
public class FooService : FooProxyBase, IFooService { #region Implement IFooService public void MethodWithNoReturn() { Invoke("MethodWithNoReturn", new object[0]); }
public int MethodTakeParameterAndReturn(int a, int b) { return Invoke("MethodTakeParameterAndReturn", new object[] { a, b }); } #endregion }
这样一来有一个显然的问题,Proxy类包含大量重复的代码,方法越多实现起来越费劲。这个问题的point of interest就在于Proxy类的动态生成,实现以后只需要一行代码就能替代人肉实现一个巨大的Proxy类:
1
IFooService proxy = ProxyEmitter.CreateProxy<FooProxyBase, IFooService>(/*Constructor parameters are supported*/);
privatestaticvoidEmitCtor(TypeBuilder tBuilder, ConstructorInfo ctor) { var pTypes = ctor.GetParameters().Select(p => p.ParameterType).ToArray(); var builder = Emitter.GetConstructor( tBuilder, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, pTypes ); var ilGen = builder.GetILGenerator();
// No locals
// Load all args, note arg 0 is this pointer, so must emit one more for (int i = 0; i <= pTypes.Length; i++) { DoEmit(ilGen, OpCodes.Ldarg_S, i); } // Call base ctor DoEmit(ilGen, OpCodes.Call, ctor);
```cs // Now fill the array for (int i = 0; i < pTypes.Length; i++) { // Load the array first // IL_000X + 00: ldloc.0 DoEmit(ilGen, OpCodes.Ldloc_0);
// Push the index // IL_000X + 01: ldc_i4_x DoEmit(ilGen, OpCodes.Ldc_I4_S, i); // Load argument i + 1 (note that argument 0 is this pointer(?)) // IL_000X + 02: ldarg_X DoEmit(ilGen, OpCodes.Ldarg_S, i + 1); // Box value type if (pTypes[i].IsValueType) { // IL_000X + 03: box pTypes[i] DoEmit(ilGen, OpCodes.Box, pTypes[i]); } // Set arrary element // IL_00X + ??: stelem.ref DoEmit(ilGen, OpCodes.Stelem_Ref); }