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;
}
}
}