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

  • Thread starter Thread starter Andrus
  • Start date Start date
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();
}
}
 
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
 
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.
 
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
 
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.
 
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
 
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
 
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.
 
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
 
Back
Top