簡體   English   中英

處理不可變的一次性對象

[英]Dealing with Immutable disposable objects

給定一個 Disposable Immutable class,它有時會持有一些大的東西,並且當 object 被處理兩次時,你不知道是否有副作用或異常,(而且我不持有代碼所有權來修改它以解決這種情況) 處理鏈式轉換的最佳方法是什么?

以 Bitmap 為例。

        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;
        }

如果您需要通過轉換創建一個新的 bitmap,那么采用該 memory 是有意義的,但是如果轉換沒有效果(RotateFlipNone,scale = 0 或 pad = 0),則創建一個新的 bitmap 是沒有意義的。我發現自己創建克隆是為了在每次轉換時返回一個新的 Disposable object,而不是返回相同的輸入 object。

同樣的情況適用於例如 Date object 如果它是 Disposable 並且您需要執行 n 操作,其中一些操作無效,具體取決於輸入參數,(添加零天)。

關鍵是,根據輸入參數,某些操作沒有效果,創建新的 object 仍然比跟蹤哪個用戶是 object 的第一個所有者並且事先了解您正在使用的 API 更容易一些參數真的會創建一個不同的項目或只是一個副本。

  • 這種情況有模式嗎?
  • using是否記下它持有的 object 引用屬於另一個using ,這樣它就不會處理它兩次或者會拋出 ObjectDisposedException?
  • 每次都有一個新的 object 是否是最安全的方法,即使它需要更多的計算和 memory? (從我的角度來看,它看起來是最具可讀性的)

我想到的一個選擇是有一個一次性包裝器 class 確保它持有的 object 不會被處理兩次,但這意味着我需要事先知道轉換是否具有零效果所以我不會調用它或者轉換函數知道這個包裝器機制。 就像是:

    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 原因的大列表)。

我最好的猜測是保留初始轉換,除非存在真正的性能問題,但想知道是否存在一些優雅的解決方案:

  • 一個 function,有時會返回一個新實例,有時會返回給定的 IDisposable 輸入參數本身。
  • 而不是總是返回一個新實例以避免處理兩次

我認為一次性物品並不是真正不可變的。 如果它們在哪里,返回原始 object 或新的 object 不會有任何問題。因為它不是不可變的,我認為你應該總是做同樣的事情,並且永遠不要嘗試通過返回原始 object 來優化。

這種情況有模式嗎?

有一些模式可以減少創建新對象的影響

  1. 就地修改對象——這可能是開銷最少的最簡單的選項。 但它會使代碼更難理解,並且可能並不總是適用。
  2. Object 池- 保留可重復使用的圖像列表,以避免創建 object 的開銷。這可以允許“冰棒不變性”,您可以在其中修改 object 然后凍結它,只有在它返回池時才解凍它。
  3. 讓調用者提供 object 將結果寫入- 這將 object 分配的責任從方法轉移到調用者,調用者應該在更好的 position 中來決定最優策略。

using 是否記下它持有的 object 引用屬於另一個 using,這樣它就不會處理它兩次或者會拋出 ObjectDisposedException?

不, using只是try{....} finally{myObject.Dispose()}的簡寫。 然而,設計良好的對象不應該關心它們是否被處理了兩次。 如果某些第三方 object 不遵循這種做法,則使用包裝器可能是合理的。 但是大多數對象不需要這樣的包裝器,我會讓這樣的包裝器比你建議的要簡單得多,因為它只需要一個標志來判斷它是否已經被處理掉。

每次都有一個新的 object 是否是最安全的方法,即使它需要更多的計算和 memory? (從我的角度來看,它看起來是最具可讀性的)

我認為不可變對象更易於使用和理解,這需要創建新的 object 而不是對其進行修改。 但是,它確實會影響性能。 這種影響是否顯着取決於具體應用。 在做圖像處理之類的事情時,易用性的優先級低於性能是很常見的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM