- Home /
Destroyed object not comparing to null
I read that once a GameObject is destroyed, comparing it to null will return true. But I have this line of code which yields a MissingReferenceException
saying the object has been destroyed but I'm still trying to access it:
if (shootTarget != null) {
// something
} else {
distance = shootTarget.GetDistance(this.transform.position);
}
Any ideas why? Any other checks I can perform?
Update:
Ok I found the reason, but not a solution. shootTarget
is not a MonoBehaviour per se, it's an interface I created, but the MonoBehaviour implements the interface and the actual value of shootTarget is a MonoBehaviour that's been destroyed, so comparing it to null doesn't yield true...
Any ideas how to work around this?
Do you have == in your code, or is this some mistake in pasted text? It should be !=.
EDIT: if this is your actual code, then probably this is a reason of your error, though I would expect NullReferenceException in this case...
Yeah sorry, the code was in the else section and I forgot to change to != when I simplified it...
@Arkanex You should post this as an answer. That look like a good answer. I would Up Vote that.
That would complicate things a bit too much, since I'm not destroying it from this script necessarily...
@karljj - after tbkn comment, I think you should convert to answer :) +1
EDIT: I meant comment under my answer
I edited the code snippet the fix the typo pointed out by @ArkaneX.
Answer by ArkaneX · Nov 29, 2013 at 03:52 PM
Inspired by @tbkn own answer, I did a bit of research. MonoBehaviour == operator is not overriden - it's Object == operator. But I see that Equals
method is overriden as well, so you might try
object.Equals(shootTarget, null) // C#
System.Object.Equals(shootTarget, null) // JS
If code from your answer works, this should work as well.
EDIT: after a bunch of additional test it looks like the correct solution is the one posted by tbkn in a comment below:
shootTarget == null || shootTarget.Equals(null)
The first condition prevents situation when the shootTarget
really becomes null
, while second one correctly uses Equals
overriden in UnityEngine.Object
.
It doesn't... but something very similar does:
if (shootTarget == null || shootTarget.Equals(null))
Not sure why if they overload Object.Equals
...
This is the best solution because it doesn't require to force cast... But please change your answer based on my previous comment so it's accurate.
Thanks for the help!
Thanks for this answer. I had the same issue as the OP (referencing a $$anonymous$$onoBehaviour as an interface) and indeed == null returns false but .Equals(null) returns true.
Answer by Tomer-Barkan · Nov 29, 2013 at 03:37 PM
Ok, figured out a possible solution, though not the most elegant. Will keep this question open a bit more to see if anyone knows a more elegant solution.
Apparently a MonoBehaviour's operator == is overridden to return true when comparing a destroyed object to null. Apparently, in C#, when you override an operator of a class, it will not match the overridden operator when you compare an instance of that class that is assigned to an interface variable. This is to do with the fact that operator resolution is done in compile time, and not runtime like virtual methods.
MonoBehaviour myMono;
MonoBehaviourInterface myMonoInterface = myMono;
// this will use the overloaded operator== of MonoBehaviour
if (myMono == null) {}
// this will NOT use the overloaded operator== of MonoBehaviour
if (myMonoInterface == null) {}
Thread in which this is discussed: http://stackoverflow.com/questions/728434/operator-overloading-with-interface-based-programming-in-c-sharp
So my solution would be to cast shootTarget
to MonoBehaviour when doing the comparison to null. Although I don't know the exact class of shootTarget
, I do know it is a subclass of MonoBehaviour, so this works.
if (((MonoBehaviour)shootTarget) == null) {
// something
} else {
distance = shootTarget.GetDistance(this.transform.position);
}
O$$anonymous$$G, that a really specific problem. You should probably rename the question to reflect the implication of the interface.
I didn't know that when I asked the question. I expect others that encounter this problem not to know that either and use the same terms that I used, so better not to change the name...
Answer by yoyo · Sep 30, 2014 at 09:38 PM
Since the interface doesn't know about the original MonoBehaviour's operators, you won't get the over-ridden behaviour of operator == (from UnityEngine.Object), which is where destroyed objects are "equal" to null.
The following test will work for interface implementations that may or may not be MonoBehaviours.
if ((shootTarget == null) || ((shootTarget as UnityEngine.Object) == null)) {
// something
} else {
// something else
}
The first check handles the general case of the object actually being null. The second check handles the case of a destroyed Unity object.
+ 1
for: "which is where destroyed objects are "equal" to null."
Answer by karljj1 · Nov 29, 2013 at 03:22 PM
I have had this issue in the past. Calling destroy on a monobehaviour will not destroy it instantly however it will mark it as destroyed. When you destroy the object also set it to null to be extra safe.
E.G
Destroy( shootTarget ); shootTarget = null;
Edit:
I assume that shootTarget is some property of an other script in which case you are fine to set it to null from an external script. Its still the same variable.
By setting shootTarget = null, you're just setting it's reference to null, not the gameobject. And if you call Destroy on that gameObject, I think it's a good thing to not try to do anything with it after. So I think it's a good thing to do.
Assu$$anonymous$$g karljj method will help, then why not add this additional line in any script you call Destroy? If you use C# (or Boo) you can even write an extension method DestroyAndSetToNull and use it ins$$anonymous$$d.
Think of it this way:
5 objects, Obj1 - Obj5, all have reference to Obj6, they are all shooting at it. At some point, say Obj1 shoots Obj6 and it is killed, so Obj1 calls Destroy() of Obj6. Say I null the reference of Obj1 so that it no longer references Obj6. But Obj2 - Obj5 are still referencing it, because they are not the ones that destroyed it. So I have to iterate all objects, check whether they are referencing Obj6, and set to null... This is too much of a hassle, and is exactly why Unity came up with comparing an object to null will return true when it has been destroyed, that way you don't have to clear all the references.
Right - in the case you described this is not appropriate.
Answer by amirebrahimi · Nov 24, 2016 at 04:28 AM
The fixes above didn't work for me, but it's possible that some of these stopped working during the 5.x era. Here's a workaround that I found:
Equals(obj, "null")
Your answer
Follow this Question
Related Questions
using Contains(gameObject) to find and destroy a gameObject from a list 2 Answers
Why does my bullet prefab get destroyed? 1 Answer
Why is my MonoBehaviour never being deleted? 0 Answers
How do I make Destroy(gameObject) work in my script? 1 Answer
(2d) How can I destroy a gameObject when I pass over it? 1 Answer