using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using System.Threading.Tasks; namespace EasyDevCore.Common.Caches { /// /// /// public static class ExpressionCache { /// /// /// /// The dictionary. /// The o. public delegate void AppendToDictionary(IDictionary dictionary, object o); /// /// The s type cache /// static readonly ConcurrentDictionary s_typeCache = new ConcurrentDictionary(); /// /// The dictionary indexer property /// private static readonly PropertyInfo _dictionaryIndexerProperty = GetDictionaryIndexer(); /// /// Gets the or create append to dictionary method. /// /// The type. /// public static AppendToDictionary GetOrCreateAppendToDictionaryMethod(Type type) => s_typeCache.GetOrAdd(type, t => CreateAppendToDictionaryMethod(t)); /// /// Creates the append to dictionary method. /// /// The type. /// private static AppendToDictionary CreateAppendToDictionaryMethod(Type type) { var dictionaryParameter = Expression.Parameter(typeof(IDictionary), "dictionary"); var objectParameter = Expression.Parameter(typeof(object), "o"); var castedParameter = Expression.Convert(objectParameter, type); // cast o to the actual type // Create setter for each properties // dictionary["PropertyName"] = o.PropertyName; var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); var setters = from prop in properties where prop.CanRead let indexerExpression = Expression.Property(dictionaryParameter, _dictionaryIndexerProperty, Expression.Constant(prop.Name)) let getExpression = Expression.Property(castedParameter, prop.GetMethod) select Expression.Assign(indexerExpression, getExpression); var body = new List(properties.Length + 1); body.Add(castedParameter); body.AddRange(setters); var lambdaExpression = Expression.Lambda(Expression.Block(body), dictionaryParameter, objectParameter); return lambdaExpression.Compile(); } // Get the PropertyInfo for IDictionary.this[string key] /// /// Gets the dictionary indexer. /// /// private static PropertyInfo GetDictionaryIndexer() { var indexers = from prop in typeof(IDictionary).GetProperties(BindingFlags.Instance | BindingFlags.Public) let indexParameters = prop.GetIndexParameters() where indexParameters.Length == 1 && typeof(string).IsAssignableFrom(indexParameters[0].ParameterType) select prop; return indexers.Single(); } } }