views:

61

answers:

1

(This is a follow on from a comment on an answer to this question)

18 months after I posted it, someone spotted a bug in one of my Linq examples, where I use an IDisposable half way through a method chain, which never gets disposed.

I attempted to write an extension method to handle this:

public static IEnumerable<R> Using<T, R>(
     this IEnumerable<T> list, Func<T, R> selector) where R : IDisposable
{
    foreach(var item in list)
        using(var disposable = selector(item))
            yield return disposable;
}

var q = Enumerable.Range(0, 10)
        .Using(i => new Disposable(i))
        .Select(d => d.Id);

After seeing Marc's answer to the question I referenced above, I wondered if I could overload the Select extension method directly, but if I do (by renaming Using to Select), the compiler squeals about Select(d => d.Id), because There is no implicit conversion from 'string' to 'System.IDisposable'.

Here's a test class...

public class Disposable : IDisposable
{
    private string _id;
    private bool _disposed = false;
    public Disposable(int id)
    {
        Id = id.ToString();
        Console.WriteLine("Creating " + Id);
    }
    public void Dispose()
    {
        Console.WriteLine("Disposing " + Id);
        _disposed = true;
    }
    public string Id 
    { 
        get 
        {
            if(_disposed) throw new Exception("Call to disposed object!");
            return _id; 
        }
        set { _id = value; }
    }
}
+1  A: 

Like so? I haven't really changed much here, except the constructor usage...

    static void Main()
    {
        var q = from d in System.Linq.Enumerable.Range(0, 10)
                select new Disposable(d);
    //  alternatively:
    //  var q = System.Linq.Enumerable.Range(0, 10)
    //          .Select(d => new Disposable(d));

        foreach(var item in q)
        {
            Console.WriteLine("Hi");
        }
    }
    public static IEnumerable<R> Select<T, R>(
        this IEnumerable<T> list, Func<T, R> selector) where R : IDisposable
    {
        foreach (var item in list)
            using (var disposable = selector(item))
                yield return disposable;
    }

However! The thing to watch here is that we don't introduce any conflicting LINQ operations.

Marc Gravell
So I'm right that my extension method hides the builtin one, so I've basically 'broken' Select for any other purpose? I was kind of hoping the compiler would be able to tell the difference. Does your SelectMany have the same problem then?
Benjol