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