diff --git a/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs b/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs index 8714ab18ef..9759621879 100644 --- a/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs @@ -52,7 +52,7 @@ namespace Google.Protobuf.Reflection throw new ArgumentException("Cannot read from property"); } this.descriptor = descriptor; - caseDelegate = ReflectionUtil.CreateFuncIMessageT(caseProperty.GetGetMethod()); + caseDelegate = ReflectionUtil.CreateFuncIMessageInt32(caseProperty.GetGetMethod()); this.descriptor = descriptor; clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod); diff --git a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs index df820ca36b..0d5ab7cd3f 100644 --- a/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs +++ b/csharp/src/Google.Protobuf/Reflection/ReflectionUtil.cs @@ -31,8 +31,6 @@ #endregion using System; -using System.Collections.Generic; -using System.Linq.Expressions; using System.Reflection; namespace Google.Protobuf.Reflection @@ -56,52 +54,74 @@ namespace Google.Protobuf.Reflection /// Creates a delegate which will cast the argument to the appropriate method target type, /// call the method on it, then convert the result to object. /// - internal static Func CreateFuncIMessageObject(MethodInfo method) - { - ParameterExpression parameter = Expression.Parameter(typeof(IMessage), "p"); - Expression downcast = Expression.Convert(parameter, method.DeclaringType); - Expression call = Expression.Call(downcast, method); - Expression upcast = Expression.Convert(call, typeof(object)); - return Expression.Lambda>(upcast, parameter).Compile(); - } + internal static Func CreateFuncIMessageObject(MethodInfo method) => + GetReflectionHelper(method.DeclaringType, method.ReturnType).CreateFuncIMessageObject(method); /// /// Creates a delegate which will cast the argument to the appropriate method target type, /// call the method on it, then convert the result to the specified type. /// - internal static Func CreateFuncIMessageT(MethodInfo method) - { - ParameterExpression parameter = Expression.Parameter(typeof(IMessage), "p"); - Expression downcast = Expression.Convert(parameter, method.DeclaringType); - Expression call = Expression.Call(downcast, method); - Expression upcast = Expression.Convert(call, typeof(T)); - return Expression.Lambda>(upcast, parameter).Compile(); - } + internal static Func CreateFuncIMessageInt32(MethodInfo method) => + GetReflectionHelper(method.DeclaringType, typeof(object)).CreateFuncIMessageInt32(method); /// /// Creates a delegate which will execute the given method after casting the first argument to /// the target type of the method, and the second argument to the first parameter type of the method. /// - internal static Action CreateActionIMessageObject(MethodInfo method) - { - ParameterExpression targetParameter = Expression.Parameter(typeof(IMessage), "target"); - ParameterExpression argParameter = Expression.Parameter(typeof(object), "arg"); - Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType); - Expression castArgument = Expression.Convert(argParameter, method.GetParameters()[0].ParameterType); - Expression call = Expression.Call(castTarget, method, castArgument); - return Expression.Lambda>(call, targetParameter, argParameter).Compile(); - } + internal static Action CreateActionIMessageObject(MethodInfo method) => + GetReflectionHelper(method.DeclaringType, method.GetParameters()[0].ParameterType).CreateActionIMessageObject(method); /// /// Creates a delegate which will execute the given method after casting the first argument to /// the target type of the method. /// - internal static Action CreateActionIMessage(MethodInfo method) + internal static Action CreateActionIMessage(MethodInfo method) => + GetReflectionHelper(method.DeclaringType, typeof(object)).CreateActionIMessage(method); + + /// + /// Creates a reflection helper for the given type arguments. Currently these are created on demand + /// rather than cached; this will be "busy" when initially loading a message's descriptor, but after that + /// they can be garbage collected. We could cache them by type if that proves to be important, but creating + /// an object is pretty cheap. + /// + private static IReflectionHelper GetReflectionHelper(Type t1, Type t2) => + (IReflectionHelper) Activator.CreateInstance(typeof(ReflectionHelper<,>).MakeGenericType(t1, t2)); + + // Non-generic interface allowing us to use an instance of ReflectionHelper without statically + // knowing the types involved. + private interface IReflectionHelper + { + Func CreateFuncIMessageInt32(MethodInfo method); + Action CreateActionIMessage(MethodInfo method); + Func CreateFuncIMessageObject(MethodInfo method); + Action CreateActionIMessageObject(MethodInfo method); + } + + private class ReflectionHelper : IReflectionHelper { - ParameterExpression targetParameter = Expression.Parameter(typeof(IMessage), "target"); - Expression castTarget = Expression.Convert(targetParameter, method.DeclaringType); - Expression call = Expression.Call(castTarget, method); - return Expression.Lambda>(call, targetParameter).Compile(); - } + public Func CreateFuncIMessageInt32(MethodInfo method) + { + var del = (Func) method.CreateDelegate(typeof(Func)); + return message => del((T1) message); + } + + public Action CreateActionIMessage(MethodInfo method) + { + var del = (Action) method.CreateDelegate(typeof(Action)); + return message => del((T1) message); + } + + public Func CreateFuncIMessageObject(MethodInfo method) + { + var del = (Func) method.CreateDelegate(typeof(Func)); + return message => del((T1) message); + } + + public Action CreateActionIMessageObject(MethodInfo method) + { + var del = (Action) method.CreateDelegate(typeof(Action)); + return (message, arg) => del((T1) message, (T2) arg); + } + } } } \ No newline at end of file