How do you work with IDisposable sequences using LINQ?

What is the best calling approach for Dispose()sequence elements?

Suppose something like:

IEnumerable<string> locations = ...
var streams = locations.Select ( a => new FileStream ( a , FileMode.Open ) );
var notEmptyStreams = streams.Where ( a => a.Length > 0 );
//from this point on only `notEmptyStreams` will be used/visible
var firstBytes = notEmptyStreams.Select ( a => a.ReadByte () );
var average = firstBytes.Average ();

How do you place instances FileStream(as soon as they are no longer needed) while saving compressed code?


To clarify: this is not an actual piece of code, these lines are methods in a set of classes, and the type is FileStreamalso just an example.


Performs something line by line:

public static IEnumerable<TSource> Where<TSource> (
            this IEnumerable<TSource> source ,
            Func<TSource , bool> predicate
        )
        where TSource : IDisposable {
    foreach ( var item in source ) {
        if ( predicate ( item ) ) {
            yield return item;
        }
        else {
            item.Dispose ();
        }
    }
}

might be a good idea?


Alternatively: do you always solve a very specific scenario with respect IEnumerable<IDisposable>, without trying to generalize? Is this because, if this is not a typical situation? Do you design around it first? If so, how?

+3
6

streams Array List, ( ) .

var streams = locations.Select(a => new FileStream(a, FileMode.Open)).ToList();
// dispose right away of those you won't need
foreach (FileStream stream in streams.Where(a => a.Length == 0))
    stream.Dispose();

var notEmptyStreams = streams.Where(a => a.Length > 0);
// the rest of your code here

foreach (FileStream stream in notEmptyStreams)
    stream.Dispose();

EDIT. , , LINQ . , foreach?

var streams = locations.Select(a => new FileStream(a, FileMode.Open));
int count = 0;
int sum = 0;
foreach (FileStream stream in streams) using (stream)
{
    if (stream.Length == 0) continue;
    count++;
    sum += stream.ReadByte();
}
int average = sum / count;
+3

, , AsDisposableCollection, IEnumerable, IDisposable, using. ( ), , ( , ):

using(var streams = locations.Select(a => new FileStream(a, FileMode.Open))
                             .AsDisposableCollection()) {
  // ...
} 

( - , ):

class DisposableCollection<T> : IDisposable, IEnumerable<T> 
                                where T : IDisposable {
  IEnumerable<T> en; // Wrapped enumerable
  List<T> garbage;   // To keep generated objects

  public DisposableCollection(IEnumerable<T> en) {
    this.en = en;
    this.garbage = new List<T>();
  }
  // Enumerates over all the elements and stores generated
  // elements in a list of garbage (to be disposed)
  public IEnumerator<T> GetEnumerator() { 
    foreach(var o in en) { 
      garbage.Add(o);
      yield return o;
    }
  }
  // Dispose all elements that were generated so far...
  public Dispose() {
    foreach(var o in garbage) o.Dispose();
  }
}
+3

:

List<Stream> streams = locations
    .Select(a => new FileStream(a, FileMode.Open))
    .ToList();

try
{
    // Use the streams.
}
finally
{
    foreach (IDisposable stream in streams)
        stream.Dispose();
}

, - , FileStream , . , :

List<Stream> streams = new List<Stream>();
try
{
    foreach (string location in locations)
    {
        streams.Add(new FileStream(location, FileMode.Open));
    }

    // Use the streams.
}
finally { /* same as before */ }

, , , , , , .

- , LINQ, , , :

+2

:)
, , , , , FirstOrDefault ( ).

, Enumerator, . , , AsDisposeableEnumerable, .

GetMy.Disposeables()
    .AsDisposeableEnumerable() // <-- all the magic is injected here
    .Skip(5)
    .where(i => i > 1024)
    .Select(i => new {myNumber = i})
    .FirstOrDefault()

, .

  • IEnumerable

    public class DisposeableEnumerable<T> : IEnumerable<T> where T : System.IDisposable
    {
        private readonly IEnumerable<T> _enumerable;
    
        public DisposeableEnumerable(IEnumerable<T> enumerable)
        {
            _enumerable = enumerable;
        }
    
        public IEnumerator<T> GetEnumerator()
        {
            return new DisposeableEnumerator<T>(_enumerable.GetEnumerator());
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
    
  • IEnumerator

    public class DisposeableEnumerator<T> : IEnumerator<T> where T : System.IDisposable
    {
        readonly List<T> toBeDisposed = new List<T>();
    
        private readonly IEnumerator<T> _enumerator;
    
        public DisposeableEnumerator(IEnumerator<T> enumerator)
        {
            _enumerator = enumerator;
        }
    
        public void Dispose()
        {
            // dispose the remaining disposeables
            while (_enumerator.MoveNext()) {
                T current = _enumerator.Current;
                current.Dispose();
            }
    
            // dispose the provided disposeables
            foreach (T disposeable in toBeDisposed) {
                disposeable.Dispose();
            }
    
            // dispose the internal enumerator
            _enumerator.Dispose();
        }
    
        public bool MoveNext()
        {
            bool result = _enumerator.MoveNext();
    
            if (result) {
                toBeDisposed.Add(_enumerator.Current);
            }
    
            return result;
        }
    
        public void Reset()
        {
            _enumerator.Reset();
        }
    
        public T Current
        {
            get
            {
                return _enumerator.Current;
            }
        }
    
        object IEnumerator.Current
        {
            get { return Current; }
        }
    }
    
  • ,

    public static class IDisposeableEnumerableExtensions
    {
        /// <summary>
        /// Wraps the given IEnumarable into a DisposeableEnumerable which ensures that all the disposeables are disposed correctly
        /// </summary>
        /// <typeparam name="T">The IDisposeable type</typeparam>
        /// <param name="enumerable">The enumerable to ensure disposing the elements of</param>
        /// <returns></returns>
        public static DisposeableEnumerable<T> AsDisposeableEnumerable<T>(this IEnumerable<T> enumerable) where T : System.IDisposable
        {
            return new DisposeableEnumerable<T>(enumerable);
        }
    }
    
0

, IEnumerable using ( , IEnumerable, , -, #::

public static class DisposableEnumerableExtensions {
    public static DisposableEnumerable<T> AsDisposable<T>(this IEnumerable<T> enumerable) where T : IDisposable {
        return new DisposableEnumerable<T>(enumerable);
    }
}

public class DisposableEnumerable<T> : IDisposable where T : IDisposable {
    public IEnumerable<T> Enumerable { get; }

    public DisposableEnumerable(IEnumerable<T> enumerable) {
        this.Enumerable = enumerable;
    }

    public void Dispose() {
        foreach (var o in this.Enumerable) o.Dispose();
    }
}

:

using (var processes = System.Diagnostics.Process.GetProcesses().AsDisposable()) {
    foreach (var p in processes.Enumerable) {
        Console.Write(p.Id);
    }
}
0

https://lostechies.com/keithdahlby/2009/07/23/using-idisposables-with-linq/, :

(
    from location in locations
    from stream in new FileStream(location, FileMode.Open).Use()
    where stream.Length > 0
    select stream.ReadByte()).Average()

:

public static IEnumerable<T> Use<T>(this T obj) where T : IDisposable
{
    try
    {
        yield return obj;
    }
    finally
    {
        if (obj != null)
            obj.Dispose();
    }
}

, , .

0

Source: https://habr.com/ru/post/1764330/


All Articles