簡體   English   中英

C#表達式比較

[英]C# Expression Comparison

假設我在集合上有以下表達式:

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"));

有沒有辦法比較這兩個表達式並推斷出第二個表達式是運行時第一個表達式的子集? 沒有枚舉或其他任何東西。 我只是有表達式,我試圖找出這些表達式是否相交或相互包含。

你必須將每個Expression分解為所有可能的繼承類型(MethodCallExpression,ConditionalExpression等),然后並行地進行每個分解並檢查每個可能的參數......編碼會有點長......你可以啟發來自ExpressionEqualityComparer

如果您可以枚舉您的集合,您可以先將這些元素放在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

否則,這個問題通常是一個不可判定的問題 雖然有一些啟發式方法可以適用於許多情況。 例如,您可以嘗試使用旨在在編譯時推斷出這一點的代碼契約。

證明:

正式變體是:給定兩個圖靈機(方法,委托,指針),第一語言中包含的每個字符串是否包含在第二個語言中?
不可判定
證明 :鑒於它是可判定的,EQ TM將是可判定的:只需首先驗證第一個圖靈機是否是第二個的子集,反之亦然。 如果兩者都是子集,我們知道它們接受相同的字符串集。

換句話說,如果你能做到這一點,你也可以推斷出兩個函數是否產生了相同的結果, 而這些結果是無法實現的

這一切都取決於你如何權衡相等,更重要的是比較表達式等。例如,如果你有完全不同的過濾器,那么在實際執行它之前你不可能知道查詢差異。

要保持對比較的完全控制,請創建一個過濾器類,其中包含一些屬性,這些屬性可用於過濾,然后構建表達式並使用此類進行比較,而不是使用訪問者。 您可以准備用於比較整數,整數對(范圍)等的通用函數。

我沒有檢查下面的代碼,但它應該是一個好的開始。

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

看看規格設計模式

一旦實施,那么在這種情況下你的規范就變成了

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

你可以試試這個:

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
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM