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

GetComponent return types confusing

Discussion in 'Scripting' started by Vladimir_91, Sep 7, 2021.

  1. Vladimir_91

    Vladimir_91

    Joined:
    Jul 20, 2016
    Posts:
    12
    upload_2021-9-7_10-52-10.png

    Hi guys, can someone explain why getcomponent returns on empty gameobject different object types, for videoplayer its a string(!). I've never seen things like this.
     
  2. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,538
    Unity does not use some kind of special compiler, so the code works the usual C# way. A generic method that returns the generic type argument, does return that type. Your two variables that you have typed implicitly (a and b), will have the type VideoPlayer and Image. So you can not "assign" a string to such a variable. What you're probably seeing is a "FakeNull" object. Note that this behaviour of GetComponent is a pure in-editor behaviour. However even at runtime you can encounter fake null objects. This happens when you destroy a component or gameobject.

    Visual Studio and it's integrated debugger will use the ToString method of non-null object references in order to display them when you hover over them. That was always the main usage of the ToString method. For arbitrary classes that have not overridden the ToString method, it usually returns just the class name. Unity's fake null objects probably return "null" when ToString is called if the object is dead.

    Though this all should be quite irrelevant. When you compare a fake null object against null, the result will be true, (The object is faking it is null). So it's not clear what's your exact issue now? ^^
     
    Joe-Censored and JoNax97 like this.
  3. On-Stand-By

    On-Stand-By

    Joined:
    Sep 30, 2017
    Posts:
    14
    To avoids surprises like, I recommend to set the type from the start instead of using var.
    var is useful when you don't know what type you're going to get, but if you already know what it's going to be just don't use it.

    void Start()
    {
    VideoPlayer videoPlayer = GetComponent<VideoPlayer>();
    Image image = GetComponent<Image>();
    }
     
    Lethn and Joe-Censored like this.
  4. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,198
    No, that's wrong. The return type of GetComponent<VideoPlayer> is VideoPlayer, so there's no difference between using var or not.

    What @Bunny83 is writing is the correct explanation, but boy this is some dark, arbitrary magic. Here's some more detailed examples.

    Code (csharp):
    1.  
    2. // none of these on the GameObject
    3. var a = GetComponent<VideoPlayer>();
    4. var b = GetComponent<Image>();
    5.  
    6. Debug.Log(a); // Prints "null" (note lowercase n)
    7. Debug.Log(b); // Prints "Null" (note uppercase N!)
    8.  
    9. Debug.Log(a == null); // prints true
    10. Debug.Log(b == null); // prints true
    11.  
    12. try {
    13.     Debug.Log(a.GetType().Name); // prints "VideoPlayer"!
    14.  
    15. /*
    16. The next line
    17. throws: MissingComponentException: There is no 'VideoPlayer' attached to the "GameObject" game object, but a script is trying to access it.You probably need to add a VideoPlayer to the game object "GameObject". Or your script needs to check if the component is attached before using it.
    18. */
    19.     Debug.Log(a.clip);
    20. }
    21. catch (Exception e) {
    22.     Debug.LogException(e);
    23. }
    24.  
    25. try {
    26.     Debug.Log(b.GetType().Name); // this throws a NullReferenceException
    27.     Debug.Log(b.sprite);
    28. }
    29. catch (Exception e) {
    30.     Debug.LogException(e);
    31. }
    I don't know why UnityEngine.UI.Image behaves differently than other Unity objects. It's been known for a long time that the bogus nulls were a bad idea, they're kept around for backwards compatibility's sake and because the replacement isn't obvious. Maybe they avoided the issue entirely for the 4.6 UI? Or it's just another thing where the 4.6 UI is incompetent for incompetence's sake.
     
    mopthrow, Olmi, orionsyndrome and 3 others like this.
  5. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,538
    Well, every class can implement their own ToString version. ^^ Though ToString is mapped in UnityEngine.Object to a native implementation. Neither UnityEngine.UI.Image, nor UnityEngine.Video.VideoPlayer has a custom managed implementation, so the different behaviour has to be on the native side. "Null" is the usual C++ notation for a null value. So it may just be a fragment of different people working on different things at the same time ^^. Though the ToString conversion of dead objects most likely has not a high priority. It's just as important as the color of a banana peel inside a wastebin :)
     
    Olmi and orionsyndrome like this.
  6. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,198
    I wasn't talking about that. It's that usually when you do GetComponent<T>, and it's not there, you get the bogus null object for a MissingReferenceException, but it seems like if you do it with Image you get actually null instead.
     
  8. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    I think he meant because Components are UnityEngine.Object and Unity supplants a 'fake null' object, each UnityEngine.Object class has its own ToString() and some of these can return "Null" because classes are likely to have been made by different developers and there is no convention priority.
     
  9. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,538
    Yes, you're right. It seems they have changed the behaviour for MonoBehaviour components to actually return null. The old behaviour may only be there for built-in components now. The Image class of the UnityEngine.UI system is just an ordinary MonoBehaviour derived managed type (Well, it has a longer inheritance chain but is still pure managed Image-->MaskableGraphic-->Graphic-->UIBehaviour-->MonoBehaviour). I just tried it with another MonoBehaviour derived type and it also returns null. I guess they realised that the fake null object behaviour did more harm for MonoBehaviour types than doing good.

    It was never a really useful feature, though for built-in components it may be still justified. However MonoBehaviours are often used in conditional code. So do something only of this or that component is attached. The fake null object always added this unnecessary garbage allocation when testing in the editor. Though somewhere in the 2020 cycle I think they added the TryGetComponent method(s) which specifically do not return fake null objects.

    I just thought about searching through the release notes if I find some information on those potential changes. However I just realised that my mysql database where I hold a copy of all the release notes is "slightly" out of date ^^. The latest entry was Unity 2017.1 ^^. Unity has put out a s*** load of versions since then. I integrated the notes up to 2018 but this just takes too much time now ^^. Counting all release notest from Unity version 1.0 up to 2017.1, they had twice as many releases up to the current version (about 200+). Though they release much more quick fix and patch releases. the 2017.4.x cycle has 40 releases...