瀏覽代碼

动态类型扩展

懒得勤快 2 年之前
父節點
當前提交
fce8064422
共有 27 個文件被更改,包括 1564 次插入4 次删除
  1. 6 0
      Masuit.Tools.Abstractions/Extensions/BaseType/ObjectExtensions.cs
  2. 152 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/ArrayBehavior.cs
  3. 23 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/ArrayFactoryBehavior.cs
  4. 50 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/ArrayPropAssignmentBehavior.cs
  5. 55 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/ClayFactoryBehavior.cs
  6. 222 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/InterfaceProxyBehavior.cs
  7. 53 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/NullBehavior.cs
  8. 31 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/NullResultBehavior.cs
  9. 71 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/PropBehavior.cs
  10. 56 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/Clay.cs
  11. 30 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/ClayActivator.cs
  12. 68 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/ClayBehavior.cs
  13. 80 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/ClayBehaviorCollection.cs
  14. 86 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/ClayInteceptor.cs
  15. 160 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/ClayMetaObject.cs
  16. 83 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/DefaultClayActivator.cs
  17. 22 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/DynamicFactory.cs
  18. 9 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/IClayActivator.cs
  19. 39 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/IClayBehavior.cs
  20. 6 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/IClayBehaviorProvider.cs
  21. 174 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/Implementation/Arguments.cs
  22. 8 0
      Masuit.Tools.Abstractions/Extensions/Dynamics/NullInstance.cs
  23. 2 0
      Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj
  24. 13 4
      Masuit.Tools.Abstractions/Reflection/ReflectionUtil.cs
  25. 12 0
      Masuit.Tools.Net45/Masuit.Tools.Net45.csproj
  26. 6 0
      Masuit.Tools/Masuit.Tools.csproj
  27. 47 0
      Test/Masuit.Tools.Abstractions.Test/Extensions/DynamicObjectTest.cs

+ 6 - 0
Masuit.Tools.Abstractions/Extensions/BaseType/ObjectExtensions.cs

@@ -3,6 +3,7 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Reflection;
+using Masuit.Tools.Dynamics;
 using Newtonsoft.Json.Linq;
 
 namespace Masuit.Tools
@@ -209,6 +210,11 @@ namespace Masuit.Tools
 
             return dictionary;
         }
+
+        public static dynamic ToDynamic(this object obj)
+        {
+            return DynamicFactory.WithObject(obj);
+        }
     }
 
     internal class ReferenceEqualityComparer : EqualityComparer<object>

+ 152 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/ArrayBehavior.cs

@@ -0,0 +1,152 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Masuit.Tools.Dynamics.Behaviors;
+
+internal class ArrayBehavior : ClayBehavior
+{
+    private readonly List<object> _data = new();
+
+    /// <inheritdoc />
+    public override object GetIndex(Func<object> proceed, object self, IEnumerable<string> keys)
+    {
+        return IfSingleInteger(keys, key => _data[key], proceed);
+    }
+
+    /// <inheritdoc />
+    public override object SetIndex(Func<object> proceed, object self, IEnumerable<string> keys, object value)
+    {
+        return IfSingleInteger(keys, key => _data[key] = value, proceed);
+    }
+
+    /// <inheritdoc />
+    public override object GetMember(Func<object> proceed, object self, string name)
+    {
+        switch (name)
+        {
+            case "Length":
+            case "Count":
+                return _data.Count;
+
+            case "GetEnumerator":
+                return new Clay(new InterfaceProxyBehavior(), new EnumeratorBehavior(_data.GetEnumerator()));
+        }
+        return proceed();
+    }
+
+    /// <inheritdoc />
+    public override object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        switch (name)
+        {
+            case "AddRange":
+                _data.AddRange(((IEnumerable)args.Single()).OfType<object>());
+                return self;
+
+            case "Add":
+                _data.AddRange(args);
+                return self;
+
+            case "Insert":
+                return IfInitialInteger(args, (index, arr) => { _data.InsertRange(index, arr); return self; }, proceed);
+            case "RemoveAt":
+                return IfSingleInteger(args, index => { _data.RemoveAt(index); return self; }, proceed);
+            case "Contains":
+                return IfSingleArgument(args, arg => _data.Contains(arg), proceed);
+
+            case "IndexOf":
+                return IfSingleArgument(args, arg => _data.IndexOf(arg), proceed);
+
+            case "Remove":
+                return IfSingleArgument(args, arg => _data.Remove(arg), proceed);
+
+            case "CopyTo":
+                return IfArguments<object[], int>(args, (array, arrayIndex) =>
+                {
+                    _data.CopyTo(array, arrayIndex);
+                    return self;
+                }, proceed);
+        }
+
+        if (!args.Any())
+        {
+            return GetMember(proceed, self, name);
+        }
+
+        return proceed();
+    }
+
+    private static object IfArguments<T1, T2>(IEnumerable<object> args, Func<T1, T2, object> func, Func<object> proceed)
+    {
+        var objects = args as List<object> ?? args.ToList();
+        return objects.Count != 2 ? proceed() : func((T1)objects[0], (T2)objects.Last());
+    }
+
+    private static object IfSingleArgument(IEnumerable<object> args, Func<object, object> func, Func<object> proceed)
+    {
+        var objects = args as List<object> ?? args.ToList();
+        return objects.Count == 1 ? func(objects[0]) : proceed();
+    }
+
+    private static object IfSingleInteger(IEnumerable<object> args, Func<int, object> func, Func<object> proceed)
+    {
+        var objects = args as List<object> ?? args.ToList();
+        return objects.Count != 1 ? proceed() : IfInitialInteger(objects, (index, _) => func(index), proceed);
+    }
+
+    private static object IfInitialInteger(IEnumerable<object> args, Func<int, IEnumerable<object>, object> func, Func<object> proceed)
+    {
+        var objects = args as List<object> ?? args.ToList();
+        if (objects.Count > 0)
+        {
+            var key = objects[0];
+            return key.GetType() != typeof(int) ? proceed() : func((int)key, objects.Skip(1));
+        }
+
+        return proceed();
+    }
+
+    private class EnumeratorBehavior : ClayBehavior
+    {
+        private readonly IEnumerator _enumerator;
+
+        public EnumeratorBehavior(IEnumerator enumerator)
+        {
+            _enumerator = enumerator;
+        }
+
+        public override object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+        {
+            switch (name)
+            {
+                case "MoveNext":
+                    return _enumerator.MoveNext();
+
+                case "Reset":
+                    _enumerator.Reset();
+                    return null;
+
+                case "Dispose":
+                    if (_enumerator is IDisposable disposable)
+                    {
+                        disposable.Dispose();
+                    }
+
+                    return null;
+            }
+            return proceed();
+        }
+
+        public override object GetMember(Func<object> proceed, object self, string name)
+        {
+            switch (name)
+            {
+                case "Current":
+                    return _enumerator.Current;
+            }
+            return proceed();
+        }
+    }
+}

+ 23 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/ArrayFactoryBehavior.cs

@@ -0,0 +1,23 @@
+using System;
+
+namespace Masuit.Tools.Dynamics.Behaviors;
+
+internal class ArrayFactoryBehavior : ClayBehavior
+{
+    /// <inheritdoc />
+    public override object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        if (name == "Array")
+        {
+            dynamic x = new Clay(
+                new InterfaceProxyBehavior(),
+                new PropBehavior(),
+                new ArrayPropAssignmentBehavior(),
+                new ArrayBehavior(),
+                new NullResultBehavior());
+            x.AddRange(args);
+            return x;
+        }
+        return proceed();
+    }
+}

+ 50 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/ArrayPropAssignmentBehavior.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Masuit.Tools.Dynamics.Behaviors;
+
+internal class ArrayPropAssignmentBehavior : ClayBehavior
+{
+    /// <inheritdoc />
+    public override object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        return IfSingleArray(args, arr =>
+        {
+            ((dynamic)self)[name] = arr;
+            return self;
+        }, () => IfTwoOrMoreArgs(args, arr =>
+        {
+            ((dynamic)self)[name] = arr;
+            return self;
+        }, proceed));
+    }
+
+    private static object IfTwoOrMoreArgs(IEnumerable<object> args, Func<dynamic, object> func, Func<object> proceed)
+    {
+        var objects = args as List<object> ?? args.ToList();
+        return objects.Count < 2 ? proceed() : func(NewArray().AddRange(objects));
+    }
+
+    private static object IfSingleArray(IEnumerable<object> args, Func<dynamic, object> func, Func<object> proceed)
+    {
+        var objects = args as List<object> ?? args.ToList();
+        if (objects.Count != 1)
+        {
+            return proceed();
+        }
+
+        var arr = objects.Single();
+        if (arr == null)
+        {
+            return proceed();
+        }
+
+        return arr is Array ? func(NewArray().AddRange(arr)) : proceed();
+    }
+
+    private static dynamic NewArray()
+    {
+        return new Clay(new InterfaceProxyBehavior(), new PropBehavior(), new ArrayPropAssignmentBehavior(), new ArrayBehavior(), new NullResultBehavior());
+    }
+}

+ 55 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/ClayFactoryBehavior.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using Microsoft.CSharp.RuntimeBinder;
+
+namespace Masuit.Tools.Dynamics.Behaviors;
+
+internal class ClayFactoryBehavior : ClayBehavior
+{
+    /// <inheritdoc />
+    public override object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        dynamic shape = new Clay(new InterfaceProxyBehavior(), new PropBehavior(), new ArrayPropAssignmentBehavior(), new NullResultBehavior());
+        shape.ShapeName = name;
+        if (args.Positional.Count() == 1)
+        {
+            var options = args.Positional.Single();
+            var assigner = GetAssigner(options.GetType());
+            assigner.Invoke(shape, options);
+        }
+
+        foreach (var kv in args.Named)
+        {
+            shape[kv.Key] = kv.Value;
+        }
+
+        return shape;
+    }
+
+    private static Action<dynamic, object> GetAssigner(Type sourceType)
+    {
+        lock (AssignerCache)
+        {
+            if (AssignerCache.TryGetValue(sourceType, out var assigner))
+            {
+                return assigner;
+            }
+
+            var targetParameter = Expression.Parameter(typeof(object), "target");
+            var sourceParameter = Expression.Parameter(typeof(object), "source");
+            var assignments = sourceType.GetProperties().Select(property => Expression.Dynamic(Binder.SetMember(CSharpBinderFlags.None, property.Name, typeof(void), new[]
+            {
+                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
+                CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
+            }), typeof(void), targetParameter, Expression.Property(Expression.Convert(sourceParameter, sourceType), property)));
+            var lambda = Expression.Lambda<Action<dynamic, object>>(Expression.Block(assignments), targetParameter, sourceParameter);
+            assigner = lambda.Compile();
+            AssignerCache.Add(sourceType, assigner);
+            return assigner;
+        }
+    }
+
+    private static readonly Dictionary<Type, Action<dynamic, object>> AssignerCache = new();
+}

+ 222 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/InterfaceProxyBehavior.cs

@@ -0,0 +1,222 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using Castle.DynamicProxy;
+using Microsoft.CSharp.RuntimeBinder;
+using Binder = Microsoft.CSharp.RuntimeBinder.Binder;
+
+namespace Masuit.Tools.Dynamics.Behaviors;
+
+internal class InterfaceProxyBehavior : ClayBehavior
+{
+    private static readonly IProxyBuilder ProxyBuilder = new DefaultProxyBuilder();
+    private static readonly MethodInfo DynamicMetaObjectProviderGetMetaObject = typeof(IDynamicMetaObjectProvider).GetMethod("GetMetaObject");
+
+    /// <inheritdoc />
+    public override object ConvertMissing(Func<object> proceed, object self, Type type, bool isExplicit)
+    {
+        if (!type.IsInterface || type == typeof(IDynamicMetaObjectProvider))
+        {
+            return proceed();
+        }
+
+        var proxyType = ProxyBuilder.CreateInterfaceProxyTypeWithoutTarget(type, new[]
+        {
+            typeof(IDynamicMetaObjectProvider)
+        }, ProxyGenerationOptions.Default);
+
+        var interceptors = new IInterceptor[]
+        {
+            new Interceptor(self)
+        };
+        return Activator.CreateInstance(proxyType, interceptors, self);
+    }
+
+    private class Interceptor : IInterceptor
+    {
+        private object Self { get; }
+
+        public Interceptor(object self)
+        {
+            Self = self;
+        }
+
+        public void Intercept(IInvocation invocation)
+        {
+            if (invocation.Method == DynamicMetaObjectProviderGetMetaObject)
+            {
+                var expression = (Expression)invocation.Arguments.Single();
+                invocation.ReturnValue = new ForwardingMetaObject(expression, BindingRestrictions.Empty, invocation.Proxy, (IDynamicMetaObjectProvider)Self, exprProxy => Expression.Field(exprProxy, "__target"));
+                return;
+            }
+
+            var invoker = BindInvoker(invocation);
+            invoker(invocation);
+
+            if (invocation.ReturnValue == null || invocation.Method.ReturnType.IsInstanceOfType(invocation.ReturnValue) || !(invocation.ReturnValue is IClayBehaviorProvider provider))
+            {
+                return;
+            }
+
+            var returnValueBehavior = provider.Behavior;
+            invocation.ReturnValue = returnValueBehavior.Convert(() => returnValueBehavior.ConvertMissing(() => invocation.ReturnValue, invocation.ReturnValue, invocation.Method.ReturnType, false), provider, invocation.Method.ReturnType, false);
+        }
+
+        private static readonly ConcurrentDictionary<MethodInfo, Action<IInvocation>> Invokers = new();
+
+        private static Action<IInvocation> BindInvoker(IInvocation invocation)
+        {
+            return Invokers.GetOrAdd(invocation.Method, CompileInvoker);
+        }
+
+        private static Action<IInvocation> CompileInvoker(MethodInfo method)
+        {
+            var methodParameters = method.GetParameters();
+            var invocationParameter = Expression.Parameter(typeof(IInvocation), "invocation");
+
+            var targetAndArgumentInfos = Pack(CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), methodParameters.Select(mp => CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, mp.Name))).ToArray();
+
+            var targetAndArguments = Pack<Expression>(Expression.Property(invocationParameter, invocationParameter.Type, "Proxy"), methodParameters.Select((mp, index) => Expression.Convert(Expression.ArrayIndex(Expression.Property(invocationParameter, invocationParameter.Type, "Arguments"), Expression.Constant(index)), mp.ParameterType))).ToArray();
+
+            Expression body = null;
+            if (method.IsSpecialName)
+            {
+                switch (method.Name)
+                {
+                    case "get_Item":
+                        body = Expression.Dynamic(Binder.GetIndex(CSharpBinderFlags.InvokeSpecialName, typeof(object), targetAndArgumentInfos), typeof(object), targetAndArguments);
+                        break;
+
+                    case "set_Item":
+                        var targetAndArgumentInfosWithoutTheNameValue = Pack(CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), methodParameters.Select(mp => mp.Name == "value" ? CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) : CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.NamedArgument, mp.Name)));
+                        body = Expression.Dynamic(Binder.SetIndex(CSharpBinderFlags.InvokeSpecialName, typeof(object), targetAndArgumentInfosWithoutTheNameValue), typeof(object), targetAndArguments);
+                        break;
+
+                    case { } s when s.StartsWith("get_"):
+                        body = Expression.Dynamic(Binder.GetMember(CSharpBinderFlags.InvokeSpecialName, method.Name.Substring("get_".Length), typeof(object), targetAndArgumentInfos), typeof(object), targetAndArguments);
+                        break;
+
+                    case { } s when s.StartsWith("set_"):
+                        body = Expression.Dynamic(Binder.SetMember(CSharpBinderFlags.InvokeSpecialName, method.Name.Substring("set_".Length), typeof(object), targetAndArgumentInfos), typeof(object), targetAndArguments);
+                        break;
+                }
+            }
+
+            body ??= Expression.Dynamic(Binder.InvokeMember(CSharpBinderFlags.None, method.Name, null, typeof(object), targetAndArgumentInfos), typeof(object), targetAndArguments);
+
+            if (method.ReturnType != typeof(void))
+            {
+                body = Expression.Assign(Expression.Property(invocationParameter, invocationParameter.Type, "ReturnValue"), Expression.Convert(body, typeof(object)));
+            }
+
+            var lambda = Expression.Lambda<Action<IInvocation>>(body, invocationParameter);
+            return lambda.Compile();
+        }
+    }
+
+    private static IEnumerable<T> Pack<T>(T t1, IEnumerable<T> t2)
+    {
+        if (!Equals(t1, default(T)))
+        {
+            yield return t1;
+        }
+
+        foreach (var t in t2)
+        {
+            yield return t;
+        }
+    }
+
+    /// <inheritdoc />
+    public sealed class ForwardingMetaObject : DynamicMetaObject
+    {
+        private readonly DynamicMetaObject _metaForwardee;
+
+        /// <inheritdoc />
+        public ForwardingMetaObject(Expression expression, BindingRestrictions restrictions, object forwarder, IDynamicMetaObjectProvider forwardee, Func<Expression, Expression> forwardeeGetter) : base(expression, restrictions, forwarder)
+        {
+            _metaForwardee = forwardee.GetMetaObject(forwardeeGetter(Expression.Convert(expression, forwarder.GetType())));
+        }
+
+        private DynamicMetaObject AddRestrictions(DynamicMetaObject result)
+        {
+            return new DynamicMetaObject(result.Expression, BindingRestrictions.GetTypeRestriction(Expression, Value.GetType()).Merge(result.Restrictions), _metaForwardee.Value);
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
+        {
+            return AddRestrictions(_metaForwardee.BindGetMember(binder));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
+        {
+            return AddRestrictions(_metaForwardee.BindSetMember(binder, value));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder)
+        {
+            return AddRestrictions(_metaForwardee.BindDeleteMember(binder));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes)
+        {
+            return AddRestrictions(_metaForwardee.BindGetIndex(binder, indexes));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value)
+        {
+            return AddRestrictions(_metaForwardee.BindSetIndex(binder, indexes, value));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes)
+        {
+            return AddRestrictions(_metaForwardee.BindDeleteIndex(binder, indexes));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
+        {
+            return AddRestrictions(_metaForwardee.BindInvokeMember(binder, args));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)
+        {
+            return AddRestrictions(_metaForwardee.BindInvoke(binder, args));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args)
+        {
+            return AddRestrictions(_metaForwardee.BindCreateInstance(binder, args));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder)
+        {
+            return AddRestrictions(_metaForwardee.BindUnaryOperation(binder));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)
+        {
+            return AddRestrictions(_metaForwardee.BindBinaryOperation(binder, arg));
+        }
+
+        /// <inheritdoc />
+        public override DynamicMetaObject BindConvert(ConvertBinder binder)
+        {
+            return AddRestrictions(_metaForwardee.BindConvert(binder));
+        }
+    }
+}

+ 53 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/NullBehavior.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+namespace Masuit.Tools.Dynamics.Behaviors;
+
+internal class NullBehavior : ClayBehavior
+{
+    /// <inheritdoc />
+    public override object GetMember(Func<object> proceed, object self, string name)
+    {
+        return NullInstance.Instance;
+    }
+
+    /// <inheritdoc />
+    public override object GetIndex(Func<object> proceed, object self, IEnumerable<string> keys)
+    {
+        return NullInstance.Instance;
+    }
+
+    /// <inheritdoc />
+    public override object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        if (args.Any())
+        {
+            return proceed();
+        }
+
+        return name == "ToString" ? string.Empty : NullInstance.Instance;
+    }
+
+    /// <inheritdoc />
+    public override object Convert(Func<object> proceed, object self, Type type, bool isExplicit)
+    {
+        return type.IsInterface ? proceed() : null;
+    }
+
+    /// <inheritdoc />
+    public override object BinaryOperation(Func<object> proceed, object self, ExpressionType operation, object value)
+    {
+        switch (operation)
+        {
+            case ExpressionType.Equal:
+                return ReferenceEquals(value, NullInstance.Instance) || value == null;
+
+            case ExpressionType.NotEqual:
+                return !ReferenceEquals(value, NullInstance.Instance) && value != null;
+        }
+
+        return proceed();
+    }
+}

+ 31 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/NullResultBehavior.cs

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Masuit.Tools.Dynamics.Behaviors;
+
+internal class NullResultBehavior : ClayBehavior
+{
+    /// <inheritdoc />
+    public override object GetMember(Func<object> proceed, object self, string name)
+    {
+        return proceed() ?? NullInstance.Instance;
+    }
+
+    /// <inheritdoc />
+    public override object GetIndex(Func<object> proceed, object self, IEnumerable<string> keys)
+    {
+        return proceed() ?? NullInstance.Instance;
+    }
+
+    /// <inheritdoc />
+    public override object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        if (args.Any())
+        {
+            return proceed();
+        }
+
+        return proceed() ?? NullInstance.Instance;
+    }
+}

+ 71 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/Behaviors/PropBehavior.cs

@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Masuit.Tools.Dynamics.Behaviors;
+
+internal class PropBehavior : ClayBehavior
+{
+    private readonly Dictionary<string, object> _props = new();
+
+    /// <inheritdoc />
+    public override object GetMembers(Func<object> proceed, object self, IDictionary<string, object> members)
+    {
+        foreach (var pair in _props)
+        {
+            members.Add(pair.Key, pair.Value);
+        }
+
+        return proceed();
+    }
+
+    /// <inheritdoc />
+    public override object GetMemberMissing(Func<object> proceed, object self, string name)
+    {
+        return _props.TryGetValue(name, out var value) ? value : null;
+    }
+
+    /// <inheritdoc />
+    public override object SetMemberMissing(Func<object> proceed, object self, string name, object value)
+    {
+        return _props[name] = value;
+    }
+
+    /// <inheritdoc />
+    public override object InvokeMemberMissing(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        if (!args.Any())
+        {
+            return GetMemberMissing(proceed, self, name);
+        }
+
+        if (args.Count() == 1)
+        {
+            SetMemberMissing(proceed, self, name, args.Single());
+            return self;
+        }
+
+        return proceed();
+    }
+
+    /// <inheritdoc />
+    public override object GetIndex(Func<object> proceed, object self, IEnumerable<string> keys)
+    {
+        var list = keys.ToList();
+        if (list.Count != 1) proceed();
+        return _props.TryGetValue(list[0], out var value) ? value : null;
+    }
+
+    /// <inheritdoc />
+    public override object SetIndex(Func<object> proceed, object self, IEnumerable<string> keys, object value)
+    {
+        var list = keys.ToList();
+        if (list.Count != 1) proceed();
+        return _props[list[0]] = value;
+    }
+
+    public Dictionary<string, object> GetProps()
+    {
+        return _props;
+    }
+}

+ 56 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/Clay.cs

@@ -0,0 +1,56 @@
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Linq.Expressions;
+using Masuit.Tools.Dynamics.Behaviors;
+using Masuit.Tools.Dynamics.Implementation;
+
+namespace Masuit.Tools.Dynamics;
+
+public class Clay : IDynamicMetaObjectProvider, IClayBehaviorProvider
+{
+    private readonly ClayBehaviorCollection _behavior;
+
+    public Clay() : this(Enumerable.Empty<IClayBehavior>())
+    {
+    }
+
+    public Clay(params IClayBehavior[] behaviors) : this(behaviors.AsEnumerable())
+    {
+    }
+
+    public Clay(IEnumerable<IClayBehavior> behaviors)
+    {
+        _behavior = new ClayBehaviorCollection(behaviors);
+    }
+
+    DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter)
+    {
+        return new ClayMetaObject(this, parameter);
+    }
+
+    IClayBehavior IClayBehaviorProvider.Behavior => _behavior;
+
+    /// <inheritdoc />
+    public override string ToString()
+    {
+        var fallback = base.ToString();
+        return _behavior.InvokeMember(() => fallback, this, "ToString", Arguments.Empty()) as string ?? string.Empty;
+    }
+
+    public Dictionary<string, object> ToDictionary()
+    {
+        return _behavior.OfType<PropBehavior>().First().GetProps();
+    }
+
+    /// <summary>
+    /// 移除属性
+    /// </summary>
+    /// <param name="left"></param>
+    /// <param name="right"></param>
+    public static Clay operator -(Clay left, string right)
+    {
+        left.ToDictionary().Remove(right);
+        return left;
+    }
+}

+ 30 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/ClayActivator.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+
+namespace Masuit.Tools.Dynamics;
+
+internal static class ClayActivator
+{
+    static ClayActivator()
+    {
+        var instance = new DefaultClayActivator();
+        ServiceLocator = () => instance;
+    }
+
+    public static Func<IClayActivator> ServiceLocator { get; set; }
+
+    public static dynamic CreateInstance(IEnumerable<IClayBehavior> behaviors, params object[] arguments)
+    {
+        return ServiceLocator().CreateInstance(typeof(Clay), behaviors, arguments);
+    }
+
+    public static dynamic CreateInstance(Type baseType, IEnumerable<IClayBehavior> behaviors, params object[] arguments)
+    {
+        return ServiceLocator().CreateInstance(baseType, behaviors, arguments);
+    }
+
+    public static dynamic CreateInstance<TBase>(IEnumerable<IClayBehavior> behaviors, params object[] arguments)
+    {
+        return ServiceLocator().CreateInstance(typeof(TBase), behaviors, arguments);
+    }
+}

+ 68 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/ClayBehavior.cs

@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+
+namespace Masuit.Tools.Dynamics;
+
+internal abstract class ClayBehavior : IClayBehavior
+{
+    public virtual object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        return proceed();
+    }
+
+    public virtual object GetMember(Func<object> proceed, object self, string name)
+    {
+        return proceed();
+    }
+
+    public virtual object SetMember(Func<object> proceed, object self, string name, object value)
+    {
+        return proceed();
+    }
+
+    public virtual object GetIndex(Func<object> proceed, object self, IEnumerable<string> keys)
+    {
+        return proceed();
+    }
+
+    public virtual object SetIndex(Func<object> proceed, object self, IEnumerable<string> keys, object value)
+    {
+        return proceed();
+    }
+
+    public virtual object Convert(Func<object> proceed, object self, Type type, bool isExplicit)
+    {
+        return proceed();
+    }
+
+    public virtual object BinaryOperation(Func<object> proceed, object self, ExpressionType operation, object value)
+    {
+        return proceed();
+    }
+
+    public virtual object GetMembers(Func<object> proceed, object self, IDictionary<string, object> members)
+    {
+        return proceed();
+    }
+
+    public virtual object InvokeMemberMissing(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        return proceed();
+    }
+
+    public virtual object GetMemberMissing(Func<object> proceed, object self, string name)
+    {
+        return proceed();
+    }
+
+    public virtual object SetMemberMissing(Func<object> proceed, object self, string name, object value)
+    {
+        return proceed();
+    }
+
+    public virtual object ConvertMissing(Func<object> proceed, object self, Type type, bool isExplicit)
+    {
+        return proceed();
+    }
+}

+ 80 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/ClayBehaviorCollection.cs

@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+
+namespace Masuit.Tools.Dynamics;
+
+internal class ClayBehaviorCollection : List<IClayBehavior>, IClayBehavior
+{
+    public ClayBehaviorCollection(IEnumerable<IClayBehavior> behaviors)
+        : base(behaviors)
+    {
+    }
+
+    private object Execute(Func<object> proceed, Func<Func<object>, IClayBehavior, Func<object>> linker)
+    {
+        return this.Aggregate(proceed, linker)();
+    }
+
+    /// <inheritdoc />
+    public object GetMember(Func<object> proceed, object self, string name)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.GetMember(next, self, name));
+    }
+
+    public object SetMember(Func<object> proceed, object self, string name, object value)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.SetMember(next, self, name, value));
+    }
+
+    public object GetMembers(Func<object> proceed, object self, IDictionary<string, object> members)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.GetMembers(next, self, members));
+    }
+
+    public object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.InvokeMember(next, self, name, args));
+    }
+
+    public object GetIndex(Func<object> proceed, object self, IEnumerable<string> keys)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.GetIndex(next, self, keys));
+    }
+
+    public object SetIndex(Func<object> proceed, object self, IEnumerable<string> keys, object value)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.SetIndex(next, self, keys, value));
+    }
+
+    public object Convert(Func<object> proceed, object self, Type type, bool isExplicit)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.Convert(next, self, type, isExplicit));
+    }
+
+    public object BinaryOperation(Func<object> proceed, object self, ExpressionType operation, object value)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.BinaryOperation(next, self, operation, value));
+    }
+
+    public object InvokeMemberMissing(Func<object> proceed, object self, string name, INamedEnumerable<object> args)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.InvokeMemberMissing(next, self, name, args));
+    }
+
+    public object GetMemberMissing(Func<object> proceed, object self, string name)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.GetMemberMissing(next, self, name));
+    }
+
+    public object SetMemberMissing(Func<object> proceed, object self, string name, object value)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.SetMemberMissing(next, self, name, value));
+    }
+
+    public object ConvertMissing(Func<object> proceed, object self, Type type, bool isExplicit)
+    {
+        return Execute(proceed, (next, behavior) => () => behavior.ConvertMissing(next, self, type, isExplicit));
+    }
+}

+ 86 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/ClayInteceptor.cs

@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Concurrent;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using Castle.DynamicProxy;
+using Masuit.Tools.Dynamics.Implementation;
+using Microsoft.CSharp.RuntimeBinder;
+
+namespace Masuit.Tools.Dynamics;
+
+internal class ClayInterceptor : IInterceptor
+{
+    private const string GetPrefix = "get_";
+    private const string SetPrefix = "set_";
+
+    public void Intercept(IInvocation invocation)
+    {
+        var invocationDestinedForSelf = ReferenceEquals(invocation.InvocationTarget, invocation.Proxy);
+        if (!invocationDestinedForSelf)
+        {
+            invocation.Proceed();
+            return;
+        }
+
+        if (invocation.Proxy is IClayBehaviorProvider behaviorProvider)
+        {
+            var invocationMethod = invocation.Method;
+            switch (invocationMethod.IsSpecialName)
+            {
+                case true when invocationMethod.Name.StartsWith(GetPrefix):
+                    invocation.ReturnValue = behaviorProvider.Behavior.GetMember(() =>
+                    {
+                        invocation.Proceed();
+                        return invocation.ReturnValue;
+                    }, invocation.Proxy, invocationMethod.Name.Substring(GetPrefix.Length));
+                    AdjustReturnValue(invocation);
+                    return;
+
+                case true when invocationMethod.Name.StartsWith(SetPrefix) && invocation.Arguments.Length == 1:
+                    invocation.ReturnValue = behaviorProvider.Behavior.SetMember(() =>
+                    {
+                        invocation.Proceed();
+                        return invocation.ReturnValue;
+                    }, invocation.Proxy, invocationMethod.Name.Substring(SetPrefix.Length), invocation.Arguments.Single());
+                    AdjustReturnValue(invocation);
+                    return;
+
+                case false:
+                    invocation.ReturnValue = behaviorProvider.Behavior.InvokeMember(() =>
+                    {
+                        invocation.Proceed();
+                        return invocation.ReturnValue;
+                    }, invocation.Proxy, invocationMethod.Name, Arguments.From(invocation.Arguments, Enumerable.Empty<string>()));
+                    AdjustReturnValue(invocation);
+                    return;
+            }
+        }
+
+        invocation.Proceed();
+    }
+
+    private static readonly ConcurrentDictionary<Type, CallSite<Func<CallSite, object, object>>> ConvertSites = new();
+
+    private static void AdjustReturnValue(IInvocation invocation)
+    {
+        var methodReturnType = invocation.Method.ReturnType;
+        if (methodReturnType == typeof(void))
+        {
+            return;
+        }
+
+        if (invocation.ReturnValue == null)
+        {
+            return;
+        }
+
+        var returnValueType = invocation.ReturnValue.GetType();
+        if (methodReturnType.IsAssignableFrom(returnValueType))
+        {
+            return;
+        }
+
+        var callSite = ConvertSites.GetOrAdd(methodReturnType, x => CallSite<Func<CallSite, object, object>>.Create(Binder.Convert(CSharpBinderFlags.None, x, null)));
+        invocation.ReturnValue = callSite.Target(callSite, invocation.ReturnValue);
+    }
+}

+ 160 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/ClayMetaObject.cs

@@ -0,0 +1,160 @@
+using System;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using Masuit.Tools.Dynamics.Implementation;
+
+namespace Masuit.Tools.Dynamics;
+
+internal class ClayMetaObject : DynamicMetaObject
+{
+    private static readonly MethodInfo ClayBehaviorInvokeMember = typeof(IClayBehavior).GetMethod("InvokeMember");
+
+    private static readonly MethodInfo ClayBehaviorGetMember = typeof(IClayBehavior).GetMethod("GetMember");
+    private static readonly MethodInfo ClayBehaviorSetMember = typeof(IClayBehavior).GetMethod("SetMember");
+    private static readonly MethodInfo ClayBehaviorGetIndex = typeof(IClayBehavior).GetMethod("GetIndex");
+    private static readonly MethodInfo ClayBehaviorSetIndex = typeof(IClayBehavior).GetMethod("SetIndex");
+    private static readonly MethodInfo ClayBehaviorBinaryOperation = typeof(IClayBehavior).GetMethod("BinaryOperation");
+    private static readonly MethodInfo ClayBehaviorConvert = typeof(IClayBehavior).GetMethod("Convert");
+    private static readonly MethodInfo ClayBehaviorInvokeMemberMissing = typeof(IClayBehavior).GetMethod("InvokeMemberMissing");
+    private static readonly MethodInfo ClayBehaviorGetMemberMissing = typeof(IClayBehavior).GetMethod("GetMemberMissing");
+    private static readonly MethodInfo ClayBehaviorSetMemberMissing = typeof(IClayBehavior).GetMethod("SetMemberMissing");
+    private static readonly MethodInfo ClayBehaviorConvertMissing = typeof(IClayBehavior).GetMethod("ConvertMissing");
+
+    public ClayMetaObject(object value, Expression expression) : base(expression, BindingRestrictions.Empty, value)
+    {
+    }
+
+    public ClayMetaObject(object value, Expression expression, Func<Expression, Expression> getClayBehavior) : this(value, expression)
+    {
+        _getClayBehavior = getClayBehavior;
+    }
+
+    private Expression GetLimitedSelf()
+    {
+        if (Expression.Type == LimitType || Expression.Type.IsEquivalentTo(LimitType))
+        {
+            return Expression;
+        }
+
+        return Expression.Convert(Expression, LimitType);
+    }
+
+    private readonly Func<Expression, Expression> _getClayBehavior = expr => Expression.Property(Expression.Convert(expr, typeof(IClayBehaviorProvider)), "Behavior");
+
+    protected virtual Expression GetClayBehavior()
+    {
+        return _getClayBehavior(Expression);
+    }
+
+    public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
+    {
+        var binderDefault = binder.FallbackGetMember(this);
+        var missingLambda = Expression.Lambda(Expression.Call(GetClayBehavior(), ClayBehaviorGetMemberMissing, Expression.Lambda(binderDefault.Expression), GetLimitedSelf(), Expression.Constant(binder.Name, typeof(string))));
+        var call = Expression.Call(GetClayBehavior(), ClayBehaviorGetMember, missingLambda, GetLimitedSelf(), Expression.Constant(binder.Name, typeof(string)));
+        var dynamicSuggestion = new DynamicMetaObject(call, BindingRestrictions.GetTypeRestriction(Expression, LimitType).Merge(binderDefault.Restrictions));
+        return binder.FallbackGetMember(this, dynamicSuggestion);
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
+    {
+        var binderDefault = binder.FallbackSetMember(this, value);
+        var missingLambda = Expression.Lambda(Expression.Call(GetClayBehavior(), ClayBehaviorSetMemberMissing, Expression.Lambda(binderDefault.Expression), GetLimitedSelf(), Expression.Constant(binder.Name, typeof(string)), Expression.Convert(value.Expression, typeof(object))));
+        var call = Expression.Call(GetClayBehavior(), ClayBehaviorSetMember, missingLambda, GetLimitedSelf(), Expression.Constant(binder.Name, typeof(string)), Expression.Convert(value.Expression, typeof(object)));
+        var dynamicSuggestion = new DynamicMetaObject(call, BindingRestrictions.GetTypeRestriction(Expression, LimitType).Merge(binderDefault.Restrictions));
+        return binder.FallbackSetMember(this, value, dynamicSuggestion);
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
+    {
+        var argValues = Expression.NewArrayInit(typeof(object), args.Select(x => Expression.Convert(x.Expression, typeof(Object))));
+        var argNames = Expression.Constant(binder.CallInfo.ArgumentNames, typeof(IEnumerable<string>));
+        var argNamedEnumerable = Expression.Call(typeof(Arguments).GetMethod("From"), argValues, argNames);
+        var binderDefault = binder.FallbackInvokeMember(this, args);
+        var missingLambda = Expression.Lambda(Expression.Call(GetClayBehavior(), ClayBehaviorInvokeMemberMissing, Expression.Lambda(binderDefault.Expression), GetLimitedSelf(), Expression.Constant(binder.Name, typeof(string)), argNamedEnumerable));
+        var call = Expression.Call(GetClayBehavior(), ClayBehaviorInvokeMember, missingLambda, GetLimitedSelf(), Expression.Constant(binder.Name, typeof(string)), argNamedEnumerable);
+        var dynamicSuggestion = new DynamicMetaObject(call, BindingRestrictions.GetTypeRestriction(Expression, LimitType).Merge(binderDefault.Restrictions));
+        return binder.FallbackInvokeMember(this, args, dynamicSuggestion);
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindConvert(ConvertBinder binder)
+    {
+        var binderDefault = binder.FallbackConvert(this);
+        var missingLambda = Expression.Lambda(Expression.Call(GetClayBehavior(), ClayBehaviorConvertMissing, Expression.Lambda(Expression.Convert(binderDefault.Expression, typeof(object))), GetLimitedSelf(), Expression.Constant(binder.Type, typeof(Type)), Expression.Constant(binder.Explicit, typeof(bool))));
+        var call = Expression.Call(GetClayBehavior(), ClayBehaviorConvert, missingLambda, GetLimitedSelf(), Expression.Constant(binder.Type, typeof(Type)), Expression.Constant(binder.Explicit, typeof(bool)));
+        var convertedCall = Expression.Convert(call, binder.ReturnType);
+        return new DynamicMetaObject(convertedCall, BindingRestrictions.GetTypeRestriction(Expression, LimitType).Merge(binderDefault.Restrictions));
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder)
+    {
+        throw new NotImplementedException();
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes)
+    {
+        var a2 = Expression.NewArrayInit(typeof(string), indexes.Select(x => Expression.Convert(x.Expression, typeof(string))));
+        var binderFallback = binder.FallbackGetIndex(this, indexes);
+        var call = Expression.Call(GetClayBehavior(), ClayBehaviorGetIndex, Expression.Lambda(binderFallback.Expression), GetLimitedSelf(), a2);
+        return new DynamicMetaObject(call, BindingRestrictions.GetTypeRestriction(Expression, LimitType).Merge(binderFallback.Restrictions));
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value)
+    {
+        var a2 = Expression.NewArrayInit(typeof(string), indexes.Select(x => Expression.Convert(x.Expression, typeof(string))));
+        var binderFallback = binder.FallbackSetIndex(this, indexes, value);
+        var call = Expression.Call(GetClayBehavior(), ClayBehaviorSetIndex, Expression.Lambda(binderFallback.Expression), GetLimitedSelf(), a2, Expression.Convert(value.Expression, typeof(object)));
+        return new DynamicMetaObject(call, BindingRestrictions.GetTypeRestriction(Expression, LimitType).Merge(binderFallback.Restrictions));
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes)
+    {
+        throw new NotImplementedException();
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args)
+    {
+        var argValues = Expression.NewArrayInit(typeof(object), args.Select(x => Expression.Convert(x.Expression, typeof(Object))));
+        var argNames = Expression.Constant(binder.CallInfo.ArgumentNames, typeof(IEnumerable<string>));
+        var argNamedEnumerable = Expression.Call(typeof(Arguments).GetMethod("From"), argValues, argNames);
+        var binderFallback = binder.FallbackInvoke(this, args);
+        var call = Expression.Call(GetClayBehavior(), ClayBehaviorInvokeMember, Expression.Lambda(binderFallback.Expression), GetLimitedSelf(), Expression.Constant(null, typeof(string)), argNamedEnumerable);
+        return new DynamicMetaObject(call, BindingRestrictions.GetTypeRestriction(Expression, LimitType).Merge(binderFallback.Restrictions));
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args)
+    {
+        throw new NotImplementedException();
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder)
+    {
+        throw new NotImplementedException();
+    }
+
+    /// <inheritdoc />
+    public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg)
+    {
+        var binderFallback = binder.FallbackBinaryOperation(this, arg);
+        var call = Expression.Call(GetClayBehavior(), ClayBehaviorBinaryOperation, Expression.Lambda(binderFallback.Expression), GetLimitedSelf(), Expression.Constant(binder.Operation, typeof(ExpressionType)), Expression.Convert(arg.Expression, typeof(object)));
+        return new DynamicMetaObject(call, BindingRestrictions.GetTypeRestriction(Expression, LimitType));
+    }
+
+    /// <inheritdoc />
+    public override IEnumerable<string> GetDynamicMemberNames()
+    {
+        throw new NotImplementedException();
+    }
+}

+ 83 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/DefaultClayActivator.cs

@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Dynamic;
+using System.Linq.Expressions;
+using Castle.DynamicProxy;
+
+namespace Masuit.Tools.Dynamics;
+
+internal class DefaultClayActivator : IClayActivator
+{
+    private static readonly IProxyBuilder _builder = new DefaultProxyBuilder();
+
+    public dynamic CreateInstance(Type baseType, IEnumerable<IClayBehavior> behaviors, IEnumerable<object> arguments)
+    {
+        var isDynamicMetaObjectProvider = typeof(IDynamicMetaObjectProvider).IsAssignableFrom(baseType);
+        var isClayBehaviorProvider = typeof(IClayBehaviorProvider).IsAssignableFrom(baseType);
+        if (isDynamicMetaObjectProvider && isClayBehaviorProvider)
+        {
+            var constructorArguments = new object[]
+            {
+                behaviors
+            };
+            return Activator.CreateInstance(baseType, constructorArguments);
+        }
+
+        Func<object, object> contextualize = proxy => proxy;
+        var options = new ProxyGenerationOptions();
+        var constructorArgs = new List<object>();
+        if (!isClayBehaviorProvider)
+        {
+            var mixin = new MixinClayBehaviorProvider(behaviors);
+            options.AddMixinInstance(mixin);
+            constructorArgs.Add(mixin);
+        }
+
+        if (!isDynamicMetaObjectProvider)
+        {
+            var mixin = new MixinDynamicMetaObjectProvider();
+            options.AddMixinInstance(mixin);
+            constructorArgs.Add(mixin);
+            var prior = contextualize;
+            contextualize = proxy =>
+            {
+                mixin.Instance = proxy;
+                return prior(proxy);
+            };
+        }
+
+        var proxyType = _builder.CreateClassProxyType(baseType, Type.EmptyTypes, options);
+        constructorArgs.Add(new IInterceptor[]
+        {
+            new ClayInterceptor()
+        });
+        if (arguments != null)
+        {
+            constructorArgs.AddRange(arguments);
+        }
+
+        return contextualize(Activator.CreateInstance(proxyType, constructorArgs.ToArray()));
+    }
+
+    private class MixinClayBehaviorProvider : IClayBehaviorProvider
+    {
+        private readonly IClayBehavior _behavior;
+
+        public MixinClayBehaviorProvider(IEnumerable<IClayBehavior> behaviors)
+        {
+            _behavior = new ClayBehaviorCollection(behaviors);
+        }
+
+        IClayBehavior IClayBehaviorProvider.Behavior => _behavior;
+    }
+
+    private class MixinDynamicMetaObjectProvider : IDynamicMetaObjectProvider
+    {
+        public object Instance { get; set; }
+
+        DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression expression)
+        {
+            return new ClayMetaObject(Instance, expression);
+        }
+    }
+}

+ 22 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/DynamicFactory.cs

@@ -0,0 +1,22 @@
+using Masuit.Tools.Dynamics.Behaviors;
+
+namespace Masuit.Tools.Dynamics;
+
+public class DynamicFactory : Clay
+{
+    public DynamicFactory() : base(new ClayFactoryBehavior(), new ArrayFactoryBehavior())
+    {
+    }
+
+    public static dynamic NewObject()
+    {
+        dynamic factory = new DynamicFactory();
+        return factory.DynamicObject();
+    }
+
+    public static dynamic WithObject(object obj)
+    {
+        dynamic factory = new DynamicFactory();
+        return factory.DynamicObject(obj);
+    }
+}

+ 9 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/IClayActivator.cs

@@ -0,0 +1,9 @@
+using System;
+using System.Collections.Generic;
+
+namespace Masuit.Tools.Dynamics;
+
+internal interface IClayActivator
+{
+    dynamic CreateInstance(Type baseType, IEnumerable<IClayBehavior> behaviors, IEnumerable<object> arguments);
+}

+ 39 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/IClayBehavior.cs

@@ -0,0 +1,39 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+
+namespace Masuit.Tools.Dynamics;
+
+public interface IClayBehavior
+{
+    object GetMember(Func<object> proceed, object self, string name);
+
+    object SetMember(Func<object> proceed, object self, string name, object value);
+
+    object InvokeMember(Func<object> proceed, object self, string name, INamedEnumerable<object> args);
+
+    object GetIndex(Func<object> proceed, object self, IEnumerable<string> keys);
+
+    object SetIndex(Func<object> proceed, object self, IEnumerable<string> keys, object value);
+
+    object GetMembers(Func<object> proceed, object self, IDictionary<string, object> members);
+
+    object Convert(Func<object> proceed, object self, Type type, bool isExplicit);
+
+    object BinaryOperation(Func<object> proceed, object self, ExpressionType operation, object value);
+
+    object InvokeMemberMissing(Func<object> proceed, object self, string name, INamedEnumerable<object> args);
+
+    object GetMemberMissing(Func<object> proceed, object self, string name);
+
+    object SetMemberMissing(Func<object> proceed, object self, string name, object value);
+
+    object ConvertMissing(Func<object> proceed, object self, Type type, bool isExplicit);
+}
+
+public interface INamedEnumerable<T> : IEnumerable<T>
+{
+    IEnumerable<T> Positional { get; }
+
+    IDictionary<string, T> Named { get; }
+}

+ 6 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/IClayBehaviorProvider.cs

@@ -0,0 +1,6 @@
+namespace Masuit.Tools.Dynamics;
+
+internal interface IClayBehaviorProvider
+{
+    IClayBehavior Behavior { get; }
+}

+ 174 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/Implementation/Arguments.cs

@@ -0,0 +1,174 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Masuit.Tools.Dynamics.Implementation;
+
+internal static class Arguments
+{
+    public static INamedEnumerable<T> FromT<T>(IEnumerable<T> arguments, IEnumerable<string> names)
+    {
+        return new NamedEnumerable<T>(arguments, names);
+    }
+
+    public static INamedEnumerable<object> From(IEnumerable<object> arguments, IEnumerable<string> names)
+    {
+        return new NamedEnumerable<object>(arguments, names);
+    }
+
+    private class NamedEnumerable<T> : INamedEnumerable<T>
+    {
+        private readonly IEnumerable<T> _arguments;
+        private readonly IEnumerable<string> _names;
+
+        public NamedEnumerable(IEnumerable<T> arguments, IEnumerable<string> names)
+        {
+            if (arguments == null)
+            {
+                throw new ArgumentNullException(nameof(arguments));
+            }
+
+            if (names == null)
+            {
+                throw new ArgumentNullException(nameof(names));
+            }
+
+            var list = arguments.ToList();
+            var nameList = names.ToList();
+            if (list.Count < nameList.Count)
+            {
+                throw new ArgumentException("arguments.Count() < names.Count()");
+            }
+
+            _arguments = list;
+            _names = nameList;
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            return _arguments.GetEnumerator();
+        }
+
+        IEnumerator<T> IEnumerable<T>.GetEnumerator()
+        {
+            return _arguments.GetEnumerator();
+        }
+
+        IEnumerable<T> INamedEnumerable<T>.Positional => _arguments.Take(_arguments.Count() - _names.Count());
+
+        IDictionary<string, T> INamedEnumerable<T>.Named => new Named(_arguments, _names);
+
+        private class Named : IDictionary<string, T>
+        {
+            private readonly IEnumerable<T> _arguments;
+            private readonly IEnumerable<string> _names;
+
+            private ICollection<T> _argumentsCollection;
+            private ICollection<string> _namesCollection;
+
+            public Named(IEnumerable<T> arguments, IEnumerable<string> names)
+            {
+                _arguments = arguments.Skip(arguments.Count() - names.Count());
+                _names = names;
+            }
+
+            private IEnumerable<KeyValuePair<string, T>> MakeEnumerable()
+            {
+                return _arguments.Zip(_names, (arg, name) => new KeyValuePair<string, T>(name, arg));
+            }
+
+            IEnumerator<KeyValuePair<string, T>> IEnumerable<KeyValuePair<string, T>>.GetEnumerator()
+            {
+                return MakeEnumerable().GetEnumerator();
+            }
+
+            IEnumerator IEnumerable.GetEnumerator()
+            {
+                return MakeEnumerable().GetEnumerator();
+            }
+
+            void ICollection<KeyValuePair<string, T>>.Add(KeyValuePair<string, T> item)
+            {
+                throw new NotImplementedException();
+            }
+
+            void ICollection<KeyValuePair<string, T>>.Clear()
+            {
+                throw new NotImplementedException();
+            }
+
+            bool ICollection<KeyValuePair<string, T>>.Contains(KeyValuePair<string, T> item)
+            {
+                return MakeEnumerable().Contains(item);
+            }
+
+            void ICollection<KeyValuePair<string, T>>.CopyTo(KeyValuePair<string, T>[] array, int arrayIndex)
+            {
+                throw new NotImplementedException();
+            }
+
+            bool ICollection<KeyValuePair<string, T>>.Remove(KeyValuePair<string, T> item)
+            {
+                throw new NotImplementedException();
+            }
+
+            int ICollection<KeyValuePair<string, T>>.Count => _names.Count();
+
+            bool ICollection<KeyValuePair<string, T>>.IsReadOnly => true;
+
+            bool IDictionary<string, T>.ContainsKey(string key)
+            {
+                return _names.Contains(key);
+            }
+
+            void IDictionary<string, T>.Add(string key, T value)
+            {
+                throw new NotImplementedException();
+            }
+
+            bool IDictionary<string, T>.Remove(string key)
+            {
+                throw new NotImplementedException();
+            }
+
+            bool IDictionary<string, T>.TryGetValue(string key, out T value)
+            {
+                var pair = MakeEnumerable().FirstOrDefault(kv => kv.Key == key);
+                value = pair.Value;
+                return pair.Key != null;
+            }
+
+            T IDictionary<string, T>.this[string key]
+            {
+                get
+                {
+                    return MakeEnumerable().Where(kv => kv.Key == key).Select(kv => kv.Value).FirstOrDefault();
+                }
+
+                set => throw new NotImplementedException();
+            }
+
+            ICollection<string> IDictionary<string, T>.Keys
+            {
+                get
+                {
+                    return _namesCollection ??= _names as ICollection<string> ?? _names.ToArray();
+                }
+            }
+
+            ICollection<T> IDictionary<string, T>.Values
+            {
+                get
+                {
+                    return _argumentsCollection ??= _arguments as ICollection<T> ?? _arguments.ToArray();
+                }
+            }
+        }
+    }
+
+    public static INamedEnumerable<object> Empty()
+    {
+        return From(Enumerable.Empty<object>(), Enumerable.Empty<string>());
+    }
+}

+ 8 - 0
Masuit.Tools.Abstractions/Extensions/Dynamics/NullInstance.cs

@@ -0,0 +1,8 @@
+using Masuit.Tools.Dynamics.Behaviors;
+
+namespace Masuit.Tools.Dynamics;
+
+internal static class NullInstance
+{
+    public static readonly object Instance = new Clay(new NullBehavior(), new InterfaceProxyBehavior());
+}

+ 2 - 0
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -43,6 +43,7 @@
     </ItemGroup>
 
     <ItemGroup>
+        <PackageReference Include="Castle.Core" Version="5.1.1" />
         <PackageReference Include="DnsClient" Version="1.7.0" />
         <PackageReference Include="HtmlSanitizer" Version="8.0.645" />
         <PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
@@ -97,6 +98,7 @@
     </ItemGroup>
 
     <ItemGroup>
+        <Folder Include="Extensions\Dynamics\" />
         <Folder Include="Files\FileDetector\" />
     </ItemGroup>
 

+ 13 - 4
Masuit.Tools.Abstractions/Reflection/ReflectionUtil.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Diagnostics.CodeAnalysis;
@@ -111,6 +112,8 @@ namespace Masuit.Tools.Reflection
             return before.ToJsonString();
         }
 
+        private static readonly ConcurrentDictionary<string, Delegate> DelegateCache = new();
+
         /// <summary>
         /// 获取属性
         /// </summary>
@@ -120,9 +123,16 @@ namespace Masuit.Tools.Reflection
         /// <returns>T类型</returns>
         public static T GetProperty<T>(this object obj, string name)
         {
-            var parameter = Expression.Parameter(obj.GetType(), "e");
+            var type = obj.GetType();
+            if (DelegateCache.TryGetValue(type.Name + "." + name, out var func))
+            {
+                return (T)func.DynamicInvoke(obj);
+            }
+            var parameter = Expression.Parameter(type, "e");
             var property = Expression.PropertyOrField(parameter, name);
-            return (T)Expression.Lambda(property, parameter).Compile().DynamicInvoke(obj);
+            func = Expression.Lambda(property, parameter).Compile();
+            DelegateCache.TryAdd(type.Name + "." + name, func);
+            return (T)func.DynamicInvoke(obj);
         }
 
         /// <summary>
@@ -132,8 +142,7 @@ namespace Masuit.Tools.Reflection
         /// <returns>属性信息</returns>
         public static PropertyInfo[] GetProperties(this object obj)
         {
-            PropertyInfo[] propertyInfos = obj.GetType().GetProperties(bf);
-            return propertyInfos;
+            return obj.GetType().GetProperties(bf);
         }
 
         #endregion 属性字段设置

+ 12 - 0
Masuit.Tools.Net45/Masuit.Tools.Net45.csproj

@@ -70,6 +70,12 @@
     <Compile Include="..\Masuit.Tools.Abstractions\Extensions\*\*.*">
       <Link>Extensions\%(RecursiveDir)%(FileName)%(Extension)</Link>
     </Compile>
+    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\Dynamics\*.*">
+      <Link>Extensions\Dynamics\%(RecursiveDir)%(FileName)%(Extension)</Link>
+    </Compile>
+    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\Dynamics\*\*.*">
+      <Link>Extensions\Dynamics\%(RecursiveDir)%(FileName)%(Extension)</Link>
+    </Compile>
     <Compile Include="..\Masuit.Tools.Abstractions\Files\FileExt.cs">
       <Link>Files\FileExt.cs</Link>
     </Compile>
@@ -282,12 +288,18 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>
+    <PackageReference Include="Castle.Core">
+      <Version>4.4.1</Version>
+    </PackageReference>
     <PackageReference Include="DnsClient">
       <Version>1.7.0</Version>
     </PackageReference>
     <PackageReference Include="Microsoft.AspNet.Mvc">
       <Version>5.2.9</Version>
     </PackageReference>
+    <PackageReference Include="Microsoft.CSharp">
+      <Version>4.7.0</Version>
+    </PackageReference>
     <PackageReference Include="Newtonsoft.Json">
       <Version>13.0.3</Version>
     </PackageReference>

+ 6 - 0
Masuit.Tools/Masuit.Tools.csproj

@@ -55,6 +55,12 @@
     <Compile Include="..\Masuit.Tools.Abstractions\Extensions\*\*.*">
       <Link>Extensions\%(RecursiveDir)%(FileName)%(Extension)</Link>
     </Compile>
+    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\Dynamics\*.*">
+      <Link>Extensions\Dynamics\%(RecursiveDir)%(FileName)%(Extension)</Link>
+    </Compile>
+    <Compile Include="..\Masuit.Tools.Abstractions\Extensions\Dynamics\*\*.*">
+      <Link>Extensions\Dynamics\%(RecursiveDir)%(FileName)%(Extension)</Link>
+    </Compile>
     <Compile Include="..\Masuit.Tools.Abstractions\Files\FileDetector\*.*">
       <Link>Files\FileDetector\%(RecursiveDir)%(FileName)%(Extension)</Link>
     </Compile>

+ 47 - 0
Test/Masuit.Tools.Abstractions.Test/Extensions/DynamicObjectTest.cs

@@ -0,0 +1,47 @@
+using System.Collections.Generic;
+using Masuit.Tools.Dynamics;
+using Xunit;
+
+namespace Masuit.Tools.Abstractions.Test.Extensions;
+
+public class DynamicObjectTest
+{
+    [Fact]
+    public void Can_Dynamic()
+    {
+        var obj = DynamicFactory.NewObject();
+        obj.Name = "Masuit";
+        obj.Age = 18;
+        obj.MyClass = DynamicFactory.WithObject(new
+        {
+            X = 10,
+            Y = 20,
+            Z = new List<int> { 1, 2, 3, 4, 5 }
+        });
+        Assert.Equal(obj.Name, obj["Name"]);
+        Assert.Equal(obj["MyClass"]["X"], obj.MyClass.X);
+        Assert.Equal(obj.MyClass.Z[2], obj["MyClass"]["Z"][2]);
+    }
+
+    [Fact]
+    public void Can_ToDynamic()
+    {
+        var obj = new
+        {
+            Name = "Masuit"
+        }.ToDynamic();
+        obj.Age = 18;
+        obj.MyClass = new
+        {
+            X = 10,
+            Y = 20,
+            Z = new List<int> { 1, 2, 3, 4, 5 }
+        }.ToDynamic();
+        obj.Prop = "test";
+        _ = obj - "Prop";
+
+        Assert.Equal(obj.Name, obj["Name"]);
+        Assert.Equal(obj["MyClass"]["X"], obj.MyClass.X);
+        Assert.IsType<Clay>(obj.Prop);
+    }
+}