This is my function:
private IEnumerable SeachItem(int[] ItemIds)
{
using (var reader = File.OpenText(Application.StartupPath + @\"
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
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 string
s. 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.
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)
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.
One thing to remember is that LINQ
statements use deferred execution. What that means is that your LINQ
statement doesn't actually execute until you either iterate over it in a foreach
statement or you call the .ToList()
method on myLine
.
For your example try changing:
return myLine;
To:
return myLine.ToList();
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. :)