Creating generic Isolator<T> class for collection modidy inside foreach loop

A

Andrus

Code below causes error in class definition line

.....Isolator<T>' does not implement interface member
'System.Collections.IEnumerable.GetEnumerator()'.
'Myapp.Isolator<T>.GetEnumerator()' cannot implement
'System.Collections.IEnumerable.GetEnumerator()' because it does not have
the matching return type of 'System.Collections.IEnumerator'.

I tried to change method signature to

public IEnumerator GetEnumerator()

bu this causes another error

....Isolator<T>' does not implement interface member
'System.Collections.Generic.IEnumerable<T>.GetEnumerator()'.
'Myapp.Isolator<T>.GetEnumerator()' cannot implement
'System.Collections.Generic.IEnumerable<T>.GetEnumerator()' because it does
not have the matching return type of
'System.Collections.Generic.IEnumerator<T>'.

How to make this code work ?

Andrus.

using System;
using System.Collections;
using System.Collections.Generic ;

public class Isolator<T> : IEnumerable<T>
{
IEnumerable<T> inner;

public Isolator(IEnumerable<T> enumerable)
{
inner = enumerable;
}

public IEnumerator<T> GetEnumerator()
{
ArrayList<T> list = new ArrayList<T>();
foreach (object o in inner)
list.Add(o);
return list.GetEnumerator();
}
}
 
M

Marc Gravell

Because IEnumerable<T> derives from IEnumerable, you need to provide
*both* forms of GetEnumerator(); this is usually dona via explicit
implementation... just add:

IEnumerator IEnumerable.GetEnumerator()
{ // reuse typed enumerator
return GetEnumerator();
}

Marc
 
A

Andrus

Marc,

Thank you. It works.
I'm planning to use it to remove members in linq invoice rows like

foreach (var row in new Isolator<TRow>(rowBindingSource))
{
if (row.Item == "VAT" || row.Item == "ROUNDING")
rowBindingSource.Remove(row);
}

Where rowBindingSource type is your linq TableList class.

Is this best style ?

In VFP I can use SQL directly from my application code

DELETE FROM rowBindingSource WHERE rows IN ("VAT", "ROUNDING")

Why Linq to Objects requires to create loop manually, .i.e forces me to use
procedural programming ?
Why there is no delete command in linq or method like

rowBindingSource.Remove( row=> row.Item == "VAT" || row.Item ==
"ROUNDING" );

or

from row in rowBindingSource
where row=> row.Item in ( "VAT", "ROUNDING" )
delete row;

Andrus.
 
J

Jon Skeet [C# MVP]

Code below causes error in class definition line

<snip>

Marc has provided the reason why this fails, and a fix - but if you're
using C# 3 and .NET 3.5, you can just call ToList() on any
IEnumerable<T> and it'll have *nearly* the same effect - the main
difference being the time of copying. If that's an issue, consider an
extension method:

public static IEnumerable<T> CopyLazily(this IEnumerable<T> source) {
List<T> list = source.ToList();
foreach (T t in list)
{
yield return t;
}
}

It makes life a lot simpler :)

Jon
 
A

Anthony Jones

Andrus said:
Marc,

In VFP I can use SQL directly from my application code

DELETE FROM rowBindingSource WHERE rows IN ("VAT", "ROUNDING")

Why Linq to Objects requires to create loop manually, .i.e forces me to use
procedural programming ?
Why there is no delete command in linq or method like

rowBindingSource.Remove( row=> row.Item == "VAT" || row.Item ==
"ROUNDING" );

or

from row in rowBindingSource
where row=> row.Item in ( "VAT", "ROUNDING" )
delete row;

The Q in LINQ stands for Query.

DELETE is not a query, it modifies the stored data.
 
J

Jon Skeet [C# MVP]

Thank you. It works.
I'm planning to use it to remove members in linq invoice rows like

foreach (var row in new Isolator<TRow>(rowBindingSource))
{
if (row.Item == "VAT" || row.Item == "ROUNDING")
rowBindingSource.Remove(row);
}

Where rowBindingSource type is your linq TableList class.

Is this best style ?

Well, I'd just use:

foreach (var row in rowBindingSource.ToList())
{
...
}

Alternatively you could put a call to Where after .ToList() and remove
every matching row in the loop.
In VFP I can use SQL directly from my application code

DELETE FROM rowBindingSource WHERE rows IN ("VAT", "ROUNDING")

Why Linq to Objects requires to create loop manually, .i.e forces me to use
procedural programming ?

LINQ is designed to be side-effect free. Deleting is a side-effect.
Moreover, LINQ to Objects is based on IEnumerable, which has no notion
for deleting. What would you expect to happen if you tried to delete
from an array, for example?

Jon
 
M

Marc Gravell

Well, even indirectly, mixing enumeration and editing (removal) of a
list is not a greate idea.

Re the difference between SQL delete and .NET version; well, LINQ is
object orientated (SQL isn't); if you want to do anything in LINQ, you
do it to the instances. This is no different. Apart from this, there are
also issues like:
* concurrency management - hard to do on a block delete
* identity: if the data-context issues a block delete, how does it know
which records the database actually deleted? to update it's own local
cache of objects...
* LINQ can be configured to to the CRUD through SPs; how would it issue
a block-delete through a DELETE sproc that only accepts a PKID?

If this is going to be doing a lot of work, then I would create an SP
and call that (you can expose SPs with arguments etc to LINQ-to-SQL very
easily).

If you are dealing with smaller volumes, and you want a Remove(fn) on
some local class, then just add one!

Marc
 
A

Andrus

Well, I'd just use:
foreach (var row in rowBindingSource.ToList())
{
...
}

Alternatively you could put a call to Where after .ToList() and remove
every matching row in the loop.

Thank you. Delete needs to scan over whole collection always.
So I changed foreach to use ToArray()

foreach (var row in rowBindingSource.ToArray() )
{
if (row.Item == "VAT" || row.Item == "ROUNDING")
rowBindingSource.Remove(row);
}

hope this faster than ToList().

Is it reasonable/how to create generic static method which implements this
mass-Remove() for IList<T> collections ?
Since covariant types are not supported in C#, generic type parameter should
be used probably.

Andrus.
 
J

Jon Skeet [C# MVP]

Thank you. Delete needs to scan over whole collection always.

Yes, but if you are only *acting* on certain elements, you can call
Where() to only get those items:

var toDelete = rowBindingSource.Where(row => row.Item=="VAT" ||
row.Item=="ROUNDING").ToList();
foreach (var row in toDelete)
{
rowBindingSource.Remove(row);
}
So I changed foreach to use ToArray()

hope this faster than ToList().

I'd expect it to be slower in some cases, actually - because it needs
to accurately count the number of items before creating the array.
Is it reasonable/how to create generic static method which implements this
mass-Remove() for IList<T> collections ?

Yes, that would be easy to do as an extension method.
Since covariant types are not supported in C#, generic type parameter should be used probably.

Absolutely.

Just offhand (untested):

public static void RemoveWhere<T> (this IList<T> source, Func<T,bool>
predicate)
{
List<T> toRemove = source.Where(predicate).ToList();
foreach (T t in toRemove)
{
source.Remove(t);
}
}

Jon
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top