using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; using Swan.Reflection; namespace Swan { /// /// Provides functionality to access objects /// associated with types. Getters and setters are stored as delegates compiled /// from constructed lambda expressions for fast access. /// public static class PropertyProxyExtensions { private static readonly object SyncLock = new object(); private static readonly Dictionary> ProxyCache = new Dictionary>(32); /// /// Gets the property proxies associated with a given type. /// /// The type to retrieve property proxies from. /// A dictionary with property names as keys and objects as values. public static Dictionary PropertyProxies(this Type t) { if (t == null) throw new ArgumentNullException(nameof(t)); lock (SyncLock) { if (ProxyCache.ContainsKey(t)) return ProxyCache[t]; var properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Public); var result = new Dictionary(properties.Length, StringComparer.InvariantCultureIgnoreCase); foreach (var propertyInfo in properties) result[propertyInfo.Name] = new PropertyInfoProxy(t, propertyInfo); ProxyCache[t] = result; return result; } } /// /// Gets the property proxies associated with the provided instance type. /// /// The instance type. /// The instance. /// A dictionary with property names as keys and objects as values. public static Dictionary PropertyProxies(this T obj) => (obj?.GetType() ?? typeof(T)).PropertyProxies(); /// /// Gets the property proxy given the property name. /// /// The associated type. /// Name of the property. /// The associated public static IPropertyProxy PropertyProxy(this Type t, string propertyName) { var proxies = t.PropertyProxies(); return proxies.ContainsKey(propertyName) ? proxies[propertyName] : null; } /// /// Gets the property proxy given the property name. /// /// The type of instance to extract proxies from. /// The instance to extract proxies from. /// Name of the property. /// The associated public static IPropertyProxy PropertyProxy(this T obj, string propertyName) { if (propertyName == null) throw new ArgumentNullException(nameof(propertyName)); var proxies = (obj?.GetType() ?? typeof(T)).PropertyProxies(); return proxies?.ContainsKey(propertyName) == true ? proxies[propertyName] : null; } /// /// Gets the property proxy given the property name as an expression. /// /// The instance type. /// The property value type. /// The object. /// The property expression. /// The associated public static IPropertyProxy PropertyProxy(this T obj, Expression> propertyExpression) { if (propertyExpression == null) throw new ArgumentNullException(nameof(propertyExpression)); var proxies = (obj?.GetType() ?? typeof(T)).PropertyProxies(); var propertyName = propertyExpression.PropertyName(); return proxies?.ContainsKey(propertyName) == true ? proxies[propertyName] : null; } /// /// Reads the property value. /// /// The type to get property proxies from. /// The type of the property. /// The instance. /// The property expression. /// /// The value obtained from the associated /// /// obj. public static V ReadProperty(this T obj, Expression> propertyExpression) { if (obj == null) throw new ArgumentNullException(nameof(obj)); var proxy = obj.PropertyProxy(propertyExpression); return (V)(proxy?.GetValue(obj)); } /// /// Reads the property value. /// /// The type to get property proxies from. /// The instance. /// Name of the property. /// /// The value obtained from the associated /// /// obj. public static object? ReadProperty(this T obj, string propertyName) { if (obj == null) throw new ArgumentNullException(nameof(obj)); var proxy = obj.PropertyProxy(propertyName); return proxy?.GetValue(obj); } /// /// Writes the property value. /// /// The type to get property proxies from. /// The type of the property. /// The instance. /// The property expression. /// The value. public static void WriteProperty(this T obj, Expression> propertyExpression, TV value) { var proxy = obj.PropertyProxy(propertyExpression); proxy?.SetValue(obj, value); } /// /// Writes the property value using the property proxy. /// /// The type to get property proxies from. /// The instance. /// Name of the property. /// The value. public static void WriteProperty(this T obj, string propertyName, object? value) { var proxy = obj.PropertyProxy(propertyName); proxy?.SetValue(obj, value); } private static string PropertyName(this Expression> propertyExpression) { var memberExpression = !(propertyExpression.Body is MemberExpression) ? (propertyExpression.Body as UnaryExpression).Operand as MemberExpression : propertyExpression.Body as MemberExpression; return memberExpression.Member.Name; } } }