给定一个 Disposable Immutable 类,它有时包含一些很大的东西,并且您不知道当对象被释放两次时是否存在副作用或异常,(并且我不持有代码所有权来修改它以解决这种情况) 如何处理链式转换的最佳方法是什么?
以位图为例。
public static Bitmap Transform(Bitmap src, RotateFlipType rotate = RotateFlipType.RotateNoneFlipNone,
double scale = 0, int pad = 0, int alterGradient = 0)
{
using Bitmap rotated = src.Rotate(rotate);
using Bitmap scaled = MyImageUtils.ScaleBitmap(rotated, scale);
using Bitmap padded = MyImageUtils.PaddBitmap(scaled, scale);
//The owner is the caller
Bitmap result = MyImageUtils.Gradient(padded, alterGradient);
return result;
}
如果您需要使用转换创建新位图,则占用该内存是有意义的,但如果转换没有效果(RotateFlipNone、scale = 0 或 pad = 0),则创建新位图没有意义。我发现自己创建克隆是为了在每次转换时返回一个新的 Disposable 对象,而不是返回相同的输入对象。
例如,如果 Date 对象是 Disposable 并且您需要执行 n 个操作,则相同的情况将适用于该对象,其中一些操作对输入参数没有影响(添加零天)。
关键是,某些操作对输入参数没有影响,创建新对象仍然比跟踪哪个用户是对象的第一个所有者更容易,并且如果某些参数事先了解您正在使用的 API真的会创建一个不同的项目或只是一个副本。
- 这种情况有模式吗?
- 是否
using
考虑到它持有的对象引用属于另一个using
,所以它不会两次处理它或抛出 ObjectDisposedException ? - 即使需要更多的计算和内存,每次都拥有一个新对象是最安全的方法吗?(从我的角度来看,它看起来是最易读的)
我想到的一个选择是拥有一个 Disposable 包装类,以确保它持有的对象不会被释放两次,但这意味着我需要事先知道转换是否具有零效应,所以我不会调用它或者转换函数知道这个包装机制。就像是:
public class DisposableOnce<T> : IDisposable
where T : IDisposable
{
private bool disposedValue;
public delegate void DisposedDelegate(EventArgs e);
public event DisposedDelegate? OnDisposed;
public T Value { get; }
private readonly DisposableOnce<T>? Other;
public DisposableOnce(T value)
{
Value = value;
}
public DisposableOnce(DisposableOnce<T> disposableOther)
{
Value = disposableOther.Value;
Other = disposableOther;
Other.OnDisposed += OnRefDisposed;
}
private void OnRefDisposed(EventArgs e)
{
SetDisposed();
}
public void SetDisposed()
{
disposedValue = true;
try
{
OnDisposed?.Invoke(new EventArgs());
}
catch (Exception ex)
{
//Shallow the exception to avoid propagation?
throw ex;
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
Value.Dispose();
if (Other != null)
{
//Not listening you anymore
Other.OnDisposed -= OnRefDisposed;
}
}
SetDisposed();
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
它会像这样使用:
public static Bitmap Transform(Bitmap src, RotateFlipType rotate = RotateFlipType.RotateNoneFlipNone, double scale = 0, int pad = 0, int alterGradient = 0)
{
using DisposableOnce<Bitmap> rotated = new DisposableOnce<Bitmap>(src.Rotate(rotate));
using DisposableOnce<Bitmap> scaled = scale == 0 ? new DisposableOnce<Bitmap>(rotated) : new DisposableOnce<Bitmap>(MyImageUtils.ScaleBitmap(rotated.Value, scale));
using DisposableOnce<Bitmap> padded = pad == 0 ? new DisposableOnce<Bitmap>(scaled) : new DisposableOnce<Bitmap>(MyImageUtils.PaddBitmap(scaled.Value, scale));
Bitmap result;
if (alterGradient == 0)
{
//Avoid the value being disposed by the wrapper relatives
padded.SetDisposed();
result = padded.Value;
}
else
{
result = MyImageUtils.Gradient(padded.Value, alterGradient);
}
return result;
}
这要大得多,令人困惑,需要对每个变换函数有更多的了解(+ 大的 nono 原因列表)。
我最好的猜测是保持初始转换,除非存在真正的性能问题,但想知道是否存在一些优雅的解决方案:
- 有时返回新实例并有时返回给定 IDisposable 输入参数本身的函数。
- 而不是总是返回一个新实例以避免处理两次