PropertyInfo GetValue and Expression Cache GetValue I have implemented PropertyInfo GetValue and Expression Cache GetValue by Logic from Reflection vs. compiled expressions vs. delegates - Performance comparison,but I'm sure that there are better ways to do every thing that I did. I would appreciate any feedback on this code, which does work according to spec:

MyLogic


  
  Asynchronous nonblocking, which does not block thread when adding a Cache
  Dictionary Key use propertyInfo GetHash()
  if dictionary cache containskey then use compiler expression function else use Reflection GetValue
  


My Class

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;

public class TestService
{
    private static Dictionary&lt;int, object&gt; ExpressionCache = new Dictionary&lt;int, object&gt;();

    public IEnumerable&lt;string&gt; Execute&lt;T&gt;(IEnumerable&lt;T&gt; enums)
    {
        var expressionCache = (enums);
        var props = typeof(T).GetProperties();
        foreach (var e in enums)
        {
            foreach (var p in props)
            {
                var func = GetOrAddExpressionCache&lt;T&gt;(p);
                var value = string.Empty;
                if (func == null)
                {
                    Debug.WriteLine("Use Reflection");
                    value = p.GetValue(e).ToString();
                }
                else
                {
                    Debug.WriteLine("Use Expression");
                    value = func(e);
                }
                yield return value;
            }
        }
    }

    private Func&lt;T, string&gt; GetOrAddExpressionCache&lt;T&gt;(PropertyInfo prop)
    {
        var key = prop.GetHashCode();
        if (ExpressionCache.ContainsKey(key))
        {
            var func = ExpressionCache[key] as Func&lt;T, string&gt;;
            return func;
        }else{
            Task.Run(()=&gt;AddExpressionCacheAsync&lt;T&gt;(prop));
            return null;
        }
    }

    //Asynchronous nonblocking, which does not block a thread when adding a Cache
    private async Task AddExpressionCacheAsync&lt;T&gt;(PropertyInfo propertyInfo)
    {
        var key = propertyInfo.GetHashCode();
        if (!ExpressionCache.ContainsKey(key))
        {
            var func = GetValueGetter&lt;T&gt;(propertyInfo);
            ExpressionCache.Add(key, func);
        }
        await Task.Yield();
    }

    private static Func&lt;T, string&gt; GetValueGetter&lt;T&gt;(PropertyInfo propertyInfo)
    {
        var instance = Expression.Parameter(propertyInfo.DeclaringType);
        var property = Expression.Property(instance, propertyInfo);
        var toString = Expression.Call(property, "ToString", Type.EmptyTypes);
        return Expression.Lambda&lt;Func&lt;T, string&gt;&gt;(toString, instance).Compile();
    }
}


Demo

class Program{
    public static void Main(string[] args){
        var data = new[] { new { Name = "Henry", Age = 25 } };
        for (int i = 0; i &lt; 2; i++)
        {
            var service = new TestService();
            var result = service.Execute(data).ToArray();
        }
    }
}

/* Console :
        Use Reflection
        Use Reflection
        Use Expression
        Use Expression
*/
Problems


Both the reflection and expression-based approach fail to take null values into account (NullReferenceException).
They also fail to take types with indexers into account (TargetParameterCountException).
Hash-codes are not unique identifiers. Different properties (and objects in general) can have the same hash code - that just means that they might be equal. Use prop.MetadataToken in combination with the module they're declared in (prop.Module.ModuleVersionId).
Compiling expressions asynchronously makes this a lot more complex than it needs to be, and that complexity is not properly taken care of:


There's no guarantee that compilation will be finished when the next object is processed. This can result in multiple compilations for the same property. That's a waste of work.
The results of these compilations are added to a Dictionary, which is not thread-safe. In the best case, adding a key that already exists will throw an exception. In the worst case, the dictionary's internal state will become corrupted.
Those exceptions are not being caught. Before .NET Framework 4.5, that would've caused a crash, and in 4.5 and higher it may still do so, depending on certain settings.



Other improvements


Why does Execute take a sequence of items, instead of a single item? Flattening results is easy (data.SelectMany(service.Execute)), 'unflattening' is not - the caller would have to figure out the number of properties, and you'd have to write a method to split up a single sequence into sub-sequences.
Instead of doing ExpressionCache.ContainsKey(key), followed by ExpressionCache[key], use TryGetValue. This lets you check the presence of a key and get its associated value with a single lookup.
There's no point in making AddExpressionCacheAsync async. It's not doing any asynchronous work (that await Task.Yield() is useless). You're already calling this method from within a Task.Run call, so it'll be executed asynchronously anyway.
In GetValueGetter, use nameof(object.ToString) instead of "ToString".


Readability issues


Some names are not very descriptive: Execute -> GetPropertyValues, enums -> items, func -> compiledExpression, GetValueGetter -> CompileGetValueExpression.
There are several abbreviations that do not improve readability: e -> item, props -> properties, p -> property.