在字典中使用Expression<Func<object>>作为键

3

如何将Expression>类型用作字典中的键?

我刚开始使用Expression实例,不确定我想做的是否可行。

似乎两个相同的表达式不相等,因为当我尝试使用表达式作为键来向字典中添加条目时,它返回false,除非我使用完全相同的表达式实例。

TypeToTest test = new TypeToTest();
Expression<Func<object>> expression = ()=>test.PropertyA;
IDictionary<Expression<Func<object>>,bool> dictionary = new Dictionary<Expression<Func<object>>, bool> ();
dictionary[expression] = true;
Assert.That (dictionary.ContainsKey(expression), Is.True);
Assert.That (dictionary.ContainsKey(()=>test.PropertyA), Is.True);

上面的最后一行失败了,而我希望它成功。
意图是能够定义适用于对象属性或方法的一组规则,以便我可以确定例如属性是否可编辑,或字典中具有特定键的值是否可以被删除。我不想在对象上设置一个标志来确定它是否可编辑(因为可编辑性可能对不同的属性有所不同),也不想设置另一个标志来确定它是否可删除,而是由另一个类负责维护与对象相关联的规则,以便随着对象在未来的扩展,可以添加更多规则来描述对象的组成部分的可编辑性/可访问性/可删除性等。如果这样说有意义的话。
这里有一个类似的帖子(链接),但这似乎是基于属性名进行键控,而我希望也可以基于方法和参数进行键控,以便我可以支持根据键确定字典中保存的对象的可编辑性。
像这样的东西是否可能存在,还是只是空想?
2个回答

4

当比较不同引用时,这是一种预期的行为,其中该类型没有覆盖Equals或实现IEquatable<T>。您可以编写自定义比较器(可能只是比较ToString()),并将其传递到字典中 - 但在我看来,Expression不是一个好的键选择。

以下不一定是通过ToString()的健壮用法;请谨慎使用:

class Program {
    static void Main() {
        TypeToTest test = new TypeToTest();
        Expression<Func<object>> expression = () => test.PropertyA;
        IDictionary<Expression<Func<object>>, bool> dictionary =
            new Dictionary<Expression<Func<object>>, bool>(
               new ToStringComparer<Expression<Func<object>>>());
        dictionary[expression] = true;

        bool x = dictionary.ContainsKey(expression), // true
            y = dictionary.ContainsKey(() => test.PropertyA); // true
    }
}
class ToStringComparer<T> : IEqualityComparer<T> where T : class {
    public bool Equals(T x, T y) {
        if ((x == null && y == null) || ReferenceEquals(x,y)) return true;
        if (x == null || y == null) return false;
        return x.ToString() == y.ToString();
    }
    public int GetHashCode(T obj) {
        return obj == null ? 0 : obj.ToString().GetHashCode();
    }
}

你为什么认为这不是一个好的键选择?它似乎允许每个属性/方法以一种可以轻松扩展类型(例如另一个接口)的方式单独控制其可访问性。 - Sam Holder
@Sam - 因为没有明确定义的相等检查。例如:x => x.A 这个表达式和 y => y.A 相同吗?如果这个表达式捕获了一个被捕获变量包装器的对象实例,会怎么样? - Marc Gravell
这是一个不错的解决方案。 - hackp0int

2
Expression 类型的子类没有覆盖 EqualsGetHashCode 方法,这使得它们难以作为字典键使用。您的字典正在使用引用相等性,并且两个出现的 ()=>test.PropertyA 会产生两个不同的表达式树对象。
您可以编写自己的 IEqualityComparer<Expression> 实现,并将其传递到字典构造函数中。在您的比较器中,您需要编写处理每个 Expression 类的 EqualsGetHashCode 方法,并比较它们的属性。

Expression 覆盖了 GetHashCode,因此 IEqualityComparer<Expression> 实现可能能够委托实现(只需检查相等的实例是否给出相同的哈希码)。 - Richard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接