'LINQ: How to declare IEnumerable[AnonymousType]?

This is my function:

    private IEnumerable<string> SeachItem(int[] ItemIds)
    {
        using (var reader = File.OpenText(Application.StartupPath + @"\temp\A_A.tmp"))
        {
            var myLine = from line in ReadLines(reader)
                         where line.Length > 1
                         let id = int.Parse(line.Split('\t')[1])
                         where ItemIds.Contains(id)
                         let m = Regex.Match(line, @"^\d+\t(\d+)\t.+?\t(item\\[^\t]+\.ddj)")
                         where m.Success == true
                         select new { Text = line, ItemId = id, Path = m.Groups[2].Value };
            return myLine;
        }
    }

I get a compile error,because "myLine" is not a IEnumerable[string] and I don't know how to write IEnumerable[Anonymous]

"Cannot implicitly convert type 'System.Collections.Generic.IEnumerable[AnonymousType#1]' to 'System.Collections.Generic.IEnumerable[string]'"



Solution 1:[1]

You cannot declare IEnumerable<AnonymousType> because the type has no (known) name at build time. So if you want to use this type in a function declaration, make it a normal type. Or just modify your query to return a IENumerable<String> and stick with that type.

Or return IEnumerable<KeyValuePair<Int32, String>> using the following select statement.

select new KeyValuePair<Int32, String>(id, m.Groups[2].Value)

Solution 2:[2]

I am not necessarily recommending this... It is a kind of subversion of the type system but you could do this:

1) change your method signature to return IEnumerable (the non generic one)

2) add a cast by example helper:

public static class Extensions{
    public static IEnumerable<T> CastByExample<T>(
            this IEnumerable sequence, 
            T example) where T: class
    {
        foreach (Object o in sequence)
            yield return o as T;
    }
}

3) then call the method something like this:

var example = new { Text = "", ItemId = 0, Path = "" };
foreach (var x in SeachItem(ids).CastByExample(example))
{
    // now you can access the properties of x 
    Console.WriteLine("{0},{1},{2}", x.Text, x.ItemId, x.Path);
}

And you are done.

The key to this is the fact that if you create an anonymous type with the same order, types and property names in two places the types will be reused. Knowing this you can use generics to avoid reflection.

Hope this helps Alex

Solution 3:[3]

The method signature on SearchItem indicates that the method returns an IEnumerable<string> but the anonymous type declared in your LINQ query is not of type string. If you want to keep the same method signature, you have to change your query to only select strings. e.g.

return myLine.Select(a => a.Text);

If you insist on returning the data selected by your query, you can return an IEnumerable<object> if you replace your return statement with

return myLine.Cast<object>();

Then you can consume the objects using reflection.

But really, if your going to be consuming an anonymous type outside the method that it is declared in, you should define a class an have the method return an IEnumerable of that class. Anonymous types are convenience but they are subject to abuse.

Solution 4:[4]

Your function is trying to return IEnumerable<string>, when the LINQ statement you are executing is actually returning an IEnumerable<T> where T is a compile-time generated type. Anonymous types are not always anonymous, as they take on a specific, concrete type after the code is compiled.

Anonymous types, however, since they are ephemeral until compiled, can only be used within the scope they are created in. To support your needs in the example you provided, I would say the simplest solution is to create a simple entity that stores the results of your query:

public class SearchItemResult
{
    public string Text { get; set; }
    public int ItemId { get; set; }
    public string Path { get; set; }
}

public IEnumerable<SearchItemResult> SearchItem(int[] itemIds)
{
    // ...
    IEnumerable<SearchItemResult> results = from ... select new SearchItemResult { ... }
}

However, if your ultimate goal is not to retrieve some kind of object, and you are only interested in, say, the Path...then you can still generate an IEnumerable<string>:

IEnumerable<string> lines = from ... select m.Groups[2].Value;

I hope that helps clarify your understanding of LINQ, enumerables, and anonymous types. :)

Solution 5:[5]

Return a ValueTuple instead of an anonymous class. Ex (using "named tuples")-

(Text: line, ItemId: id, Path: m.Groups[2].Value)

https://docs.microsoft.com/en-us/dotnet/csharp/tuples

Instead of-

new { Text = line, ItemId = id, Path = m.Groups[2].Value }

The ValueTuple is part of C# version 7 and was originally implemented as a separate NuGet package (System.ValueTuple). Starting with .NET 4.7 it is a built-in type. For .NET Core, versions prior to 2.0 required the NuGet package but it is built-in with version 2.0.

Solution 6:[6]

The question was asked a long time ago, I hope it helps someone...

"You cannot declare IEnumerable", instead, must convert it to a "custom" IEnumerable:

public class MyString
    {
        public string String { get; set; } = string.Empty;
    }

public IEnumerable<MyString> GetMyStrings(List<string> Strings)
        {
            var AnonString = from S in Strings group S by S into Grouped select new { String = Grouped.Key };

            IEnumerable<MyString> Result = AnonString.Select(x => new MyString() { String = x.String }).ToArray();

            return Result;
        }

Regards.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Daniel Brückner
Solution 2 Alex James
Solution 3 jason
Solution 4 jrista
Solution 5 Sixto Saez
Solution 6 Metafunken