I'm a recent exile from Java land and would like to know the following for C# (2.0 ... can't use LINQ in production yet :( ):
What is the best way to not just iterate through a list, but keep track of new entries to it and 'process' those as wel. In other words, I would like to do something like this:
foreach(Data d in myList) Console.WriteLine(d.somevalue);
The above line will just go through the list and display the 'somevalue' attribute of the list. I want the foreach loop to keep 'listening' to 'myList' and each time a new value is added, WriteLine that as well.
The reason I"m posting this in the LINQ forum is because I would like to simulate LINQ with my C# 2.0 code. So I may need to add, say, filters to my list:
foreach (Data x in Operators.Where<Data>(list, delegate(Data d) { return d.name.Equals("Joe"); }))
Console.WriteLine(x.name+ "," + x.salary+ "," + x.age);
I'd then like to link this up to a GUI and watch with a smile as incoming data (perhaps from a network, perhaps manually entered) is automatically updated on win-forms.
(btw: since this is C# 2, I'll obviously be writing my own Where, Group, etc. functions).
Once again, I'm a bit fo a newbie to C# so if I missed something obvious, please point it out. Thanks!
----------------------
The above code is just some quick 'thinking-out-loud' code (as well as some cut&paste). But just for completeness, following is my Where function:
class Operators
{
public static IEnumerable<T> Where<T>(IEnumerable<T> list, Predicate<T> p)
{
foreach(T item in list) if(p(item)) yield return item;
}
}

foreach or yield on a 'stream' instead of an IList?
Luis Mack
I like the approach...
Mareen Philip.
I'll be posting sources sometime in the next month hopefully when I finish the CS integration for my companies website.
partha mandayam
I should note that once your list's enumerator exits (rather than simply yields), that ending will propagate. "yield return" means "here's the next item", whereas "yield break" and falling out of the function scope means "no more items, stop asking". Just in case you hadn't caught that.
The following is similar to a loop I have at work:
IEnumerable<string> GetBytes(SerialPort port)
{
while(true) yield return port.ReadByte();
}
(I say similar, since I actually pull from the underlying stream to avoid the Encoding layer -- I need the raw bytes. I also don't have it in such a foolishly infinite loop.)
mbkh_84
With custom joins I have:
public sealed class Join<T, U>
{
public T Left { get; }
public U Right { get; }
}
I then can do something like:
IEnumerable<Grouping<Customer, Order>> custorders = DbQuery
.From(nwind.Customer)
.InnerJoin(nwind.Order,
delegate(Customer left, Order right)
{
return right.CustomerID = left.CustomerID;
})
.GroupBy<Customer, Order>(
delegate(Join<Customer, Order> j)
{
return j.Customer;
},
delegate(Join<Customer, Order> j)
{
return j.Order;
});
foreach (Grouping<Customer, Order> g in custorders)
{
Console.WriteLine(g.Key.Name);
foreach(Order o in g.Values)
{
Console.WriteLine(o.OrderNumber);
}
}
This lets me do a join, then a group over the join. This isn't working for the database in my current code but it does work in my basic query extensions
Miskky
David Streeter
Assuming list is IEnumerable<Data>, it looks correct to me.
If you wanted to, you could create an IEnumerable<T> wrapper. The following code compiles under RTM csc and Mono gmcs (thanks, Ovatsus.CSharp.Targets), but I haven't tested it:
using System;
using System.Collections;
using System.Collections.Generic;
namespace Thuban
{
/// <summary>
/// Wraps an IEnumerable, adding Extensions a la Linq.
/// </summary>
/// <typeparam name="T"></typeparam>
public class LinqEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerable<T> _enumerable;
public LinqEnumerable(IEnumerable<T> enumerable)
{
this._enumerable = enumerable;
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return this._enumerable.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region Extensions
public IEnumerable<T> Where(Predicate<T> p)
{
foreach (T item in this._enumerable)
{
if (p(item))
{
yield return item;
}
}
}
#endregion
}
}
Frank Boyne
Without anything specific about what you're working on, I don't know how best to approach it. But I can sketch out what I did with the serial port:
while (no keypresses waiting)
{
if (port has data waiting)
{
List<byte> buffer;
// fill buffer from port
foreach (byte b in buffer)
{
Process(b);
}
}
// DoOtherStuff
}
In this case, the enumerator is pulled directly from buffer. The buffer is compatible with IEnumerable<byte>, but is a different instance each time you go through the loop.
Beyond that, if you want to filter when you add, you can subclass List<T> and override Add() and AddRange().
Gary zhuo
Similarly with the Joins -- assume join on primary keys unless otherwise specified.
That should allow you to reduce it to:
IEnumerable<Grouping<Customer, Order>> custorders = DbQuery
.From(nwind.Customer)
.InnerJoin(nwind.Order)
.GroupBy<Customer, Order>();
Again, very cool.. I was thinking of this as well, but I wasn't up tackling the IL -> tree conversion.
mladjoboy
JoePD
a.HasValue -> (a is not null)
a.GetValueOrDefault() -> (a)
.. The server's execution planner should be able to handle this, no
JimScudder
How do you plan to support joins
Harshal Kherde
I'll be publishing the code hopefully in the next few weeks.
int a = 0;
int b = 1;
return a > b;
becomes
int a = 0;
int b = 0;
return a.GetValueOrDefault() > b.GetValueOrDefaul() && a.HasValue == b.HasValue();
BenoitFranc
IEnumerable<U>
SelectMany(IEnumerable<T>, Func<T, IEnumerable<U>>)
IEnumerable<Join<T, U>>
CrossJoin(IEnumerable<T>, IEnumerable<U>)
IEnumerable<Join<T, U>>
InnerJoin(IEnumerable<T>, IEnumerable<U>, Func<T, U, bool>)
IEnumerable<Join<T, U>>
LeftOuterJoin(IEnumerable<T>, IEnumerable<U>, Func<T, U, bool>)
IEnumerable<Join<T, U>>
RightOuterJoin(IEnumerable<T>, IEnumerable<U>, Func<T, U, bool>)
HaidongJi
It work very well, I just have to work out a few kinks in the conversion from msil to an expression tree, and implement some type of caching mechanism for delegates to trees to make it perform a bit better.
The fun stuff with generating these trees is when generating Expression Trees over a closure. Because of how .NET generates the hidden class for a closure I can use the Target of the delegate to get the hidden class and wrap the field that represents the closed value from the outer function inside of a Funclet, similar to how the current linq does the same thing to enclose a closed value in a closure.
Early tests gave me working DB queries using:
int id = 1;
Employee emp = DbQuery
.From(nwind.Employees)
.Where(delegate(Employee e) { return e.EmployeeID == id; })
.FirstOrDefault();