Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Components and GameObject evaluate == to null after destroyed, except w/ interfaces?

Discussion in 'Scripting' started by lordofduct, Nov 11, 2012.

  1. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,052
    So check this...

    In unity GameObjects and Components, when destroyed, evaluate as == to null. This is useful because the managed mono wrapper for them actually still exists (you can call GetInstanceID on them for instance), but a whole bunch of it has been destroyed and it's waiting for garbage collection.

    Well I use interfaces rather frequently and if you reference a component by its interface, this evaluation no longer works. It'll still return as non-null. But if you attempt to use it for anything it throws errors (of course, because it's been destroyed).

    Code (csharp):
    1.  
    2. IMyComponent comp = obj.GetComponent<Something>() As IMyComponent;
    3. GameObject.Destroy(obj);
    4. if(comp == null) ... //this doesn't work
    5.  
    You can see this in action in this attachment:

    View attachment $DestroyedTest.zip

    Open Scene01, play it, and click the sphere on the right... you'll see how it doesn't evaluate as null. You'll see in the script 'Loop' that I have a work around.

    Now this work around is what I'm here to talk about.

    Currently I just have it so that all my interfaces that represent a component inherit from a generic interface 'IComponent'. And IComponent has a property for the component, and a property to test if 'isDestroyed'... which does the test directly on the component. Getting the default unity behaviour.

    I just don't like this... it means I can no longer say:

    if(mycomp == null)

    I instead have to say:

    if(mycomp == null || mycomp.isDestroyed)

    or I create a utility function that does the long testing for me...

    if(MyUtility.IsNullOrDestroyed(mycomp))





    So yeah, wanted to both point this out to everyone AND get some people's opinion on the matter.
     
    electric_jesus and Ghopper21 like this.
  2. Democre

    Democre

    Joined:
    Mar 31, 2010
    Posts:
    345
    That is curious. It seems as interface typed class members set up another wrapper around the object.

    I altered the Loop code as follows:
    Code (csharp):
    1.     private Script _obj;
    2.    
    3.     // Use this for initialization
    4.     void Start ()
    5.     {
    6.         var go = GameObject.Find ("Sphere");
    7.         var tmp = go.GetComponent<Script> () as IScript;
    8.         _obj = (Script)tmp;
    9.     }
    or
    Code (csharp):
    1.     private Script _obj;
    2.    
    3.     // Use this for initialization
    4.     void Start ()
    5.     {
    6.         var go = GameObject.Find ("Sphere");
    7.         _obj = go.GetComponent ( typeof(IScript)) as Script;
    8.     }
    And the expected behavior returns. In both alterations, the type of the class member has been changed to concrete instead of interface. The expected behavior is there whether or not it has been cast through interface type. This indicates to me that a reference to an interface typed object is not the same as a reference to a concrete typed object.

    Definitely something to be aware of.
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,052
    yeah, if the variable is a concrete type, it works fine... it's when you reference it as an interface type.
     
  4. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,525
    Interesting gotcha but I'm curious what the use case for something like this is? Because GetComponent only takes concrete types that inherit from Component it would seem like you'd be handcuffed a bit and casting back to the interface wouldn't really get you much since you already need to know the concrete type.

    I guess alternately you could write an extension method that takes an interface type and calls GetComponents<Component> "behind the scenes".

    Off the top of my head - and this might not even get you any further, but would save you the interface members I guess.
    Code (csharp):
    1.  
    2. public static T GetInterfaceComponent<T>(this GameObject gameObject) where T : class
    3. {
    4.     Component[] components = gameObject.GetComponents<Component>();
    5.     foreach (Component comp in components)
    6.     {
    7.         if (comp is T)
    8.         {
    9.             return comp as T;
    10.         }
    11.     }
    12.     return default(T);
    13. }
    14.  
     
    Last edited: Nov 12, 2012
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,052
    I have functions that will let me get component/components of some interface type. So the GetComponent thing isn't an issue. Similarish to what you (KelsoMRK) posted. My issue isn't getting a reference to the interface... it's that once I have it I can't test null evaluations.

    This allows me to write components that have the shape of an interface... which commands that they follow some contract.

    For instance I have an interface (the one where I found this issue) called IEntity. An IEntity is a component that is attached to a GameObject that is an intelligent entity. It can see/hear/smell/pathfind and has health/strength/etc.

    Code (csharp):
    1.  
    2. interface IEntity
    3. {
    4.  
    5. float MaxHealth { get; }
    6. float CurrentHealt { get; set; }
    7. float Strength { get; set; }
    8.  
    9. float CanSee(IEntity targ)
    10. float CanSmell(IEntity targ)
    11. float CanHear(IEntity targ)
    12.  
    13. void FollowPath(Path path)
    14.  
    15. //... other stuff too
    16.  
    17. }
    18.  
    Now how it implement CanSee depends on the entity. Some entities may see by overlapping some geometry in front of itself. Another, like the player, might see what ever is in the main camera's view frustum. Another might be able to see through walls or the entire world or something.

    Well that means the component for each entity is different, has different implementations. But they're all entities. So I implement the IEntity interface and do my custom work in there. Now when I reference it I don't care if it's the player entity, or bear entity... it's an entity, and that's all I need to know.

    For instance... HunterVision, this calculates some entity that another might have advantage over. If I have advantage and I'm stalking it, I can perform an ultra on it. Think like the "bite" attack in "Tokyo Jungle".

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4.  
    5. using com.spacepuppy.Collections;
    6. using com.spacepuppy.Geom;
    7. using com.spacepuppy.Timers;
    8. using com.spacepuppy.Utils;
    9. using Ponyo;
    10. using Ponyo.Items;
    11.  
    12. /// <summary>
    13. /// Represents the vision of a hunter and calculates the hunter's advantage, if any.
    14. /// Requires IEntity interface attached to gameObject.
    15. /// </summary>
    16. public class HunterVision : com.spacepuppy.SPComponent
    17. {
    18.  
    19.     #region Fields
    20.  
    21.     private IEntity _self;
    22.     private IEntity _targ;
    23.  
    24.     private RandomSwitch _switch = new RandomSwitch(0.2f, 1.0f, 0.2f, 1.0f);
    25.  
    26.     #endregion
    27.  
    28.     #region Properties
    29.  
    30.     public IEntity Target
    31.     {
    32.         get { return _targ; }
    33.     }
    34.  
    35.     #endregion
    36.  
    37.     #region Unity Events
    38.  
    39.     void Start()
    40.     {
    41.         IEntity comp;
    42.         if (this.GetFirstComponentOfInterface<IEntity>(out comp))
    43.         {
    44.             _self = comp;
    45.         }
    46.         else
    47.         {
    48.             //throw new System.Exception("HunterVision must have a sibling component of type IEntity.");
    49.             Debug.Log("HunterVision must have a sibling component of type IEntity.");
    50.             Object.Destroy(this);
    51.         }
    52.     }
    53.  
    54.     // Update is called once per frame
    55.     void Update ()
    56.     {
    57.         if (ObjUtil.IsNullOrDestroyed(_targ)) return;
    58.  
    59.         if (!_self.HasAdvantageOver(_targ))
    60.         {
    61.             _targ = null;
    62.             _switch.Reset();
    63.         }
    64.         else
    65.         {
    66.             _switch.Update(Time.deltaTime);
    67.         }
    68.     }
    69.  
    70.     #endregion
    71.  
    72.     #region Public Methods
    73.  
    74.     public bool FindTarget()
    75.     {
    76.         var ents = _self.GetVisibleEntities();
    77.  
    78.         if (ents.Length == 0) return false;
    79.  
    80.         System.Array.Sort(ents, delegate(IEntity o1, IEntity o2)
    81.         {
    82.             var d1 = (o1.transform.position - this.transform.position).sqrMagnitude;
    83.             var d2 = (o2.transform.position - this.transform.position).sqrMagnitude;
    84.             return d1.CompareTo(d2);
    85.         });
    86.  
    87.         foreach (var ent in ents)
    88.         {
    89.             if (this.FocusOnTarget(ent)) return true;
    90.         }
    91.  
    92.         return false;
    93.     }
    94.  
    95.     public bool FocusOnTarget(IEntity targ)
    96.     {
    97.         if (targ != null  !_self.HasAdvantageOver(targ)) return false;
    98.  
    99.         _targ = targ;
    100.         return true;
    101.     }
    102.  
    103.     public bool CanPerformUltra()
    104.     {
    105.         if (ObjUtil.IsNullOrDestroyed(_targ)) return false;
    106.  
    107.         return  _switch.Allow;
    108.     }
    109.  
    110.     public bool CanPerformUltra(IEntity targ)
    111.     {
    112.         if (ObjUtil.IsNullOrDestroyed(targ) || ObjUtil.IsNullOrDestroyed(_targ)) return false;
    113.  
    114.         return (targ == _targ  _switch.Allow);
    115.     }
    116.  
    117.     #endregion
    118.  
    119. }
    120.  

    Note all the lines where I say:

    ObjUtil.IsNullOrDestroyed(targ)

    That's because 'targ' is referenced as IEntity, and even though it's a Component, when it's destroyed saying:

    targ == null

    still evaluates as false... but if I say:

    ((Component)targ) == null

    it works




    Currently the ObjUtil.IsNullOrDestroyed works for me because it allows me to do extra testing to it if I need to. For instance I could write an IEntity that wraps a component, but isn't a component itself. All my interfaces that are supposed to be components inherit from an interface IComponent:

    Code (csharp):
    1.  
    2. interface IComponent
    3. {
    4.     GameObject gameObject { get; }
    5.     Transform transform { get; }
    6.     Component component { get; }
    7.     bool isDestroyed { get; }
    8. }
    9.  
    10. interface IEntity : IComponent
    11. {
    12. //...
    13. }
    14.  
    Then I just basically say in that funciton:

    Code (csharp):
    1.  
    2. public static bool IsNullOrDestroyed(object obj)
    3. {
    4.     if(obj is IComponent)
    5.         return (obj as IComponent).isDestroyed;
    6.     else if(obj is Component)
    7.         return (obj as Component) == null;
    8.     else
    9.         return obj == null;
    10. }
    11.  
    I just don't like that this weird oddity occurs. And thought everyone here would like to know about it. And maybe other people could share how they deal with it.
     
    Last edited: Nov 12, 2012
  6. znoey

    znoey

    Joined:
    Oct 27, 2011
    Posts:
    174
    I wonder if this stems from unity using their own object.
    Code (csharp):
    1.  
    2.         public static bool operator !=(Object x, Object y);
    3.         public static bool operator ==(Object x, Object y);
    4.         public static implicit operator bool(Object exists);
    5.  
    so a unity object overrides these things and a component derives from this Object class. Since your references the IEntity as an interface your not calling these overrides (which probably perform a similar check in your IsNullOrDestroyed).
    Things to ponder... Interesting find for sure and thanks for posting it!
     
  7. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,525
    I'm sure that has a lot to do with it. Obviously, destroyed components are not actually removed from memory (indeed, has lordofduct mentioned there are still some methods and properties that remain accessible) so it would appear that the engine is doing something similar to his solution.

    To lordofduct's response - yeah I suppose that makes sense. I was more looking at it from the standpoint of "does this object fall into this category because it has this interface" in which case the concrete type wouldn't be as helpful. (That is to say using your example, given an object - is that object intelligent?)
     
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,052
    From what I understand when you destroy a GameObject and its Components there's two parts to those objects.

    Unity isn't written on just mono, but instead uses mono for all its scripting. GameObjects have a bunch of unmanaged code on the non-mono side of things. And calling Destroy tells unity to dump all that used memory and destroy the object. But what still exists is the mono part of the objects which have a representation in the mono runtime. This stuff must wait until garbage collection to be destroyed.

    There is also the issue of all the references to said mono object holding it in memory from garbage collection.

    So I assume the mono object becomes a weak reference type after Destroy is called, and it evaluates as null.
     
  9. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    There is ontop of that Destroy and DestroyImmediate

    Destroy flags the object for cleanup at the end of frame, so you can continue to access it on this frame but you will potentially get greeted by the nice 'try to access blabla but was already destroyed' message in the console.

    DestroyImmediate kills it at right this moment, stopping all executions of coroutines on it etc. There are only few cases where DestroyImmediate as such is an option as you have only limited control over the order of scripts, so you could end in an unpredictable state or transition


    This whole stuff has nothing to do with garbage colleciton actually as nothing that extends UnityObject.Object is managed by mono at all.
    Its all native code managed and will neither respect nor be touched by System.GC
    The only stuff that ends in the garbage are your strings and other reference types that extend from System.Object or other mono classes etc
     
  10. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,525
    Hm - I understood it differently. I was under the impression that anything inheriting from UnityEngine.Object is never offered for garbage collection unless the developer explicitly calls UnloadUnusedAssets(). This, to me, insinuated that the engine was holding onto a reference somewhere and that the managed object - in one form or another - still existed.

    Unless that is what you're saying and I'm just not reading you correctly. :)

    Edit - dreamora clarified as I was typing.
     
  11. Deleted User

    Deleted User

    Guest

    It's neither Unity nor Mono. Basically it's C# because operator overloading and interfaces do not go well together. See
    http://stackoverflow.com/questions/143485/implicit-operator-using-interfaces and http://stackoverflow.com/questions/9861260/overloading-operator-for-an-interface for details. It boils down to:

    So just like static methods can not be expressed in contract based programming (i.e. interfaces) neither can overloaded operators. The bad news is that everything compiles just fine and silently uses the reference equality operator instead.

    There is no solution to this other than dropping interfaces or dropping operator overloading. The latter might actually happen http://blogs.unity3d.com/2014/05/16/
     
    Last edited by a moderator: Jul 13, 2014
    guneyozsan and Ghopper21 like this.
  12. Deleted User

    Deleted User

    Guest

    I don't know the benefit of your IComponent.isDestroyed vs. null-checking the component. But just in case anyone finds this thread looking for a minimal solution for checking through an interface if its component is destroyed here is my take:

    Code (csharp):
    1. public static bool IsNullOrDestroyed(object obj)
    2. {
    3.     return ReferenceEquals(obj, null) || obj.Equals(null);
    4. }
    It works because the virtual method Equals is overridden in the same way as operator== in Unity.
     
    L_Artista and guneyozsan like this.
  13. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,709
    The issue here is that your interface cannot implement the UnityEngine.Object equality operator.

    When an object is "destroyed", it still exist, and Unity does some weird stuff in their operator to return true on "== null" for all UnityEngine.Object. You can also do

    Code (csharp):
    1. if (myObject)
    Even if the object have been destroyed, which in reality should not be possible. However, since the managed side still work under the all-mighty garbage collector, it is normal the object still "exist" since you still reference it, or at least its managed representation.

    Just to add to LumpN, I would do;

    Code (CSharp):
    1. public static class ObjectUtility
    2. {
    3.     public static bool IsNullOrDestroyed(this object value)
    4.     {
    5.         return ReferenceEquals(value, null) || value.Equals(null);
    6.     }
    7. }
    An extension method in the object type, so it can be used by all object like this;

    Code (CSharp):
    1.     public void Method()
    2.     {
    3.         Material mat = null;
    4.         if (mat.IsNullOrDestroyed())
    5.             mat = new Material(Shader.Find("Diffuse"));
    6.     }
     
    guneyozsan and Ghopper21 like this.
  14. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,725
    LightStriker, I would like to know your opinion on a similar method posted by Bunny83 here (last post).
    Code (csharp):
    1.  
    2.   public static bool IsNull ( System.Object aObj )
    3.    {// coherent check wether an object is null (is handled differently by unity), source: Bunny83 http://forum.unity3d.com/threads/148090-Fun-with-null
    4.      return aObj == null || aObj.Equals ( null );
    5.    }
    6.  
    I don't care that much for performance more for consistency and reliability (robustnes). Especially I'm interested in the differences and implications of using System.Object (instead of UnityEngine.Object) and using == operator versus Equals method. Reading the comments in the Blogpost posted by LumpN reveals that there are many things going on under the hood and I cannot deduce the details out of this "mess".
    Thanks for your time.
     
  15. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,709
    An object (or System.Object) will never be null unless you explicitly do "myVariable = null". Even Unity's UnityEngine.Object are not really null, but an overload on the "==" operator returns true when the unmanaged version have been destroyed. One very bad thing that can actually happen is that the Garbage Collector could collect a managed object that was not destroyed on the unmanaged side. I'm fairly sure Unity does nothing to prevent that.

    When you derive from ScriptableObject and MonoBehaviour, you get that special behaviour. Frankly, you should never derive directly from UnityEngine.Object, and always from ScriptableObject and MonoBehaviour. Trying to derive from UnityEngine.Object directly will simply mess your serialization.

    As for "==" vs "Equals" vs "ReferenceEquals", usually == and Equals should return the same result, however since they are two different overload, someone may plug different behaviour. I'm fairly sure Unity's == and Equals give the same result when called from the same level. However, I think the == is tested against the variable type, while Equals find the proper overload, so if called on a System.Object, they may return a different result. So Bunny83 is not wrong by doing == and Equals.

    ReferenceEquals actually test if the object itself is null, not if the object equals to null.

    Code (csharp):
    1. GameObject.Destroy(gameObject)
    2. ReferenceEquals(gameObject, null); // return false;
    3. gameObject = null;
    4. ReferenceEquals(gameObject, null); // return true;
    However, like said in this thread, this kind of fancy test should only be needed if the current variable is an interface, because it has no guaranty it is a UnityEngine.Object. I'm not surprised at all that CLR falls back to the default comparison - which fails on == null - instead of trying to find by reflection the proper type and the proper overload.
     
  16. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,725
    Thanks for your explanation. Just a note but I'm sure you know it. Destroy is not immediate but done after end of frame so your second line in the example should wait until next frame to check if gameObject is null.

    I just asked regarding UnityEngine.Object because i thought the special treatment is only required for objects which have an counterpart on the unmanaged side of the engine. So in my naivety it would be consistent and required for UnityEngine.Object only.

    Anyway, I find all this managed/unmanaged mess very annoying and poorly documented. There should be a list of Do's and Dont's and an overview which operator returns what in which situation. Sometimes I got the feeling (simply because its not well communicated) the people at UT don't even know this and the implications which arise from it. I could be wrong though ;).
    Anyway, thanks again.
     
  17. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,709
    I could have added WaitForSecond(10) after it, it would have not changed the result. The managed object still exist, even if the unmanaged one is destroyed. Just to be clear, a managed object's destructor cannot be called manually, it is called when the garbage collector performs its job. So as long as something references the managed object, it will continue to exist.

    To quote Microsoft;

    And yes, Unity still have one of the worst serialization pipeline in existence.
     
    Maisey likes this.
  18. horatiu665

    horatiu665

    Joined:
    Nov 22, 2012
    Posts:
    15
    Hey guys,

    I hate to break any bubbles but I tried all the suggestions in this thread and they didn't work for me. I am referencing an interface type which is on a monobehaviour which is on an object which can (and often will) be destroyed in my game, therefore I have to nullcheck that interface before using it in some other behaviours. I tried checking for null, using object.Equals(null), ReferenceEquals(myObject, null), the interface of interfaces suggestion at the beginning of the post, and nothing worked for me. I managed to develop yet another hack for it... it involves the following method. Say what you want, but at least I can get things running again......

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public static class ObjectDestroyExtension
    4. {
    5.     public static bool IsDestroyed(this UnityEngine.Object obj)
    6.     {
    7.         try
    8.         {
    9.             if (obj is Component)
    10.             {
    11.                 var activeself = (obj as Component).gameObject.activeSelf;
    12.             }
    13.             return obj == null;
    14.         }
    15.         catch (MissingReferenceException e)
    16.         {
    17.             Debug.LogWarning("[IsDestroyed()] Considering object " + obj + " to be destroyed. " + e);
    18.             return true;
    19.         }
    20.     }
    21. }
    For a bit of context:
    obj is an interface on a monobehaviour on an object that has been destroyed some time ago. It is referenced in a list somewhere.
    obj == null returns false.
    obj.gameObject == null returns false.
    obj.gameObject.activeSelf gives me the MissingReferenceException saying that I should nullcheck (thanks Unity for rubbing it in my face) or not destroy the object
    Therefore I chose to embrace that error and use a try..catch instead of a nullcheck. Works like a charm!

    Happy hacking everyone!
    Horatiu

    PS: forgot to mention... You pretty much have to cast your interface to UnityEngine.Object before being able to use this hack. But I guess that should be possible since that's why you have this problem in the first place.
     
    Last edited: Feb 19, 2017
  19. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    877
    I would avoid try catch especially if you can manage it (which you can there). the first step would be to not write out long call chains. check each call every step of the way. this is considered a low level component that could be used by everything, so its important for it to be thorough, like so:
    Code (CSharp):
    1. if (obj is Component)
    2. {
    3.     Component comp = obj as Component;
    4.     if(!comp) return false;
    5.    
    6.     var gameObject = comp.gameObject;
    7.     if(!gameObject) return false;
    8.    
    9.     var activeself = gameObject.activeSelf;
    10.     return activeSelf;
    11. }
    however in this case testing the gameobject and the activeself is kind of pointless. For an IsDestroyed call the "if(!comp) return false;" is more than sufficient (since components won't exists without a gameobject, and if the gameobject is inactive, that doesn't mean its destroyed). infact just change that "if(!comp) return false;" to "return !((bool)comp);" and it'll work with no exceptions. Also that code seems specifically for Components, if that was the case why not just pass in the component as is.

    Anywho, in reply to the original problem LordofDuct posted, this is how I would solve the interface problem, for all types of UnityObjects

    Instead of just using equality checks, I use the UnityObject bool operator. its what implictly used anyway on UnityObjects. casting to UnityObject and then casting to bool should easily show if the object was destroyed or not

    Best of all you can use extension methods on null objects

    Code (CSharp):
    1. public static class NullInterfaceExtensions
    2. {
    3.     public static bool IsNull(this IMyInterface obj)
    4.     {
    5.         if(obj == null)
    6.             return true;
    7.  
    8.         if(obj is UnityEngine.Object)
    9.             return !((bool)(UnityEngine.Object)obj);
    10.  
    11.         return false;
    12.     }
    13.     public static bool IsNull(this IMyOtherInterface obj)
    14.     {
    15.         if(obj == null)
    16.             return true;
    17.  
    18.         if(obj is UnityEngine.Object)
    19.             return !((bool)(UnityEngine.Object)obj);
    20.  
    21.         return false;
    22.     }
    23.  
    24. }
    Best part is that this should work for any UnityObject, not just GameObjects, and Not just Components. be it sprites, Materials, Behaviours, ScriptableObjects, Editors, AnimationClips Animation Controllers.
    Example:
    Code (CSharp):
    1. public void DoSomethingWith(IMyInterface target)
    2. {
    3.    if(target.IsNull()) return;
    4.  
    5.    // do stuff on the target
    6. }
     
  20. guavaman

    guavaman

    Joined:
    Nov 20, 2009
    Posts:
    5,154
    Yes, it's very hacky, but here's an even lazier version:

    Code (csharp):
    1. internal static class ExtensionMethods {
    2.     public static bool IsNullOrDestroyed(this object obj) {
    3.         return obj == null || ((obj is UnityEngine.Object) && (UnityEngine.Object)obj == null);
    4.     }
    5. }
    Now you can call IsNullOrDestroyed on any interface or on any object including null itself!

    Code (csharp):
    1. if(((object)null).IsNullOrDestroyed()) return;
    Of course, if there's any class in your project that defines a method IsNullOrDestroyed(object obj), you'll get a compiler error. Definitely not for production code. :D

    Another much safer possibility is to make all of your interfaces that may be used on a MonoBehaviour inherit from a common ancestor like so:

    Code (csharp):
    1. public interface IUnityObject { }
    2.  
    3. public interface MyInterface : IUnityObject {
    4.     void SomeMethod();
    5. }
    6.  
    7. public interface MyInterface2 : IUnityObject {
    8.     void SomeOtherMethod();
    9. }
    10.  
    11. internal static class ExtensionMethods {
    12.     public static bool IsNullOrDestroyed(this IUnityObject obj) {
    13.         return obj == null || ((obj is UnityEngine.Object) && (UnityEngine.Object)obj == null);
    14.     }
    15. }
    Edit: Fixed code errors so nobody would copy/paste them.
     
    Last edited: Mar 18, 2017
  21. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,796
    I may be reading this wrong. But wouldn't this fail if you run it on a non UnityEngine.Object?

    If obj is not a UnityEngine.Object then (obj as UnityEngine.Object) will evaluate to null. Then the second half of the or will return true, and true will be returned.

    The method should work in general. I just think your Boolean logic is slightly off.

    Code (csharp):
    1. internal static class ExtensionMethods {
    2.     public static bool IsNullOrDestroyed(this object obj) {
    3.         return !(obj != null || (obj as UnityEngine.Object) != null);
    4.     }
    5. }
     
  22. guavaman

    guavaman

    Joined:
    Nov 20, 2009
    Posts:
    5,154
    Indeed. <facepalm> The point was about the extension method on the System.Object, so I didn't really pay much attention to the value comparison part. :oops:
     
    Kiwasi likes this.
  23. guavaman

    guavaman

    Joined:
    Nov 20, 2009
    Posts:
    5,154
    Actually, I had to use this today and I found out neither version of the code worked correctly. This version evaluates to false when a Unity Object is already destroyed because the obj != null always evaluates to true as long as the System.Object still exists. Here's better code that is verified to work:

    Code (csharp):
    1. internal static class ExtensionMethods {
    2.     public static bool IsNullOrDestroyed(this object obj) {
    3.         return obj == null || ((obj is UnityEngine.Object) && (UnityEngine.Object)obj == null);
    4.     }
    5. }
    I've edited the original post so as to prevent someone from copying and pasting the bad code.
     
    Last edited: Mar 18, 2017
    Gizmoi likes this.
  24. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,052
    Wow, this is an old thread of mine.

    I've since updated the way I check for if destroyed. I went about it by calling the internal method used by Unity themselves:
    https://github.com/lordofduct/spacepuppy-unity-framework/blob/master/SpacepuppyBase/Utils/ObjUtil.cs

    Code (csharp):
    1.  
    2. namespace com.spacepuppy.Utils
    3. {
    4.     public static class ObjUtil
    5.     {
    6.  
    7.         #region Fields
    8.  
    9.         private static System.Func<UnityEngine.Object, bool> _isObjectAlive;
    10.  
    11.         #endregion
    12.  
    13.         #region CONSTRUCTOR
    14.  
    15.         static ObjUtil()
    16.         {
    17.             try
    18.             {
    19.                 var tp = typeof(UnityEngine.Object);
    20.                 var meth = tp.GetMethod("IsNativeObjectAlive", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
    21.                 if (meth != null)
    22.                     _isObjectAlive = System.Delegate.CreateDelegate(typeof(System.Func<UnityEngine.Object, bool>), meth) as System.Func<UnityEngine.Object, bool>;
    23.                 else
    24.                     _isObjectAlive = (a) => a != null;
    25.             }
    26.             catch
    27.             {
    28.                 //incase there was a change to the UnityEngine.dll
    29.                 _isObjectAlive = (a) => a != null;
    30.                 UnityEngine.Debug.LogWarning("This version of Spacepuppy Framework does not support the version of Unity it's being used with.");
    31.                 //throw new System.InvalidOperationException("This version of Spacepuppy Framework does not support the version of Unity it's being used with.");
    32.             }
    33.         }
    34.        
    35.         #endregion
    36.  
    37.         /// <summary>
    38.         /// Returns true if the object is either a null reference or has been destroyed by unity.
    39.         /// This will respect ISPDisposable over all else.
    40.         /// </summary>
    41.         /// <param name="obj"></param>
    42.         /// <returns></returns>
    43.         public static bool IsNullOrDestroyed(this System.Object obj)
    44.         {
    45.             if (object.ReferenceEquals(obj, null)) return true;
    46.  
    47.             if (obj is ISPDisposable)
    48.                 return (obj as ISPDisposable).IsDisposed;
    49.             else if (obj is UnityEngine.Object)
    50.                 return !_isObjectAlive(obj as UnityEngine.Object);
    51.             else if (obj is IComponent)
    52.                 return !_isObjectAlive((obj as IComponent).component);
    53.             else if (obj is IGameObjectSource)
    54.                 return !_isObjectAlive((obj as IGameObjectSource).gameObject);
    55.            
    56.             return false;
    57.         }
    58.  
    59.     }
    60. }
    61.  
    I wanted to add support for my 'ISPDisposable' objects to be be tested and result as true if they were disposed.

    Furthermore, I wanted to make sure that if my IComponents are wrappers (they aren't directly the component, but instead are containers for components) resolve correctly.

    Same goes for what I call IGameObjectSource's.

    Furthermore, I use the reflected internal 'IsNativeObjectAlive' method just to have the shortest call chain for what is already several tests before actually getting to test it.
     
    Kiwasi likes this.
unityunity