Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

Unity's custom == operator

Discussion in 'Scripting' started by mdrotar, Apr 4, 2015.

  1. mdrotar

    mdrotar

    Joined:
    Aug 26, 2013
    Posts:
    377
    This blog post was posted almost a year ago which made some good arguments for getting rid of the custom == operator for unity objects. Unity 5's release would have been a great opportunity to make that change since there were other scripting changes needed anyway. Unfortunately, it seems to still be in Unity 5.

    So what's the plan for the == operator now? Will it be removed in a future 5 release or is it sticking around until Unity 6?
     
  2. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    On that page:

    I don't think there ever really was a sufficiently compelling reason to cause so much potential script changes, especially since they wouldn't be able to auto-upgrade these, unlike the changes that actually happened. It definitely will not be removed in Unity 5 since they only make breaking changes with major versions, and I would guess likely not ever.

    --Eric
     
  3. mdrotar

    mdrotar

    Joined:
    Aug 26, 2013
    Posts:
    377
    Ah, that's disappointing. Auto-upgrade would require some proper code inspection but I don't think it would be too difficult. Microsoft has a code analysis API for that sort of thing.
     
  4. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Yeah, I like the custom implementation. It's fine once you get used to it. Either way, not a huge deal.

    I would have liked removal of implicit casting from Vector3 to Vector2. Again, not a huge deal.
     
    shkar-noori likes this.
  5. mdrotar

    mdrotar

    Joined:
    Aug 26, 2013
    Posts:
    377
    I've seen someone get fired for overloading operators like that. Ok, maybe that's a stretch, but I'm pretty sure it contributed to them getting let go before the end of the probation period. Maybe it's not a "huge deal" but it's not good coding practice.
    https://msdn.microsoft.com/en-us/library/ms229032(v=vs.110).aspx

    It causes confusion which can result in bugs.
    I've seen plenty of "why doesn't this code work" questions with the answer being "Because Unity overloads ==".
    And if/when C# 6 becomes available, the .? operator will cause even more confusion.

    I understand that it would inconvenience the people are used to it, but I think it's a bad idea to continue doing something the wrong way just because people are used to it. Either way, it looks like the overloading will continue until at least Unity 6 so there doesn't seem to be much point in arguing about it right now.
     
  6. Spellbook

    Spellbook

    Joined:
    May 21, 2015
    Posts:
    30
    I have to imagine considerations like these change with time.

    The Unity guys/gals are probably starting to (or soon will be) discuss Unity 6... They need to at least have a thorough debate on removing the operative overload. This is coming from a team with hundreds-of-thousands of lines of code that will be affected.

    I've been a .NET developer for over 15 years now, and I cringe at least once a day because of this. It is heinously bad, and casts a long, dark shadow over everything we do in Unity. It has cost us incredible amounts of money in bug hunts and wasted time, and is confusing even for seasoned developers. It causes problems in Asset Store code regularly, and is the source of innumerable forum and Stack questions from new developers.

    Whatever fringe benefits exist are absolutely not worth it. Please break our code in Unity 6 and remove it!
     
    Huacanacha and mdrotar like this.
  7. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,341
    Code (csharp):
    1. void CheckIfNull(ISomeInterface obj) {
    2.     bool isNull = obj == null || (obj is UnityEngine.Object && ((obj as UnityEngine.Object) == null))
    3.     Debug.Log("It is null: " + isNull);
    4. }
    :D
     
    Spellbook likes this.
  8. Krzsiek-Siewiorek-Tate

    Krzsiek-Siewiorek-Tate

    Joined:
    Nov 17, 2015
    Posts:
    15
    This check function doesn't really help since it uses the == operator. That's the purpose for using it then?
     
  9. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,341
    It helps. == is a method, and C# uses static dispatch:

    Code (csharp):
    1. IEnumerator Demo() {
    2.     GameObject go = new GameObject("Test Object");
    3.     Destroy(go);
    4.     yield return null;
    5.  
    6.     object objRef = go;
    7.     Debug.Log(objRef == null); //prints false
    8.     Debug.Log(go == null); //prints true
    9.  
    10. }
     
  10. Krzsiek-Siewiorek-Tate

    Krzsiek-Siewiorek-Tate

    Joined:
    Nov 17, 2015
    Posts:
    15
    Ok. Now I get it. But it only solves one problem. But the other one is still there...
     
  11. Teravisor

    Teravisor

    Joined:
    Dec 29, 2014
    Posts:
    654
    This isn't same as
    As you will cast obj to UnityEngine.Object then use UnityEngine.Object ==. Maybe you meant
    Code (CSharp):
    1. (((object)(obj as UnityEngine.Object)) == null)
     
  12. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,341
    The original post was a copy from our project. It's essentially for "check if this reference will throw exceptions if I call methods on it". So it checks "is this object null in the C# semantics of null, or is this object null in the Unity-semantics of null?".

    Say you have some interface IDamageReceiver:

    Code (csharp):
    1. interface IDamageReceiver {
    2.     void ReceiveDamage(int damage);
    3. }
    And some enemy that's a MonoBehaviour, and is knocked back when it takes damage:

    Code (csharp):
    1. public class Enemy : MonoBehaviour, IDamageReceiver {
    2.  
    3.     public void ReceiveDamage(int damage) {
    4.         health -= damage;
    5.         transform.Translate(-transform.forward);
    6.  
    7.         if(health < 0) {
    8.             Die();
    9.         }
    10.     }
    11. }
    Then you have some script that does delayed damage to IDamageReceivers:

    Code (csharp):
    1. public class SomeScript : MonoBehaviour {
    2.  
    3.     public void DamageDelayed(IDamageReceiver dr, float delay) {
    4.         StartCoroutine(DamageAfter(dr, delay));
    5.     }
    6.  
    7.     private IEnumerator DamageAfter(IDamageReceiver dr, float delay) {
    8.         yield return new WaitForSeconds(delay);
    9.         if(dr != null)
    10.             dr.ReceiveDamage(damage);
    11.     }
    12. }
    if the dr that gets sent in is an Enemy, and it has been destroyed at any point, the != null check will pass, and the transform.Translate line will cast a MissingReferenceException.

    If I use this code instead:

    Code (csharp):
    1. private IEnumerator DamageAfter(IDamageReceiver dr, float delay) {
    2.         yield return new WaitForSeconds(delay);
    3.         if(!CheckIsNull(dr))
    4.             dr.ReceiveDamage(damage);
    5.     }
    6.  
    7. bool CheckIsNull(object obj) {
    8.     bool isNull = obj == null || (obj is UnityEngine.Object && ((obj as UnityEngine.Object) == null));
    9.     return isNull;
    10. }
    The if-check will not pass, and we won't get any exceptions. Yay!
     
    KelsoMRK likes this.