Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dynamic, linq and Select()

Considering the following (pointless, but it's for illustration purpose) test class :

public class Test
{
    public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
    {
        return t.Select(x => ToStr(x));
    }

    public IEnumerable<string> ToEnumerableStrsWillCompile(IEnumerable<dynamic> t)
    {
        var res = new List<string>();

        foreach (var d in t)
        {
            res.Add(ToStr(d));
        }

        return res;
    }

    public string ToStr(dynamic d)
    {
        return new string(d.GetType());
    }
}

Why doesn't it compile with the following error, on t.Select(x => ToStr(x)) ?

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<dynamic>' 
to 'System.Collections.Generic.IEnumerable<string>'. An explicit conversion 
exists (are you missing a cast?)

No error on the second method.

like image 488
mathieu Avatar asked May 04 '11 07:05

mathieu


People also ask

What is Dynamic LINQ?

The Dynamic LINQ library exposes a set of extension methods on IQueryable corresponding to the standard LINQ methods at Queryable, and which accept strings in a special syntax instead of expression trees.

Is Dynamic LINQ safe?

And Dynamic Linq is actually composed from strings, therefore it is potentially prone to attack by injection. Obviously, the attacker will have to be aware of the fact that you are using DynamicLinq and could attack only preparing the data so it results in valid malicious Dynamic Linq query.

What is select in LINQ?

Select() takes each source element, transforms it, and returns a sequence of the transformed values.


1 Answers

I believe what happens here is that since the expression ToStr(x) involves a dynamic variable, the whole expression's result type is also dynamic; that's why the compiler thinks that it has an IEnumerable<dynamic> where it expects an IEnumerable<string>.

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(x => ToStr(x));
}

There are two ways you can fix this.

Use an explicit cast:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(x => (string)ToStr(x));
}

This tells the compiler that the result of the expression is definitely going to be a string, so we end up with an IEnumerable<string>.

Replace the lambda with a method group:

public IEnumerable<string> ToEnumerableStrsWontCompile(IEnumerable<dynamic> t)
{
    return t.Select(ToStr);
}

This way the compiler implicitly converts the method group expression to a lambda. Note that since there is no mention of the dynamic variable x in the expression, the type of its result can be immediately inferred to be string because there is only one method to consider, and its return type is string.

like image 65
Jon Avatar answered Sep 21 '22 07:09

Jon