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. Dismiss Notice

Question Access Interface method : "TryGetComponent" or "MonoBehaviour is IInterfacable" ?

Discussion in 'Scripting' started by NotMyUsernameAgain, Sep 9, 2023.

  1. NotMyUsernameAgain

    NotMyUsernameAgain

    Joined:
    Sep 28, 2017
    Posts:
    126
    Hi everyone,
    starting to work with interfaces, I wondering about what I should use to access the interface method?

    In the unity learning section, it uses an example where it gets all MonoBehaviours and then access it.
    Though, in the example it gets all MonoBehaviours in the scene, whereas I do a raycast.

    But I also stumbled across another way, which is via the "TryGetComponent".

    So my two codes would look like this:
    MonoBehaviuor:
    Code (CSharp):
    1. if (hit.collider.GetComponent<MonoBehaviour>()) {
    2.                     MonoBehaviour M = hit.collider.GetComponent<MonoBehaviour>();
    3.                    
    4.                     if (M is IArrowable && M is not IInteractable) {
    5.                         //FALSE "collided" (e.g. treasure boxes will not be arrowed)
    6.                         IArrowable g = (IArrowable)hit.collider.GetComponent<MonoBehaviour>();
    7.                         g.Method();
    8.                     }
    9.                 }
    TryGetComponent:
    Code (CSharp):
    1. if(hit.collider.gameObject.TryGetComponent(out IArrowable o) &&
    2.                     !hit.collider.gameObject.TryGetComponent(out IInteractable i)) {
    3.  
    4.                     o.Method;
    5.  
    6.                 }
    (The two codes are just as an example)

    I believe "GetComponent<MonoBehaviour>()" and the casting to (IArrowable) may be unneccessary costly.
    Also, what is the "is" and "is not" which I've seen the first time? What is the difference to == and !=.

    In the other method, I'm wondering if it matters that I have an (out IInteractable i) but not using it?
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,769
    As I went over in a thread about literally the same thing today you can use interfaces with
    GetComponent<T>
    .

    TryGetComponent(out T)
    is more or less the same thing that just does the null check inside the method, and returns true or false if there was a hit or not. It makes for cleaner code and has some performance benefits as well.
     
    Ryiah, SisusCo, Bunny83 and 1 other person like this.
  3. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    3,899
    If you are concerned about cost, you should meticulously avoid writing code like this:
    Code (CSharp):
    1. if(hit.collider.gameObject.TryGetComponent(out IArrowable o) &&
    2.                     !hit.collider.gameObject.TryGetComponent(out IInteractable i))
    This violates the DRY principle, which can become detrimental to performance but it's also an effective way to keep your code maintainable and readable, which is far more important actually (the performance benefit is merely a positive side effect):
    Code (CSharp):
    1. var target = hit.collider.gameObject;
    2. if(target.TryGetComponent(out IArrowable o) &&
    3.                     target.TryGetComponent(out IInteractable i) == false)
    Also I can't help it but for years now I've forced myself to avoid the ! negation operator (except in != comparison or when flipping bools) for the simple reason that it is so easy to skip over while reading the code. Especially if it's followed by certain characters or comes in a bunch of conditions (bogus example):
    Code (CSharp):
    1. if ((levelPlayed[i] || !IsConnected) && (IsConnected || !levelPlayed[i-1]))
    More readable:
    Code (CSharp):
    1. if ((levelPlayed[i] || IsConnected == false) && (IsConnected || levelPlayed[i-1] == false))
     
    Ryiah and NotMyUsernameAgain like this.
  4. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,104
    The
    is
    operator can be used to check if an object is castable to a particular type.
    Code (CSharp):
    1. object something = "Hello!";
    2. if(something is int)
    3. {
    4.     Debug.Log("The 'something' variable contains an integer value.");
    5. }
    6.  
    7. if(something is float)
    8. {
    9.     Debug.Log("The 'something' variable contains a floating-point value.");
    10. }
    11.  
    12. if(something is string)
    13. {
    14.     Debug.Log("The 'something' variable contains a string object.");
    15. }
    You can also use the
    is
    operator to both check if an object is castable to a particular type, and if it is, then cast it and assign it into a new variable.
    Code (CSharp):
    1. if(something is string text)
    2. {
    3.     Debug.Log($"The 'something' variable contains the string \"{text}\".");
    4. }
    You can also use the
    is
    operator to test if something is null or not.
    Code (CSharp):
    1. object maybeSomething = null;
    2.  
    3. if(maybeSomething is null)
    4. {
    5.     Debug.Log("The 'maybeSomething' variable does not contain any object.");
    6. }
    7.  
    8. if(maybeSomething is not null)
    9. {
    10.     Debug.Log("The 'maybeSomething' variable does contain some object.");
    11. }
    The
    ==
    operator on the other hand can be used to test if an object is equal to another object (not a type, but an instance of a type).

    One special property of the
    ==
    operator is that any class can override it with a custom implementation.

    For example the String class overrides it, so that it returns true for two strings if their character arrays have identical contents, even if they actually refer to different objects in memory.
    Code (CSharp):
    1. string a = new String("Hello!");
    2. string b = new String("Hello!");
    3. if(a == b)
    4. {
    5.     Debug.Log("The 'a' and 'b' variables both contain the same string value.");
    6. }
    However, the overloaded
    ==
    operator will not get used if the instance is contained in a variable of a base type or an interface type.
    Code (CSharp):
    1. object a = new String("Hello!");
    2. object b = new String("Hello!");
    3. if(a == b) // uses the == operator in the object class, not the overloaded == operator in the string class
    4. {
    5.     Debug.Log("The 'a' and 'b' variables both contain the same string value."); // not executed
    6. }
    This is important to know when dealing with UnityEngine.Object-derived classes (components, game objects, scriptable objects etc.). The
    ==
    operator in the Object class has been overloaded to return true when a destroyed Object is compared against a null value. But if a destroyed Object is stored inside an interface type variable, or an object type variable, then comparing it against null returns false instead.
    Code (CSharp):
    1. Player player = GetComponent<Player>();
    2. Destroy(player);
    3. if(player == null)
    4. {
    5.     Debug.Log("player == null"); // this gets printed
    6. }
    7.  
    8. if(player is IPlayer iplayer)
    9. {
    10.     if(iplayer != null) // does not use the overloaded == operator in the UnityEngine.Object class
    11.     {
    12.         Debug.Log("iplayer != null."); // this also gets printed
    13.     }
    14. }
    And since the
    is
    operator can not be overridden, it has not been possible for Unity to make it return true for destroyed Objects.
    Code (CSharp):
    1. Player player = GetComponent<Player>();
    2. Destroy(player);
    3. if(player is not null)
    4. {
    5.     Debug.Log("player is not null"); // this also gets printed
    6. }
     
    Last edited: Sep 9, 2023
    Ryiah and NotMyUsernameAgain like this.
  5. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,104
    It doesn't matter that much; since GameObject does not have any HasComponent method, using TryGetComponent like this makes sense.

    However, in C# you can use an underscore (
    _
    ) to discard a variable, which explicitly tells both the compiler and anybody reading your code, that it is not used. It is good practice to use this to make your intent clear.
    Code (CSharp):
    1. if(gameObject.TryGetComponent<IArrowable>(out _)
    2.     && !gameObject.TryGetComponent<IInteractable>(out _))
     
    Ryiah and NotMyUsernameAgain like this.