using System; using System.Linq.Expressions; using System.Reflection; namespace Unity.VisualScripting { public class InstancePropertyAccessor : IOptimizedAccessor { public InstancePropertyAccessor(PropertyInfo propertyInfo) { if (OptimizedReflection.safeMode) { Ensure.That(nameof(propertyInfo)).IsNotNull(propertyInfo); if (propertyInfo.DeclaringType != typeof(TTarget)) { throw new ArgumentException("The declaring type of the property info doesn't match the generic type.", nameof(propertyInfo)); } if (propertyInfo.PropertyType != typeof(TProperty)) { throw new ArgumentException("The property type of the property info doesn't match the generic type.", nameof(propertyInfo)); } if (propertyInfo.IsStatic()) { throw new ArgumentException("The property is static.", nameof(propertyInfo)); } } this.propertyInfo = propertyInfo; } private readonly PropertyInfo propertyInfo; private Func getter; private Action setter; public void Compile() { var getterInfo = propertyInfo.GetGetMethod(true); var setterInfo = propertyInfo.GetSetMethod(true); if (OptimizedReflection.useJit) { var targetExpression = Expression.Parameter(typeof(TTarget), "target"); if (getterInfo != null) { var propertyExpression = Expression.Property(targetExpression, propertyInfo); getter = Expression.Lambda>(propertyExpression, targetExpression).Compile(); } if (setterInfo != null) { setter = (Action)setterInfo.CreateDelegate(typeof(Action)); } } else { if (getterInfo != null) { getter = (Func)getterInfo.CreateDelegate(typeof(Func)); } if (setterInfo != null) { setter = (Action)setterInfo.CreateDelegate(typeof(Action)); } } } public object GetValue(object target) { if (OptimizedReflection.safeMode) { OptimizedReflection.VerifyInstanceTarget(target); if (getter == null) { throw new TargetException($"The property '{typeof(TTarget)}.{propertyInfo.Name}' has no get accessor."); } try { return GetValueUnsafe(target); } catch (TargetInvocationException) { throw; } catch (Exception ex) { throw new TargetInvocationException(ex); } } else { return GetValueUnsafe(target); } } private object GetValueUnsafe(object target) { return getter.Invoke((TTarget)target); } public void SetValue(object target, object value) { if (OptimizedReflection.safeMode) { OptimizedReflection.VerifyInstanceTarget(target); if (setter == null) { throw new TargetException($"The property '{typeof(TTarget)}.{propertyInfo.Name}' has no set accessor."); } if (!typeof(TProperty).IsAssignableFrom(value)) { throw new ArgumentException($"The provided value for '{typeof(TTarget)}.{propertyInfo.Name}' does not match the property type.\nProvided: {value?.GetType()?.ToString() ?? "null"}\nExpected: {typeof(TProperty)}"); } try { SetValueUnsafe(target, value); } catch (TargetInvocationException) { throw; } catch (Exception ex) { throw new TargetInvocationException(ex); } } else { SetValueUnsafe(target, value); } } private void SetValueUnsafe(object target, object value) { setter.Invoke((TTarget)target, (TProperty)value); } } }