Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing IEnumerable data from LINQ as parameter to a method [duplicate]

Tags:

c#

linq

Possible Duplicate:
How can I pass an anonymous type to a method?

I have the following LINQ Statement, whose output has to be processed in another method:

var data = from lines in File.ReadAllLines(TrainingDataFile)
                             .Skip(ContainsHeader ? 1 : 0)
           let f = lines.Split(new[] { FieldSeparator }).ToList<String>()
           let target = f[TargetVariablePositionZeroBased]
           select new { F=f, T=target };

What should be the datatype of the parameter in the method that will take this data?

like image 813
Aadith Ramia Avatar asked Jan 19 '13 15:01

Aadith Ramia


3 Answers

You can not return the anonymous data types from a method. You can define a class and return object of that class from query and pass it to target method.

public class SomeClass
{
    public string F {get; set;}
    public string T {get; set;}
}

var data = from lines in File.ReadAllLines(TrainingDataFile)
                                .Skip(ContainsHeader ? 1 : 0)
               let f = lines.Split(new[] { FieldSeparator }).ToList<String>()
               let target = f[TargetVariablePositionZeroBased]
               select new SomeClass { F=f, T=target };

You can pass the query result IEnumerable<SomeClass> to method as parameter.

public void MethodToCall(IEnumerable<SomeClass> someClass)
{

}

To call the method by passing the query result (IEnumerable<SomeClass>) that is stored in data in this sample code

MethodToCall(data);
like image 149
Adil Avatar answered Oct 10 '22 01:10

Adil


You can't very easily pass anonymous types around. You can either create a class, or since your data has only two properties, use a Tuple:

select new Tuple<List<string>, string> (f, target);

If I have the data types correct, then the data type of the parameter would be:

IEnumerable<Tuple<List<string>, string>>

and you would reference F and T using the Tuple properties Item1 and Item2.

like image 24
mellamokb Avatar answered Oct 10 '22 01:10

mellamokb


1) Just to pass the result of the query, make your function generic, that will do:

var data = from lines in File.ReadAllLines(TrainingDataFile)
                         .Skip(ContainsHeader ? 1 : 0)
       let f = lines.Split(new[] { FieldSeparator }).ToList<String>()
       let target = f[TargetVariablePositionZeroBased]
       select new { F=f, T=target };

SomeMethod(data);

public void SomeMethod<T>(IEnumerable<T> enumerable)
{
    // ^^choose the return type..
}

Simple. If the processing inside the method is something so simple this will do. But you won't be able to access properties F and T inside the method.

To do so:

2) You can use the "cast by example" trick shown here by Eric. To quote him:

We use method type inference and local variable type inference to tell the compiler "these two things are the same type". This lets you export an anonymous type as object and cast it back to anonymous type.

...the trick only works if the example and the source objects were created in code in the same assembly; two "identical" anonymous types in two different assemblies do not unify to be the same type.

SomeMethod(data);

public void SomeMethod(IEnumerable<object> enumerable)
{
    var template = new { F = new List<string>(), T = string.Empty };
    foreach (var item in enumerable)
    {
        var anonymousType = item.CastToTypeOf(template);
        //print string.Join(", ", anonymousType.F) + " - " + anonymousType.T //compiles
        //or whatever
    }
}

//a more generic name perhaps is 'CastToTypeOf' as an extension method
public static T CastToTypeOf<T>(this object source, T example) where T : class
{
    return (T)source;
}

The catch here is that SomeMethod now is tailor made for your anonymous type, since you're specifying a specific type inside the method, so its better to not make the function generic (though you can do) and to give a suitable name for the function.

3) If function is just for your unique type now, I would better have them all wrapped in a single method and not pass at all - no hassle! :)

4) Or you can delegate the action to be done on your anonymous type. So method signature would be like:

SomeMethod(data, d => print string.Join(", ", d.F) + " - " + d.T);

public void SomeMethod<T>(IEnumerable<T> enumerable, Action<T> actor)
{
    foreach (var item in enumerable)
        actor(item);
}

If it matters you can have Func delegate as well by having one more type argument.

5) Rely on fiddly reflection to get the properties from your anonymous type otherwise.

6) Use dynamic keyword on method argument and now you have dynamic typing. Both the above doesnt give you benefits of static typing.

7) You will be better off having a separate class that holds F and T. And that the best of all. But ask yourself do they together represent something as an entity?

8) If not, just pass an IEnumerable<Tuple> or IDictionary depending on what matters.


It all depends on what/how you want to achieve with the method. Personally, I would go for the approach 2 in a hobby project (for the fun involved), but in production code 3, 4, 7, 8 depending on the context.

like image 23
nawfal Avatar answered Oct 10 '22 02:10

nawfal