Search Unity

Resolved Interface belonging to expired Monobehaviour is "null" not null; sneaking past null check

Discussion in 'Scripting' started by Peeling, May 13, 2022.

  1. Peeling

    Peeling

    Joined:
    Nov 10, 2013
    Posts:
    443
    EDIT: The solution is to perform the following additional check:

    Code (CSharp):
    1.             if (kvp.Value.unboxed is UnityEngine.Object o)
    2.             {
    3.                 if (o == null) continue;
    4.             }


    The following code is crashing with a null reference exception:

    Code (CSharp):
    1.         foreach (KeyValuePair<string, ContextBox> kvp in all)
    2.         {
    3.             if (kvp.Value.unboxed == null) continue;
    4.             CareerData.SaveString("CTX_" + kvp.Key, kvp.Value.unboxed.UID());
    5.         }
    Single-stepping through, the exception arises inside UID() because the monobehaviour whose interface I am holding in the 'unboxed' variable has been destroyed.

    VisualStudio displays the value of unboxed as "null", not null. Presumably, the fact I'm holding an interface reference rather than a monobehaviour reference is bypassing the usual safeguards and sneaking past my null check.

    Not all IContext interfaces belong to monobehaviours. Is there a safe test I can make to fix this, before I throw my hands up and wrap the whole thing in try/catch?
     
    Last edited: May 13, 2022
    FelipeCavaco likes this.
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,695
    Is it simply because you're iterating
    KVP<string,ContextBox>
    when you should be iterating
    KVP<string,IContext>
    ?

    Either way, not sure what your .unboxed property does... is that what returns it as an IContext?

    In other news, boxing / unboxing is often a sign that you need to reconsider your data design to avoid it.
     
  3. Peeling

    Peeling

    Joined:
    Nov 10, 2013
    Posts:
    443
    It's not that kind of boxing :)

    IContext in our game is a performant interface for accessing properties by string name (and more than that; parameters can be passed in the string too to influence the result). A ContextBox is a persistent named slot for an IContext. The upshot is that designers can do things such as reference "{PhoneCaller.FullName}" in our localisation text file and the Context system will toddle off and ask whatever's slotted as "PhoneCaller" for its "FullName". Or visual appearance. Or gender. Or whatever.

    The gotcha here is that if you retain a reference to an expired UnityEngine.Object as an interface, the interface will pass a null test even though a direct reference to the object would fail it.

    I've found a workaround and will add it to the OP.