Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
289 views
in Technique[技术] by (71.8m points)

c# - Make using statement usable for multiple disposable objects

I have a bunch of text files in a folder, and all of them should have identical headers. In other words the first 100 lines of all files should be identical. So I wrote a function to check this condition:

private static bool CheckHeaders(string folderPath, int headersCount)
{
    var enumerators = Directory.EnumerateFiles(folderPath)
        .Select(f => File.ReadLines(f).GetEnumerator())
        .ToArray();
    //using (enumerators)
    //{
        for (int i = 0; i < headersCount; i++)
        {
            foreach (var e in enumerators)
            {
                if (!e.MoveNext()) return false;
            }
            var values = enumerators.Select(e => e.Current);
            if (values.Distinct().Count() > 1) return false;
        }
        return true;
    //}
}

The reason I am using enumerators is memory efficiency. Instead of loading all file contents in memory I enumerate the files concurrently line-by-line until a mismatch is found, or all headers have been examined.

My problem is evident by the commented lines of code. I would like to utilize a using block to safely dispose all the enumerators, but unfortunately using (enumerators) doesn't compile. Apparently using can handle only a single disposable object. I know that I can dispose the enumerators manually, by wrapping the whole thing in a try-finally block, and running the disposing logic in a loop inside finally, but is seems awkward. Is there any mechanism I could employ to make the using statement a viable option in this case?


Update

I just realized that my function has a serious flaw. The construction of the enumerators is not robust. A locked file can cause an exception, while some enumerators have already been created. These enumerators will not be disposed. This is something I want to fix. I am thinking about something like this:

var enumerators = Directory.EnumerateFiles(folderPath)
    .ToDisposables(f => File.ReadLines(f).GetEnumerator());

The extension method ToDisposables should ensure that in case of an exception no disposables are left undisposed.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can create a disposable-wrapper over your enumerators:

class DisposableEnumerable : IDisposable
{
    private IEnumerable<IDisposable> items;

    public event UnhandledExceptionEventHandler DisposalFailed;

    public DisposableEnumerable(IEnumerable<IDisposable> items) => this.items = items;

    public void Dispose()
    {
        foreach (var item in items)
        {
            try
            {
                item.Dispose();
            }
            catch (Exception e)
            {
                var tmp = DisposalFailed;
                tmp?.Invoke(this, new UnhandledExceptionEventArgs(e, false));
            }
        }
    }
}

and use it with the lowest impact to your code:

private static bool CheckHeaders(string folderPath, int headersCount)
{
    var enumerators = Directory.EnumerateFiles(folderPath)
        .Select(f => File.ReadLines(f).GetEnumerator())
        .ToArray();

    using (var disposable = new DisposableEnumerable(enumerators))
    {
        for (int i = 0; i < headersCount; i++)
        {
            foreach (var e in enumerators)
            {
                if (!e.MoveNext()) return false;
            }
            var values = enumerators.Select(e => e.Current);
            if (values.Distinct().Count() > 1) return false;
        }
        return true;
    }
}

The thing is you have to dispose those objects separately one by one anyway. But it's up to you where to encapsulate that logic. And the code I've suggested has no manual try-finally,)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...