被处理的闭包有一个奇怪的行为

本文关键字:有一个 处理 闭包 | 更新日期: 2023-09-27 18:19:20

我有一个像这样的一次性类:

public class Person:IDisposable
{
    public string Name{get;set;}
    public void Dispose()
    {}
}

,这里有一组扩展方法,如果调用者为null,则返回默认值:

static class Extensions
{
    public class Option<T>
    {
        private readonly Func<T> _resultGetter;
        public bool HasValue { get; }
        public Option(Func<T> resultGetter, bool hasValue)
        {
            _resultGetter = resultGetter;
            HasValue = hasValue;
        }
        public T Value => _resultGetter();
    }
    public static Option<TResult> SafeGetter<T, TResult>(this T self, Func<T, TResult> getter, Func<TResult> defaultGetter = null) where T : class
    {
        defaultGetter = defaultGetter ?? (() => default(TResult));
        return new Option<TResult>(self == null ? defaultGetter :()=> getter(self),self!=null);
    }
}

可以看到,SafeGetter方法返回一个Option对象,该对象有一个getter函数,该getter函数有一个self参数作为闭包。

为了在对象被处理后测试getter函数,我写了这样一个测试:

public void SafeGetterDisposableTest()
{
    Extensions.Option<string> nameGetter; 
    using (var john = new Person { Name = "John" })
    {
        nameGetter = john.SafeGetter(x => x.Name);
    }
    Console.WriteLine(nameGetter.Value);
}

你可以看到,我已经得到了Option对象,并在john被处置后调用它。我以为我会得到一个异常,但令我惊讶的是,测试是有效的。为什么会发生这种情况?这段代码是否以某种方式引入了内存泄漏?

被处理的闭包有一个奇怪的行为

我认为我会得到一个异常,但令我惊讶的是,测试是工作。

仅仅通过简单地实现IDisposable,您不会收到ObjectDisposedException(或任何其他异常)。您的Dispose方法是,并且即使在被您的using语句调用之后也不会更改对象。如果你改变它,在对象被处置后在getter中抛出异常,你将得到一个异常。

这是约定实现IDisposable对象时,当其成员在被处置后被访问时抛出异常。这不是语言的行为。如果您的一次性对象实际上有一个它正在使用的非托管资源,然后被清理,那么在它被处理后执行的操作可能无法正常工作,即使您没有显式抛出,因此约定