ClayInteceptor.cs 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Linq;
  4. using System.Runtime.CompilerServices;
  5. using Castle.DynamicProxy;
  6. using Masuit.Tools.Dynamics.Implementation;
  7. using Microsoft.CSharp.RuntimeBinder;
  8. namespace Masuit.Tools.Dynamics;
  9. internal class ClayInterceptor : IInterceptor
  10. {
  11. private const string GetPrefix = "get_";
  12. private const string SetPrefix = "set_";
  13. public void Intercept(IInvocation invocation)
  14. {
  15. var invocationDestinedForSelf = ReferenceEquals(invocation.InvocationTarget, invocation.Proxy);
  16. if (!invocationDestinedForSelf)
  17. {
  18. invocation.Proceed();
  19. return;
  20. }
  21. if (invocation.Proxy is IClayBehaviorProvider behaviorProvider)
  22. {
  23. var invocationMethod = invocation.Method;
  24. switch (invocationMethod.IsSpecialName)
  25. {
  26. case true when invocationMethod.Name.StartsWith(GetPrefix):
  27. invocation.ReturnValue = behaviorProvider.Behavior.GetMember(() =>
  28. {
  29. invocation.Proceed();
  30. return invocation.ReturnValue;
  31. }, invocation.Proxy, invocationMethod.Name.Substring(GetPrefix.Length));
  32. AdjustReturnValue(invocation);
  33. return;
  34. case true when invocationMethod.Name.StartsWith(SetPrefix) && invocation.Arguments.Length == 1:
  35. invocation.ReturnValue = behaviorProvider.Behavior.SetMember(() =>
  36. {
  37. invocation.Proceed();
  38. return invocation.ReturnValue;
  39. }, invocation.Proxy, invocationMethod.Name.Substring(SetPrefix.Length), invocation.Arguments.Single());
  40. AdjustReturnValue(invocation);
  41. return;
  42. case false:
  43. invocation.ReturnValue = behaviorProvider.Behavior.InvokeMember(() =>
  44. {
  45. invocation.Proceed();
  46. return invocation.ReturnValue;
  47. }, invocation.Proxy, invocationMethod.Name, Arguments.From(invocation.Arguments, Enumerable.Empty<string>()));
  48. AdjustReturnValue(invocation);
  49. return;
  50. }
  51. }
  52. invocation.Proceed();
  53. }
  54. private static readonly ConcurrentDictionary<Type, CallSite<Func<CallSite, object, object>>> ConvertSites = new();
  55. private static void AdjustReturnValue(IInvocation invocation)
  56. {
  57. var methodReturnType = invocation.Method.ReturnType;
  58. if (methodReturnType == typeof(void))
  59. {
  60. return;
  61. }
  62. if (invocation.ReturnValue == null)
  63. {
  64. return;
  65. }
  66. var returnValueType = invocation.ReturnValue.GetType();
  67. if (methodReturnType.IsAssignableFrom(returnValueType))
  68. {
  69. return;
  70. }
  71. var callSite = ConvertSites.GetOrAdd(methodReturnType, x => CallSite<Func<CallSite, object, object>>.Create(Binder.Convert(CSharpBinderFlags.None, x, null)));
  72. invocation.ReturnValue = callSite.Target(callSite, invocation.ReturnValue);
  73. }
  74. }