Search code examples
c#linqgenericslambdalinq-expressions

Pass generic EquaityComparer with SequenceEqual


I'm updating the question, to be more clear:

I am trying to build a LINQ expression for filtering records.

Eg:

public string Name {get; set;}
public List<string> Score { get; set; }

 public Student(string name, List<string> list=null)
 {
     Name=name;
     Score=list;
 }

And I have a list of these students:

    List<Student> listStudent = new List<Student>
              { new Student("Jane", new List<string>{"A","B"}),
                new Student("Joe", new List<string>{"B", "C"}),
                new Student("Jack")};

Now, I want to get only the students whose scores match a given list:

List<string> scoresToCompare=new List<string>{"B", "C"};
var Result= listStudent.Where(x =>x.Score!=null ? scoresToCompare.SequenceEqual(x.Score,  new StringComparer<string>()): false)

And my StringComparer to ignore case when comparing:

class StringComparer<T> : IEqualityComparer<T>
{
    public bool Equals(T x, T y)
    {
        //Check whether the compared objects reference the same data.
        if (Object.ReferenceEquals(x, y)) return true;

        //Check whether any of the compared objects is null.
        if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
            return false;

        //Ignore case when comparing.
        return x.ToString().Equals(y.ToString(), StringComparison.InvariantCultureIgnoreCase);
    }

    public int GetHashCode(T item)
    {
        //Check whether the object is null
        if (Object.ReferenceEquals(item, null)) return 0;

        //Get hash code for the Name field if it is not null.
        int hashCode = item == null ? 0 : item.GetHashCode();

        //Calculate the hash code for the product.
        return hashCode;
    }

}

Now, I will not know the type of the items in the List until runtime, so I am trying to build an expression to achieve the same result.

Type typeOfItemInList = listStudent.FirstOrDefault().GetType(); //Not the exact code, but thats how I get the type

How do I call the sequenceEqual method, with the stringComparer ?

var callSequenceEqualMethodWithComparer= Expression.Call(typeof(System.Linq.Enumerable), "SequenceEqual", new Type[] typeOfItemsInList }, new Expression[] { listToCompare1,  listToCompare2, **stringComparer**});

And finally I need to also do an Expression.Condition to check if the x.Score !=null to avoid the null exception by:

Expression.Condition(Expression.NotEqual(expressionOfList1, Expression.Constant(null)), Expression.Convert(Expression.Call(callSequenceEqualMethodWithComparer, expressionOfList1, expressionOdList2), typeof(bool)), Expression.Default(typeof(bool)) );

Thanks in advance !


Solution

  • You can create an expression that creates an object by calling Expression.New().

    To get the type, you can use typeof(StringComparer<>) to get the open generic type for StringComparer<T> and then call MakeGenericType() on it to get the closed type, for example StringComparer<string>.

    Put together:

    Expression.New(typeof(StringComparer<>).MakeGenericType(typeOfItemInList))