//
// TODO: optimization, remove the static initializer, so we do not need to add code
// at JIT/AOT time to probe for static class initialization
//
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Urho {
public partial class RefCounted : IDisposable {
internal IntPtr handle;
public IntPtr Handle => handle;
internal RefCounted (UrhoObjectFlag empty) { }
protected RefCounted (IntPtr handle)
{
if (handle == IntPtr.Zero)
throw new ArgumentException ($"Attempted to instantiate a {GetType()} with a null handle");
this.handle = handle;
Runtime.RegisterObject (this);
}
public void Dispose ()
{
DeleteNativeObject();
Dispose (true);
GC.SuppressFinalize (this);
}
///
/// Called by RefCounted::~RefCounted - we don't need to check Refs here - just mark it as deleted and remove from cache
///
internal void HandleNativeDelete()
{
Dispose(true);
GC.SuppressFinalize(this);
}
[DllImport(Consts.NativeImport, CallingConvention = CallingConvention.Cdecl)]
static extern void TryDeleteRefCounted(IntPtr handle);
///
/// Try to delete underlying native object if nobody uses it (Refs==0)
///
void DeleteNativeObject()
{
if (!IsDeleted)
{
try
{
TryDeleteRefCounted(handle);
}
catch (Exception exc)
{
//should not happen, JIC.
throw new InvalidOperationException("Underlying native object is already deleted", exc);
}
}
}
protected virtual void Dispose (bool disposing)
{
if (IsDeleted)
return;
if (disposing)
{
IsDeleted = true;
OnDeleted();
}
Runtime.UnregisterObject(handle);
}
[Conditional("DEBUG")]
protected void CheckAccess()
{
if (IsDeleted)
throw new InvalidOperationException("The underlying native object was deleted");
if (handle == IntPtr.Zero)
throw new InvalidOperationException("Object has zero handle");
//TODO: check thread
}
protected void CheckEngine()
{
if (!Application.EngineInited)
{
throw new InvalidOperationException("The engine is not inited. Please call UrhoEngine.Init().");
}
}
///
/// True if underlying native object is deleted
///
public bool IsDeleted { get; private set; }
protected virtual void OnDeleted() { }
public override bool Equals (object other)
{
if (other == null)
return false;
if (other.GetType () != GetType ())
return false;
var or = other as RefCounted;
if (or != null && or.handle == handle)
return true;
return false;
}
public static bool operator ==(RefCounted _a, RefCounted _b)
{
object a = _a;
object b = _b;
if (a == null){
if (b == null)
return true;
return false;
} else {
if (b == null)
return false;
return _a.handle == _b.handle;
}
}
public static bool operator !=(RefCounted _a, RefCounted _b)
{
object a = _a;
object b = _b;
if (a == null)
return b != null;
else {
if (b == null)
return true;
return _a.handle != _b.handle;
}
}
public override int GetHashCode ()
{
if (IntPtr.Size == 8) //means 64bit
return unchecked ((int) (long) handle);
return (int) handle;
}
~RefCounted ()
{
DeleteNativeObject();
Dispose (false);
}
}
}