views:

298

answers:

6

So I have a collection of Razzies created from a Collection of Bloops. I retrieve this collection using a Linq query. Reference:http://stackoverflow.com/questions/923238/linq-select-certain-properties-into-another-object for the query.

I would like to know if it is possible to run a method on all of the newly created Razzies before returning the collection, or even right after, just without using a for-loop.

I tried this:

Dim results = From item In bloops _
              Select New Razzie() With _
              { _
                  .FirstName = item.FirstName, _
                  .LastName = item.LastName _
              }.UpdateAddress(item.Address)

But it returns nothing.

A: 

I'm not sure what you mean by RunVoid. Void suggests no return yet you assign the results to a value.

Do you RunVoid to execute a method on every item and then return the original collection? If so, there is no built-in method but you can add one like so.

<Extension>
Public Function RunVoid(Of T)(source As IEnumerable(Of T), del As Action(Of T) As IEnumerable(Of T)
  For Each cur in source
    del(cur)
  Next
  return source
End Function
JaredPar
Yes, I am trying to run the method on every item then return the original collection. the reason being that the method set some "Read-Only" properties that I can not otherwise access directly. So you are saying I am stuck with a loop?
Russ Bradberry
@Russ, You're stuck with defining the loop one time. Now that you have RunVoid defined you can call it for any collection which implements IEnumerable(Of T). If you paste it inside a module in your application, your sample should compile and run
JaredPar
A: 

results will have a type IQueryable<Razzie> after your LINQ statement. No execution actually occurs on the collection until you iterate over results.

Try calling results.ToList immediately afterward.

Randolpho
+1  A: 

Using a foreach loop after your initial processing is the normal way to do this. If you don't want to use a foreach loop, you'll need to define your own extension method to handle this situation.

mquander
The only problem i have with that is that I need to access a property of the original bloop class (i updated my code to reflect) so I would be limited to a nested loop? in that case, for performance, i would need to get rid of the linq call all together?
Russ Bradberry
+2  A: 

Russ, this might do what you want. It's a pretty simple approach. If this is not what you want, please expand your question.

This will run the method on each element as you enumerate over them. It will not run the method until you enumerate, but you can safely know that the method will run before you use the data.

EDIT Since you are using a sealed 3rd party class, use extension methods. That's what they're for. ;) Modified code to use extension methods.

class MyArgs { }
class Razzie //pretend this is a 3rd party class that we can't edit
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
static class RazzieExtensions
{
    public static Razzie MyMethod(this Razzie razzie, MyArgs args)
    {
        razzie.FirstName = razzie.FirstName.ToUpper();
        return razzie;
    }
}
class Program
{
    static void Main(string[] args)
    {
        var bloops = new List<Razzie>
            {
                new Razzie{FirstName = "name"},
                new Razzie{FirstName = "nAmE"}
            };
        var myArgs = new MyArgs();
        var results = from item in bloops
                      select new Razzie
                      {
                          FirstName = item.FirstName,
                          LastName = item.LastName
                      }.MyMethod(myArgs);

        foreach (var r in results)
            Console.WriteLine(r.FirstName);
        Console.ReadKey();
    }
}
dss539
this "would" work, but in my real code the Razzie class is part of a library that I do not have source for, therefore "MyMethod" is a void and returns nothing.
Russ Bradberry
oh well then I have two words for you: extension method
dss539
or wait... "do not have source for" or "do not have source for and is sealed"? If you simply don't have the source, just inherit from Razzie and extend it. If the class is sealed, however, use an extension method.
dss539
A: 

Why not just do this the normal, unconvaluted way? You can do a bunch of fancy stuff with extension methods...but your likely breaking the purity rule by performing an update with an extension method...and it makes things difficult to understand too. Try this:

var results = from ... select new { Address = item.Address, Razzie = new Razzie { ... }}

foreach (var result in results)
{
    result.Razzie.UpdateAddress(result.Address);
}
jrista
because i need item.Address as my input as the Address property is read-only.
Russ Bradberry
My example accounted for that.
jrista
A: 

If you're looking to abstract away the foreach loop, I'd just write an extension method for IEnumerable<T> that duplicates List<T>'s ConvertAll and ForEach methods, like this:

public static IEnumerable<TOutput> ConvertAll<T, TOutput>(this IEnumerable<T> collection, Converter<T, TOutput> converter)
{
    if (converter == null)
        throw new ArgumentNullException("converter");

    List<TOutput> list = new List<TOutput>();

    foreach (T item in collection)
    {
        list.Add(converter(item));
    }

    return list;
}

public static void ForEach<T>(this IEnumerable<T> collection, Action<T> action)
{
    foreach (T item in collection)
    {
        action(item);
    }
}

Then you could just call something like:

bloops
    .ConvertAll(bloop => new Razzie()
                            { 
                                FirstName = bloop.FirstName, 
                                Lastname = bloop.LastName 
                            })
    .ForEach(razzie => razzie.UpdateAddress());
Chris Doggett