C#表达式比较

时间:2015-02-17 22:36:12

标签: c# lambda

假设我在集合上有以下表达式:

var people = new List<Person>
{
     new Person {FullName = "Some Dude", Age = 45},
     new Person {FullName = "Another Dude", Age = 28},
     new Person {FullName = "Some Other Dude", Age = 36}
 };

var filtered = people.Where(person => person.Age > 28 && person.FullName.StartsWith("So"));
var narrowlyFiltered = people.Where(person => person.Age > 36 && person.FullName.StartsWith("Some"));

有没有办法比较这两个表达式并推断第二个表达式是运行时第一个表达式的子集?没有枚举或其他任何东西。我只是有表达式,我试图找出这些表达式是否相交或相互包含。

5 个答案:

答案 0 :(得分:5)

您必须将每个表达式分解为所有可能的继承类型(MethodCallExpression,ConditionalExpression等),然后并行处理每个分解并检查每个可能的参数......代码将会有点长...你可以从ExpressionEqualityComparer

激励自己

答案 1 :(得分:2)

如果您可以枚举您的馆藏,您可以先将这些元素放在HashSet<T>中,然后在其上运行HashSet<T>.IsSubSet

HashSet<T> hs = new HashSet<T>(filtered);
HashSet<T> hs2 = new HashSet<T>(narrowlyFiltered);
hs.IsSubSetOf(hs2); //<- booleans saying true or false

否则,这个问题一般是undecidable problem。虽然有一些启发式方法可以用于许多案例。例如,您可以尝试使用旨在在编译时推断出此代码的代码契约。

证明:

  

正式变体是:给定两个图灵机(方法,代理,指针),第一语言中包含的每个字符串是否都包含在第二个中?   的不可判定
  证明:如果它是可判定的,则EQ TM 将是可判定的:只需首先验证第一个图灵机是否是第二个的子集,反之亦然。如果两者都是子集,我们知道它们接受相同的字符串集。

换句话说,如果你能做到这一点,你也可以推断出两个函数是否产生相同的结果,which cannot be implemented

答案 2 :(得分:2)

这一切都取决于你如何衡量相等,比较表达等等更重要。 例如,如果你有完全不同的过滤器,那么在实际执行之前你可能不知道查询差异。

要完全控制比较,请创建一个过滤器类,其中包含一些可用于过滤的属性,然后构建表达式并使用此类进行比较,而不是使用访问者。 您可以准备用于比较整数,整数对(范围)等的通用函数。

我没有检查下面的代码,但它应该是一个好的开始。

public class PersonFilter:  IComparable<PersonFilter>
{
    public int? MinAge { get; set; }

    public int? MaxAge { get; set; }

    public string NamePrefix { get; set; }

    public Expression<Predicate<Person>> Filter
    {
        return people => people.Where(person => (!MinAge.HasValue || person.Age > MinAge.Value) && 
            (!MaxAge.HasValue || person.Age < MaxAge.Value) && 
            (string.IsNullOrEmpty(NamePrefix) || person.FullName.StartsWith(NamePrefix))
    }

    // -1 if this filter is filtering more than the other
    public int CompareTo(PersonFilter other)
    {
        var balance = 0; // equal
        if(MinAge.HasValue != other.MinAge.HasValue)
        {
            balance += MinAge.HasValue ? -1 : 1;
        }
        else if(MinAge.HasValue)
        {
            balance += MinAge.Value.CompareTo(other.MinAge.Value) ?
        }
        if(string.IsNullOrEmpty(NamePrefix) != string.IsNullOrEmpty(other.NamePrefix))
        {
            balance += string.IsNullOrEmpty(NamePrefix) ? -1 : 1;
        }
        else if(!string.IsNullOrEmpty(NamePrefix))
        {
            if(NamePrefix.StartsWith(other.NamePrefix))
            {
                balance -= 1;
            }
            else if(other.NamePrefix.StartsWith(NamePrefix))
            {
                balance += 1;
            }
            else
            {
                // if NamePrefix is the same or completely different let's assume both filters are equal
            }
        }
        return balance;
    }

    public bool IsSubsetOf(PersonFilter other)
    {
        if(MinAge.HasValue != other.MinAge.HasValue)
        {
            if(other.MinAge.HasValue)
            {
                return false;
            }
        }
        else if(MinAge.HasValue && MinAge.Value < other.MinAge.Value)
        {
            return false;
        }

        if(string.IsNullOrEmpty(NamePrefix) != string.IsNullOrEmpty(other.NamePrefix))
        {
            if(!string.IsNullOrEmpty(other.NamePrefix))
            {
                return false;
            }
        }
        else if(!string.IsNullOrEmpty(NamePrefix))
        {
            if(!NamePrefix.StartsWith(other.NamePrefix))
            {
            return false;
            }
        }

        return true;
    }
}

答案 3 :(得分:1)

查看Specification design pattern

一旦实施,那么在这种情况下您的规范将成为

public class PersonNamedOlderThanSpecification : CompositeSpecification<Person>
{
    private string name;
    private int age;

    public PersonNamedOlderThanSpecification(string name, int age)
    {
        this.name = name;
        this.age = age;
    }


    public override bool IsSatisfiedBy(Person entity)
    {
        return (entity.Name.Contains(this.name)) && (entity.Age > age);
    }
}

然后你可以像这样使用它:

var personSpecs = new PersonNamedOlderThanSpecification("So", 28);
var personSpecs2 = new PersonNamedOlderThanSpecification("Some", 36);

var filtered = people.FindAll(x => personSpecs.IsSatisfiedBy(x));
var adjusted = people.FindAll(x => personSpecs2.IsSatisfiedBy(x));

答案 4 :(得分:0)

你可以试试这个:

var people = new List<Person>
{
     new Person {FullName = "Some Dude", Age = 45},
     new Person {FullName = "Another Dude", Age = 28},
     new Person {FullName = "Some Other Dude", Age = 36}
};

var filtered = people.Where(person => person.Age > 28 && person.FullName.StartsWith("So"));
var narrowlyFiltered = people.Where(person => person.Age > 36 && person.FullName.StartsWith("Some"));

var intersection = filtered.Intersect(narrowlyFiltered);
if (intersection != null)
{
    if (intersection.Count() > 0)
    {
        //narrowlyFiltered is subset of filtered
    }
}