BlackjacketMack
BlackjacketMack

Reputation: 5692

Why does using Linq's .Select() return IEnumerable<dynamic> even though the type is clearly defined?

I'm using Dapper to return dynamic objects and sometimes mapping them manually. Everything's working fine, but I was wondering what the laws of casting were and why the following examples hold true.

(for these examples I used 'StringBuilder' as my known type, though it is usually something like 'Product')

Example1: Why does this return an IEnumerable<dynamic> even though 'makeStringBuilder' clearly returns a StringBuilder object?

Example2: Why does this build, but 'Example1' wouldn't if it was IEnumerable<StringBuilder>?

Example3: Same question as Example2?

private void test()
    {
        List<dynamic> dynamicObjects = {Some list of dynamic objects};

        IEnumerable<dynamic> example1 = dynamicObjects.Select(s => makeStringBuilder(s));

        IEnumerable<StringBuilder> example2 = dynamicObjects.Select(s => (StringBuilder)makeStringBuilder(s));

        IEnumerable<StringBuilder> example3 = dynamicObjects.Select(s => makeStringBuilder(s)).Cast<StringBuilder>();

    }

    private StringBuilder makeStringBuilder(dynamic s)
    {
        return new StringBuilder(s);
    }

With the above examples, is there a recommended way of handling this? and does casting like this hurt performance? Thanks!

Upvotes: 3

Views: 399

Answers (2)

DuncanMack
DuncanMack

Reputation: 340

While I can't speak very well to the "why", I think you should be able to write example1 as:

IEnumerable<StringBuilder> example1 = dynamicObjects.Select<dynamic, StringBuilder>(s => makeStringBuilder(s));

You need to tell the compiler what type the projection should take, though I'm sure someone else can clarify why it can't infer the correct type. But I believe by specifying the projection type, you can avoid having to actually cast, which should yield some performance benefit.

Upvotes: 1

Reed Copsey
Reed Copsey

Reputation: 564363

When you use dynamic, even as a parameter, the entire expression is handled via dynamic binding and will result in being "dynamic" at compile time (since it's based on its run-time type). This is covered in 7.2.2 of the C# spec:

However, if an expression is a dynamic expression (i.e. has the type dynamic) this indicates that any binding that it participates in should be based on its run-time type (i.e. the actual type of the object it denotes at run-time) rather than the type it has at compile-time. The binding of such an operation is therefore deferred until the time where the operation is to be executed during the running of the program. This is referred to as dynamic binding.

In your case, using the cast will safely convert this to an IEnumerable<StringBuilder>, and should have very little impact on performance. The example2 version is very slightly more efficient than the example3 version, but both have very little overhead when used in this way.

Upvotes: 2

Related Questions