[NUI] Fix CA1063
authorDongsug Song <dongsug.song@samsung.com>
Fri, 11 Dec 2020 07:41:12 +0000 (16:41 +0900)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Mon, 21 Dec 2020 12:05:23 +0000 (21:05 +0900)
- https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1063
- Every unsealed type that declares and implements the IDisposable interface must provide its own protected virtual void Dispose(bool) method. Dispose() should call Dispose(true), and the finalizer should call Dispose(false). If you create an unsealed type that declares and implements the IDisposable interface, you must define Dispose(bool) and call it. For more information, see Clean up unmanaged resources (.NET guide) and Dispose pattern.

src/Tizen.NUI/src/internal/Disposable.cs
src/Tizen.NUI/src/public/BaseHandle.cs
src/Tizen.NUI/src/public/Layouting/FlexLayout.cs
src/Tizen.NUI/src/public/TransitionOptions/TransitionOptions.cs

index b229cd1..eebd3b8 100755 (executable)
@@ -34,6 +34,8 @@ namespace Tizen.NUI
         private bool swigCMemOwn { get; set; }
         private bool isDisposeQueued = false;
 
+        private bool disposedThis = false;
+
         /// <summary>
         /// Create an instance of Disposable.
         /// </summary>
@@ -56,14 +58,7 @@ namespace Tizen.NUI
         /// Dispose.
         /// </summary>
         /// <since_tizen> 6 </since_tizen>
-        ~Disposable()
-        {
-            if (!isDisposeQueued)
-            {
-                isDisposeQueued = true;
-                DisposeQueue.Instance.Add(this);
-            }
-        }
+        ~Disposable() => Dispose(false);
 
         /// <summary>
         /// Dispose.
@@ -71,22 +66,62 @@ namespace Tizen.NUI
         /// <since_tizen> 6 </since_tizen>
         public void Dispose()
         {
-            //Throw excpetion if Dispose() is called in separate thread.
-            if (!Window.IsInstalled())
+            Dispose(true);
+            System.GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Hidden API (Inhouse API).
+        /// Dispose. 
+        /// </summary>
+        /// <remarks>
+        /// Following the guide of https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose.
+        /// This will replace "protected virtual void Dispose(DisposeTypes type)" which is exactly same in functionality.
+        /// </remarks>
+        /// <param name="disposing">true in order to free managed objects</param>
+        // Protected implementation of Dispose pattern.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposedThis)
             {
-                Tizen.Log.Error("NUI", "This API called from separate thread. This API must be called from MainThread.");
                 return;
             }
 
-            if (isDisposeQueued)
+            if (disposing)
             {
-                Dispose(DisposeTypes.Implicit);
+                // TODO: dispose managed state (managed objects).
+                // Explicit call. user calls Dispose()
+
+                //Throw excpetion if Dispose() is called in separate thread.
+                if (!Window.IsInstalled())
+                {
+                    throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
+                }
+
+                if (isDisposeQueued)
+                {
+                    Dispose(DisposeTypes.Implicit);
+                }
+                else
+                {
+                    Dispose(DisposeTypes.Explicit);
+                }
             }
             else
             {
-                Dispose(DisposeTypes.Explicit);
-                System.GC.SuppressFinalize(this);
+                // Implicit call. user doesn't call Dispose(), so this object is added into DisposeQueue to be disposed automatically.
+                if (!isDisposeQueued)
+                {
+                    isDisposeQueued = true;
+                    DisposeQueue.Instance.Add(this);
+                }
             }
+
+            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
+            // TODO: set large fields to null.
+
+            disposedThis = true;
         }
 
         /// <summary>
index 9a09a0b..1b601ba 100755 (executable)
@@ -48,6 +48,8 @@ namespace Tizen.NUI
         //A Flag to check who called Dispose(). (By User or DisposeQueue)
         private bool isDisposeQueued = false;
 
+        private bool disposedThis = false;
+
         /// <summary>
         /// Create an instance of BaseHandle.
         /// </summary>
@@ -107,14 +109,7 @@ namespace Tizen.NUI
         /// Dispose.
         /// </summary>
         /// <since_tizen> 3 </since_tizen>
-        ~BaseHandle()
-        {
-            if (!isDisposeQueued)
-            {
-                isDisposeQueued = true;
-                DisposeQueue.Instance.Add(this);
-            }
-        }
+        ~BaseHandle() => Dispose(false);
 
         /// <summary>
         /// Event when a property is set.
@@ -295,24 +290,65 @@ namespace Tizen.NUI
         /// <since_tizen> 3 </since_tizen>
         public void Dispose()
         {
-            //Throw excpetion if Dispose() is called in separate thread.
-            if (!Window.IsInstalled())
+            Dispose(true);
+            System.GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Hidden API (Inhouse API).
+        /// Dispose. 
+        /// </summary>
+        /// <remarks>
+        /// Following the guide of https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose.
+        /// This will replace "protected virtual void Dispose(DisposeTypes type)" which is exactly same in functionality.
+        /// </remarks>
+        /// <param name="disposing">true in order to free managed objects</param>
+        // Protected implementation of Dispose pattern.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposedThis)
             {
-                Tizen.Log.Error("NUI", "This API called from separate thread.This API must be called from MainThread.");
                 return;
             }
 
-            if (isDisposeQueued)
+            if (disposing)
             {
-                Dispose(DisposeTypes.Implicit);
+                // TODO: dispose managed state (managed objects).
+                // Explicit call. user calls Dispose()
+
+                //Throw excpetion if Dispose() is called in separate thread.
+                if (!Window.IsInstalled())
+                {
+                    throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
+                }
+
+                if (isDisposeQueued)
+                {
+                    Dispose(DisposeTypes.Implicit);
+                }
+                else
+                {
+                    Dispose(DisposeTypes.Explicit);
+                }
             }
             else
             {
-                Dispose(DisposeTypes.Explicit);
-                System.GC.SuppressFinalize(this);
+                // Implicit call. user doesn't call Dispose(), so this object is added into DisposeQueue to be disposed automatically.
+                if (!isDisposeQueued)
+                {
+                    isDisposeQueued = true;
+                    DisposeQueue.Instance.Add(this);
+                }
             }
+
+            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
+            // TODO: set large fields to null.
+
+            disposedThis = true;
         }
 
+
         /// <summary>
         /// Performs an action on this object with the given action name and attributes.
         /// </summary>
index 49b2296..dc5b3c7 100755 (executable)
@@ -95,6 +95,7 @@ namespace Tizen.NUI
         private bool swigCMemOwn;
         private bool disposed;
         private bool isDisposeQueued = false;
+        private bool disposedThis = false;
 
         private MeasureSpecification parentMeasureSpecificationWidth;
         private MeasureSpecification parentMeasureSpecificationHeight;
@@ -275,26 +276,73 @@ namespace Tizen.NUI
             return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr;
         }
 
+        /// <summary>
+        /// Hidden API (Inhouse API).
+        /// Destructor.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        ~FlexLayout() => Dispose(false);
+
         /// <inheritdoc/>
         /// <since_tizen> 6 </since_tizen>
         public void Dispose()
         {
-            // Throw exception if Dispose() is called in separate thread.
-            if (!Window.IsInstalled())
+            Dispose(true);
+            System.GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        /// Hidden API (Inhouse API).
+        /// Dispose. 
+        /// </summary>
+        /// <remarks>
+        /// Following the guide of https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose.
+        /// This will replace "protected virtual void Dispose(DisposeTypes type)" which is exactly same in functionality.
+        /// </remarks>
+        /// <param name="disposing">true in order to free managed objects</param>
+        // Protected implementation of Dispose pattern.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposedThis)
             {
-                Tizen.Log.Error("NUI", "This API called from separate thread.This API must be called from MainThread.");
                 return;
             }
 
-            if (isDisposeQueued)
+            if (disposing)
             {
-                Dispose(DisposeTypes.Implicit);
+                // TODO: dispose managed state (managed objects).
+                // Explicit call. user calls Dispose()
+
+                //Throw excpetion if Dispose() is called in separate thread.
+                if (!Window.IsInstalled())
+                {
+                    throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
+                }
+
+                if (isDisposeQueued)
+                {
+                    Dispose(DisposeTypes.Implicit);
+                }
+                else
+                {
+                    Dispose(DisposeTypes.Explicit);
+                }
             }
             else
             {
-                Dispose(DisposeTypes.Explicit);
-                System.GC.SuppressFinalize(this);
+                // Implicit call. user doesn't call Dispose(), so this object is added into DisposeQueue to be disposed automatically.
+                if (!isDisposeQueued)
+                {
+                    isDisposeQueued = true;
+                    DisposeQueue.Instance.Add(this);
+                }
             }
+
+            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
+            // TODO: set large fields to null.
+
+            disposedThis = true;
         }
 
         /// <inheritdoc/>
@@ -308,28 +356,39 @@ namespace Tizen.NUI
 
             if (type == DisposeTypes.Explicit)
             {
-                // Called by User
-                // Release your own managed resources here.
-                // You should release all of your own disposable objects here.
-
+                //Called by User
+                //Release your own managed resources here.
+                //You should release all of your own disposable objects here.
             }
 
-            // Release your own unmanaged resources here.
-            // You should not access any managed member here except static instance.
-            // because the execution order of Finalizes is non-deterministic.
+            //Release your own unmanaged resources here.
+            //You should not access any managed member here except static instance.
+            //because the execution order of Finalizes is non-deterministic.
             if (swigCPtr.Handle != global::System.IntPtr.Zero)
             {
                 if (swigCMemOwn)
                 {
                     swigCMemOwn = false;
-                    Interop.FlexLayout.DeleteFlexLayout(swigCPtr);
+                    ReleaseSwigCPtr(swigCPtr);
                 }
                 swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
             }
+
             disposed = true;
         }
 
         /// <summary>
+        /// Hidden API (Inhouse API).
+        /// Release swigCPtr.
+        /// </summary>
+        /// This will not be public opened.
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected virtual void ReleaseSwigCPtr(System.Runtime.InteropServices.HandleRef swigCPtr)
+        {
+            Interop.FlexLayout.DeleteFlexLayout(swigCPtr);
+        }
+
+        /// <summary>
         /// Creates a FlexLayout object.
         /// </summary>
         /// <since_tizen> 6 </since_tizen>
index 4a8b975..9cdedbc 100755 (executable)
@@ -219,6 +219,11 @@ namespace Tizen.NUI
             this.frameBroker.SendLaunchRequest(appControl, true);
         }
 
+        /// <summary>
+        /// Hidden API (Inhouse API).
+        /// Dispose.
+        /// </summary>
+        /// <param name="disposing"></param>
         [EditorBrowsable(EditorBrowsableState.Never)]
         protected virtual void Dispose(bool disposing)
         {
@@ -244,6 +249,7 @@ namespace Tizen.NUI
         public void Dispose()
         {
             Dispose(true);
+            System.GC.SuppressFinalize(this);
         }
     }
 }