Reputation: 970
I would like to create a cache for my objects and expressions. With the following code he runs true all the elements, after this he would be able to use the expression/object from the dictionary. Instead of doing this he adds the member expression again to the dictionary with the same object.
Is it possible to create a dictionary with a member expression, what am I doing wrong here?
private static readonly IDictionary<MemberExpression, object> Cache = new Dictionary<MemberExpression, object>();
private static Func<TClass, TProperty> GetCachedMemberFunction<TClass, TProperty>(
Expression<Func<TClass, TProperty>> member)
{
Func<TClass, TProperty> func;
var memberExpression = member.Body as MemberExpression;
if (Cache.ContainsKey(memberExpression))
{
func = (Func<TClass, TProperty>)Cache[memberExpression];
}
else
{
func = member.Compile();
Cache[memberExpression] = func;
}
return func;
}
Note: As you can see from the code I would like to create a cache so I only need to the compile function once.
Upvotes: 1
Views: 517
Reputation: 27105
First off, I would like to say that caching expressions is something that should be done with utmost care and is extremely complex to implement as the example from the comments (this question) already shows. There is a reason why Equals
has not been overridden for expressions, because they are so complex to compare.
Some of the main issues with expressions when it comes to comparing them:
E.g.
y => y.MyProperty.AnotherProperty
x => x.MyProperty.AnotherProperty
Are the above two expressions equal? It depends on the use case (if you are analyzing code you might want to take the variable name into account). Also, consider the following example:
class BaseClass { int MyProperty { get; } }
class DerivedClass : BaseClass { }
Now lets say we have two expressions that are pretty much equal:
BaseClass x => x.MyProperty;
DerivedClass x => x.MyProperty;
Should these be considered equal? Probably not, but maybe in your case.
end of rant
Since you are dealing with MemberExpression
s you are probably doing something with property getters and setters or the like. In this case, it might be sufficient to use the Type
in combination with the MemberInfo
as a key for your Cache
object. Note that this only applies when ALL expressions are in the form of x => x.Property
without any nested access, casts or whatever.
In that case you could come up with something like this:
private struct MemberAccessKey
{
public readonly Type Type;
public readonly MemberInfo Member;
public MemberAccessKey(Type t, MemberInfo m)
{
Type = t; Member = m;
}
public override bool Equals(object obj)
{
if (!(obj is MemberAccessKey)) return false;
var other = (MemberAccessKey)obj;
return other.Type == Type && other.Member == Member;
}
public override int GetHashCode()
{
return Type.GetHashCode() ^ Member.GetHashCode();
}
}
And then instantiate it based on a MemberExpression:
TypeKey key = new TypeKey(typeof(TClass), memberExpression.Member);
Conclusion
x => x.MyProperty
then you might be able to use this as a key. Note that any other form will either crash or resolve to a duplicate key.Upvotes: 2