Search code examples
c#linqlambdaexpression-trees

Unable to pass IEnumerable<string> as parameter in Expression.Call


This is my class:

class SampleExpression
{
    public static void SampleEnum(IEnumerator<string> ien)
    {
        while (ien.MoveNext())
        {
            Console.WriteLine(ien.Current);
        }
    }
}

This is how i am trying to invoke the SampleEnum method:

    public void Main(string[] args)
    {
        ParameterExpression param2 = Expression.Parameter(typeof(SampleExpression), "args");

        var lstConstant = "1,2,3,4,".Split(new string[] { "," },
                                                StringSplitOptions.RemoveEmptyEntries).ToList();

        Expression ep = Expression.Constant(Expression.Constant(lstConstant, typeof(IEnumerable<string>)));
        var enummethod = typeof(SampleExpression).GetMethod("SampleEnum");
        MethodCallExpression methodCall = Expression.Call
                                        (
                                            enummethod
                                            , ep
                                        );

        var e = Expression.Lambda<Func<IEnumerator<string>, string>>(methodCall, param2);
        e.Compile()(lstConstant.GetEnumerator());
    }

I get the following error in the line which tries to create the method call expression:

Expression of type 'System.Linq.Expressions.TypedConstantExpression' cannot be used for parameter of type 'System.Collections.Generic.IEnumerator1[System.String]' of method 'Void Enum(System.Collections.Generic.IEnumerator1[System.String])'

Please help.


Solution

  • You seem to be very confused - it might help to have a look at how the C# compiler would generate a similar expression tree. In any case, there's a few mistakes you've made:

    • IEnumerable<T> isn't IEnumerator<T>. They're not interchangeable.
    • param2 is a parameter expression that takes SampleExpression, while your lambda actually expects IEnumerator<string>.
    • ep is a constant expression of a constant expression. That's not what you want. Remove the outer Expression.Constant.
    • The lambda argument isn't actually used anyway - you can just use Func<string> instead of Func<IEnumerator<string>, string>.
    • Your expression tree has no return value, while the lambda expects to return a string.

    Assuming you want to have a lambda that takes IEnumerator<string> and uses it to call SampleExpression.SampleEnum, you could use something like this:

    public void Test()
    {
        var enumeratorParameter 
            = Expression.Parameter(typeof(IEnumerator<string>), "enumerator");
    
        var sampleEnumMethod = typeof(SampleExpression).GetMethod("SampleEnum");
        var methodCall = Expression.Call(sampleEnumMethod, enumeratorParameter);
    
        var e = Expression.Lambda<Func<IEnumerator<string>, string>>
                  (
                    methodCall, 
                    enumeratorParameter
                  );
    
        var lstConstant = "1,2,3,4,".Split(',');
        e.Compile()(lstConstant.ToList().GetEnumerator());
    }
    
    class SampleExpression
    {
        public static string SampleEnum(IEnumerator<string> ien)
        {
            while (ien.MoveNext())
            {
                Console.WriteLine(ien.Current);
            }
    
            return "Done!";
        }
    }