linq-to-objectsanonymous-typesvb.net-2010

Linq to Objects and Anonymous Types


I'm new to Linq to Objects, and I've just hit the problem of anonymous types and scope.

For example, this works just fine:

    Public Sub CreateResults()
        results = From e In CustomerList
                  Join f In OrderList On CustomerList.ID Equals OrderList.ID
                  Select New With {e.FirstName, e.LastName, f.OrderID}

        For Each r in results
            Console.Writeline(r.FirstName, r.LastName, r.OrderID)
        Next t
    End Sub

This however does not:

Public Class Foo

    Private _linqResults 

    Public Sub CreateResults()
        _linqResults = From e In CustomerList
                       Join f In OrderList On CustomerList.ID Equals OrderList.ID
                       Select New With {e.FirstName, e.LastName, f.OrderID}
    End Sub

    Public Sub PrintResults()
        For Each r in _linqResults
            Console.Writeline(r.FirstName, r.LastName, r.OrderID)
        Next t
    End Sub

End Class

I've been looking through SO and other places trying to find a simple solution to this without much luck. Is there any way to access the fields of an anonymous type outside the scope of the method containing the Linq query? Something like:

Console.Writeline(r("FirstName").ToString, r("LastName").ToString)

would be acceptable, though not ideal.

I really, really don't want to start creating additional classes/structures/DTOs to handle what are supposed to be dynamic queries.


Solution

  • It is not possible to infer the types the way you want. You have a few choices though:

    1. Make a additional class (which you don't want)

    2. Change the style of the class so CreateResults and PrintResults is in the same method as you have done. BUT you properly have a good reason not to do that.

    3. Use the dynamic type (I guess there is one in VB, but I don't know for sure. I'm a C# man) and then you simple access the properties you want as you do in your example. This is obviously not as fast as static typing.

    4. There is a hack to do it (at least in C#). But it is not pretty. See How to return Anonymous Type while using Linq

    5. Instead of using a custom class you could use the tuple classes. System.Tuple and store the data there. They came in .Net 4 I think.

    In the end I really think a custom class is the way to go unless you have a good reason not to. I would also like a way to do the same thing you want to, but a custom class can be described in a few lines with newer syntax.

    EDIT: Added a few points and changed a little bit

    Example with dynamic in C# as requested

    class Foo
    {
        private dynamic _linqResults;
    
        public void CreateResults()
        {
            var someData = Enumerable.Range(1, 10);
    
            _linqResults = from i in someData
                           select new
                           {
                               Number = i
                           };
        }
    
        public void PrintResults()
        {
            foreach (var i in _linqResults)
                Console.WriteLine(i.Number);
        }
    }
    

    As you maybe see, it is almost the same syntax as you want. It just uses dynamic which is a bit slower - but it sounds like that you already uses or will use some kind of dynamic/reflection in datagrid and csv. It of cause depends on which DataGrid and CSV (your own?) you use.

    As mentioned in another answer, the compiler can't infer the types and IntelliSense will not be your guide in PrintResults.

    Little update VB has a similar syntax which you can find a good walk-trough with here: Link