Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Null coalescing doesn't work with components

Discussion in 'Scripting' started by Antitheory, Jun 6, 2011.

  1. Antitheory

    Antitheory

    Joined:
    Nov 14, 2010
    Posts:
    549
    This doesn't work:

    Code (csharp):
    1.  
    2. Rigidbody rb = t.rigidbody ?? t.gameObject.AddComponent<Rigidbody>();
    3. rb.mass = tMass;
    4.  
    It gives Missing Component Exception when you try to access rb.mass.

    But this works fine.

    Code (csharp):
    1.  
    2. Rigidbody rb = t.rigidbody;
    3. if (!rb) rb = t.gameObject.AddComponent<Rigidbody>();
    4. rb.mass = tMass;
    5.  
    Which seems strange to me. Anyone care to explain why?
     
  2. orb_9

    orb_9

    Joined:
    Feb 10, 2010
    Posts:
    35
    I don't have an explanation why it doesn't work but I wanted to thank you for pointing me to that null-coalescing operator which I didn't know before :D

    I always use the conditional operator in such cases:
    Code (csharp):
    1.  
    2. Rigidbody rb = t.rigidbody ? t.rigidbody : t.gameObject.AddComponent<Rigidbody>();
    3. rb.mass = tMass;
    4.  
     
  3. Ntero

    Ntero

    Joined:
    Apr 29, 2010
    Posts:
    1,436
    Note sure, but it could be how the null coalescing operator works under the hood.

    UnityEngine.Object instances have the Equals and GetHashCode functions overriden and will throw null when in fact they exist(after they are marked for destruction). Depending on how the null coalescing operator works (something I've not seen before, and looks pretty handy), it could be using a different equality comparison, or comparison to null than the special version used for Components. It's possible that there is some under the hood trickery that is tricking that operator. You could try creating the necessary comparison functions for you components to see if it changes anything.
     
  4. Antitheory

    Antitheory

    Joined:
    Nov 14, 2010
    Posts:
    549
    Your first sentence explains it I think. When you say "if (something)" I believe it is just syntatic sugar for if (something==null) when something cannot be cast to boolean and is a nullable type. So if Objects override Equals then they probably will return a null when compared with == even though they are not null according to the ?? operator, which must not use the classes overrides.
     
  5. Antitheory

    Antitheory

    Joined:
    Nov 14, 2010
    Posts:
    549
    That does get the code all in one line, but it's slightly slower than my method because you are calling GetComponent<> twice if the rigidbody already exists. I realize the difference is probably trivial in most cases unless you are accessing a lot of rigidbodies.

    The null-coalesce operator is handy because you can chain together a whole bunch of things..

    a = b ?? c ?? d ?? e ?? "f";
     
    Last edited: Jun 6, 2011
  6. Ntero

    Ntero

    Joined:
    Apr 29, 2010
    Posts:
    1,436
    I dunno, if I had to do a = b ?? c ?? d ?? e ?? "f";
    I think I would feel that I did something wrong in the design phase.

    You can embed multiple ternary operators(even though it is damn near impossible to read):
    Code (csharp):
    1.  
    2. int a = 5;
    3. int b = a < 1 ? a < 2 ? a < 3 ? 4 : 5 : a < 6 ? 7 : 8 : 4;
    4.  
    But I place it in the same situation of I felt I did something wrong in implementation to need that sort of behaviour.

    Missing Component Exception is a Unity Specific exception that does relate to the Equals Override, so it's likely a ReferenceEquals(object, null); sort of comparison that cannot be overridden.
     
  7. Antitheory

    Antitheory

    Joined:
    Nov 14, 2010
    Posts:
    549
    That's probably true. I don't think I've ever used more than two at a time. But it does have it's uses!
     
  8. xtyler

    xtyler

    Joined:
    Dec 8, 2012
    Posts:
    11
    I finally worked around this with a method extension for getting/adding a component. Usage:
    Code (csharp):
    1. MeshRenderer renderer = GetComponent<MeshRenderer>(true);
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public static class GameObjectExtensions
    4. {
    5.     public static T GetComponent<T>(this GameObject gameObject, bool add) where T : Component
    6.     {
    7.         var value = gameObject.GetComponent<T>();
    8.         if (value == null && add)
    9.             value = gameObject.AddComponent<T>();
    10.         return value;
    11.     }
    12.  
    13.     public static T GetComponent<T>(this Component component, bool add) where T : Component
    14.     {
    15.         return component.gameObject.GetComponent<T>(add);
    16.     }
    17. }