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.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Testing for a Destroyed object... that's an interface.

Discussion in 'Scripting' started by UnbridledGames, Oct 17, 2020.

  1. UnbridledGames

    UnbridledGames

    Joined:
    May 12, 2020
    Posts:
    139
    So I have an interface, IHasContextMenu. I attach that to some scripts that attach to gameobjects, and that provides methods that allow right-click popup menus when those objects are right-clicked.

    Recently, I made some changes to some prefabs where those scripts are added to some gameobjects dynamically. Gameobject is instantiated from a prefab, placed in an object pool, and when it's needed, it's given out. The correct ItemTypeScript is applied (which has the IHasContextMenu interface) and all is well.

    Problem: When that object is released, and the ItemTypeScript is destroyed, the script that handles the context menu, which caches the IHasContextMenu reference to the script stops working right.

    Did some research. Unity objects which are Destroyed will still return false if tested for == null. Ok, got it. Website I found said that instead of testing == null, just using the object itself returns a bool, so you can test something like:

    public SomeScript scriptRef;

    Destroy(scriptRef);
    if(scriptRef == null) {
    // This will NOT run, if the scriptRef has been Destroyed. testing == null will eval false because weird Unity stuff
    }
    if(!scriptRef) {
    // THIS will correctly evaluate, as Unity gameobjects have overloads for this
    }


    The problem I'm facing is I'm not using a reference to a SCRIPT. I'm using a reference to an INTERFACE. The interface resides on quite a few different scripts, so caching the reference to IHasContextMenu and just calling contextMenuRef.menuItems works great.

    Except that once that reference is Destroy'ed, testing == null results in false. And apparently, I can NOT do a boolean if(!contextMenuRef) because that's not a built in overload.

    So if a cached reference to an interface is Destroyed, I can't find a way to test for that. Yes, I COULD make sure the destroying script also sets the reference to null, but that is a LOT of extra code in a lot of places.

    Is there a decent solution for this? The best I could think of was checking if GetComponent<IHasContextMenu>() returned anything, but that seems excessive.
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,724
    If you really want to rely on Unity's destroyed object magic, you could probably do something like this:

    Code (CSharp):
    1. if (!(scriptRef as UnityEngine.Object)) {
    2.   // If we got here, the object was destroyed (or never assigned, or it's not a UnityEngine.Object).
    3. }
     
    Forberg, atkins79 and Bunny83 like this.
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,780
    This is a pickle to be sure. The null check is effectively useless.

    One thing I've done is when I implement the interface, check if
    .gameObject
    has been destroyed or not, and don't do the rest of the function. That doesn't get you through all cases (such as needing to return a valid thing), but it's something.

    An alternate plan is to loosen the coupling, such that the interface functions only transact against local variables (such as a mailbox or queue) and then Update() is charged with doing something with the posted data. Again, doesn't handle all cases, such as "give me your X" case.

    If you must, you could always implement a second interface,
    IExistable
    and have all calling sites responsible for getting that and testing it. Alternately you could implement a
    bool DoYouStillExist()
    method into ALL your interfaces.

    In all cases, meh, not great. But interfaces are still greatly worthwhile, imnsho.
     
  4. UnbridledGames

    UnbridledGames

    Joined:
    May 12, 2020
    Posts:
    139
    What I did for now, due to the lack of a good/elegant solution, is check for the GetComponent<IMyInterface> still exists on the gameObject in question. These checks don't happen that often, so it's not like it will cause any performance issues. It just feels sloppy and I like my code to be a bit cleaner than that.

    I've worked a little with operator overloads - I had a lot of fun implementing a custom == overload for my ItemData so I could test a few different things. Totally not necessary, a method would have handled it just fine, but, was a cool learning experience.

    That being said, how would you overload something to be testable as bool with a simple if check, since technically it's not using any operator.

    If that question isn't clear, it's based off of my origial question above.

    SomeScript scriptInstance;

    scriptInstance = AddComponent<SomeScript>();

    Destroy(scriptInstance);

    if(scriptInstance) {
    // I'm still here!
    } else {
    // Nope, I was destroyed
    }


    That if check... personally I love those checks. I come from a long background in C and it took me a long while to get used to all the == null and != null checks, when I used to just do if(!thing).

    How would you go about overloading that?
     
  5. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,724
    Actually it is using an operator!

    You would create an implicit conversion-to-bool operator. Here's some documentation from Microsoft on how to write conversion operators in C#: https://docs.microsoft.com/en-us/do...e/operators/user-defined-conversion-operators

    Their example in that link is for conversion between Digit and byte, but you can write your own between any two types you want.
     
    UnbridledGames likes this.
  6. UnbridledGames

    UnbridledGames

    Joined:
    May 12, 2020
    Posts:
    139
    Thanks for the link. One of the hardest parts (for me anyway) of learning new coding techniques/languages is not knowing the terminology to even google for. I'll give that a read. Thanks again.
     
  7. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,539
    Unity actually implements an implicit type conversion operator from UnityEngine.Object to bool. That's why you can simply use a reference of type UnityEngine.Object in place where a boolean is expected (like in an if statement). Note that type conversion operators can only be implemented in one of the two types you want to convert between. So this is not really an option for interfaces since you can not declare operators inside interfaces. Since we want a conversion to bool that's also not an option. The best solution for interfaces would be an extension method. Extension methods have to be declared inside a static class. So you can use a method like this:

    Code (CSharp):
    1. public static class UnityObjectAliveExtension
    2. {
    3.     public static bool IsAlive(this object aObj)
    4.     {
    5.         var o = aObj as UnityEngine.Object;
    6.         return o != null
    7.     }
    8. }
    9.  
    This would essentially give every type an IsAlive method which would always return false except it it's a UnityEngine.Object derived type and it's still alive. Note that I would not recommend attaching an extension method to the System.Object type as this method would be valid on literally any type. Though it would be useless on all types except UnityEngine.Object derived types. For example this would be possible as well:

    Code (CSharp):
    1. int v = 5;
    2. if (v.IsAlive())
    3.     // will always be false since the as-cast inside the method will fail and evaluate to null
    4.  
    So I would recommend to create a seperate interface like IHasAliveCheck and restrict the extension method to this interface:

    Code (CSharp):
    1.  
    2. public interface IHasAliveCheck { }
    3. public static class UnityObjectAliveExtension
    4. {
    5.     public static bool IsAlive(this IHasAliveCheck aObj)
    6.     {
    7.         if (aObj is UnityEngine.Object o)
    8.             return o != null;
    9.         return aObj != null;
    10.     }
    11. }
    12.  
    13.  
    Now your own interface can simply implement this IHasAliveCheck interface and it gets the extention method:

    Code (CSharp):
    1. public interface IHasContextMenu : IHasAliveCheck
    2. {
    3.     // [ ... ]
    4. }
    That's still kinda hacky but it provides a universal null check that does handle UnityEngine.Object objects correctly. So only when the passed object is of type UnityEngine.Object we use Unity's custom null check. In all other cases we just use the ordinary null check. This IsAlive method will always return true except when the passed reference is either null or a fake null UnityEngine.Object.

    Note that extension methods are just syntactic sugar in the compiler. It's essentially a fallback solution. However that means if your actual interface has its own IsAlive method, the extension method would not work / would not be visible. They only kick in when the compiler can't find a method with this name / signature.

    Technically instance methods of objects work just like extension methods. The actual code for all methods is always static. Instance methods just have an implicit "first" argument which is the instance reference they should be executed on. All this is just hidded behind syntactic sugar of the compiler. The only methods that work a bit differently are virtual / abstract method or interface methods as they require dynamic dispatch. Though after the resolving of the actual method it again works the same way. This can be seen when looking at the CreateDelegate method of the reflection system. For instance methods you have to provide the "firstArgument" which essentially provides the "this" reference inside the method itself. See this blog on dynamic dispatch in C#.

    The programming language Lua actually has a neat feature that provides some sort of "instance" methods for tables. Lua has the
    .
    (dot) operator to access a value inside a table by name. Since functions in lua can simply be stored in a table value you can call methods like this

    someTable.myFunc()


    In addition to the "dot" operator lua provides the "colon" operator
    :
    . This is also just syntactic sugar. It simply passes the table as the first argument. So when doing

    someTable:myFunc(5, 3)


    you actually do

    someTable.myFunc(someTable, 5, 3)
     
    _geo__, troyspencer and BigRookGames like this.
  8. unity_ctg0ujCJYRi52g

    unity_ctg0ujCJYRi52g

    Joined:
    Jun 1, 2020
    Posts:
    11
    I don't know if its the best solution but for me worked smth like this:
    Code (CSharp):
    1. if(scriptRef == null)
    Code (CSharp):
    1. if((Object)scriptRef == null)
     
  9. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,539
    This only works if you can guarantee that the object behind the interface is an UnityEngine.Object derived class. You would get an invalid cast exception in any other case. You could use an "as" cast instead. Though this would just silently filter out any valid instances which are not derived from MonoBehaviour or ScriptableObject. Depending on your usecase it may not matter. Though it does not apply to all cases.