I don't how to work with MonoBehaviours that implement interfaces. Do you?

I’ve never worked with interfaces before. What I’m doing might be totally terrible. If you know that to be true, please tell me what very different thing I should be doing!

The goal is to have a menu item that serializes a bunch of helpful fields. I had this working via an abstract class that inherited from MonoBehaviour, but I think that an interface is more appropriate.

Here’s the interface:

public interface IResettable {

void Reset();

}

And this is what I’m trying to use (called from a menu item):

foreach (MonoBehaviour monoBehaviour in FindObjectsOfType(typeof(MonoBehaviour)) )
	if (monoBehaviour is IResettable) monoBehaviour.Reset();

That results in this error:

This alternative works. Why?

foreach (MonoBehaviour monoBehaviour in FindObjectsOfType(typeof(MonoBehaviour)) ) {
	var resettableBehaviour = monoBehaviour as IResettable;
	if (resettableBehaviour != null) resettableBehaviour.Reset();
}

Because MonoBehaviour, the class itself, does not implement your interface. Your DERIVED class, MyMonoBehaviour, is the one that actually implements your interface. The only way to call an interface is to cast the object to a class that can “see” the interface. The easiest thing to do, of course, is to cast the class to the interface itself. But you could also cast to MyMonoBehaviour and use .Reset().

But…

?

Even this gets the same error as above.

foreach (MonoBehaviour monoBehaviour in FindObjectsOfType(typeof(MonoBehaviour)) ) {
	monoBehaviour.Reset();

thats because MonoBehaviour is not your class, its a monobehaviour

you have to use your class name there like MyMonoBehaviour for the type of monoBehaviour and search for typeof(MyMonoBehaviour), then it will know of reset.

there is no automagic that magically makes a MonoBehaviour a MyMonoBehaviour, only the opposite direction is true (MyMonoBehaviour is always a MonoBehaviour, but MonoBehaviour is not always a MyMonoBehaviour)

That’s nowhere in my code. My current assumption is that MonoBehaviour is an abstract class, and so you can’t assume that its derived classes will implement its methods. The same error shows up for all the MonoBehaviour methods I’ve tried. I’m testing…

Okay, I see what you’re getting at.

You have to realize that the folks at Unity have decided to use Reflection very heavily in the design of their run-time classes. This causes some discrepancies between terminology.

You see, MonoBehaviour isn’t really a true abstract class, because in a true abstract C# class, you MUST implement all methods. Unity uses some reflection stuff to make psuedo-“overridable” functions such as Start, Update and Reset. MonoBehaviour, at compile time, actually does not have a function definition for “Reset” and thats what the error is saying.

Later on, during run time, that definition MAY be implemented by a derived class. If you want to guarantee that all your MonoBehaviours must implement Reset, then you use the interface approach that you seem to be doing.

MonoBehaviour is declared as a concrete class.

Declaration of MonoBehaviour when disassembled:

    public class MonoBehaviour : Behaviour

Standard way to implement interfaces with MonoBehaviours:

public interface EnemyAI
{
    void Wander();
}

public class Enemy01Behaviour : MonoBehaviour, EnemyAI
{
    public void Wander()
    {
        // wander around aimlessly... derp! derp! derp!
    }

}

public class Enemy02Behaviour : MonoBehaviour, EnemyAI
{
    public void Wander()
    {
        // hunt the player down like the dog that he is!
    }

}

public class EnemyMonitor : MonoBehaviour
{
    void ProcessAI()
    {
        EnemyAI[] enemyIntelligence = GameObject.FindObjectsOfType(typeof(EnemyAI)) as EnemyAI[];
        foreach (EnemyAI ai in enemyIntelligence)
        {
            ai.Wander();
        }

    }


}

I think the issue you are running in to is the fact you are trying to declare a function called Reset in your interface, and MonoBehaviour reserves Reset for its own usage.

Also, in your original code you never cast your object to IResetable before calling reset. You verify that the object is IResetable but then just invoke the method as though it were a MonoBehaviour.

Unity uses reflection to call Reset (it is a reserved function in a MonoBehaviour) on a MonoBehaviour so you will introduce bugs by also having a function called Reset in your interface.

Note that nowhere in the inheritance tree for a MonoBehaviour is Reset explicitly declared. The Reset function is a Unity3D implementation detail that is inferred through reflection.

How can that work? EnemyAI, which is an interface, can’t derive from Object, right?

Right. That’s what I want to do, so having any methods in IResettable would actually be unnecessary, I think, if you could call MonoBehaviour.Reset().

Reset is called one time, does what I want it to do, and then I’ll call it again and again, but Unity won’t without my say-so. The interface just makes it so that Unity knows Reset() will be available. Where could bugs pop up?

The Reset() method in MonoBehaviour is invoked when you right click on a script attached to a GameObject or prefab and click “Reset” or when you first attach your script to a GameObject or prefab in the inspector.

All objects within Mono derive from Object. When you cast any object to a valid interface, it will be assumed to derive from Object as the very root of the object inheritance hierarchy. It cannot ever be anything else.

I think you are confused between what interfaces are for and how they are implemented by the developer.

I still believe that declaring an interface with a Reset() method in it, or any of the other reserved functions that MonoBehaviours have such as Awake, Start, Update, OnGUI, and so on and so forth, is a huge mistake and will lead to bugs further down the road.

The only valid reason to have an interface with one of the MonoBehaviour reserved functions is to ensure that when you need to invoke a method from another class, e.g. the EnemyMonitor class in my example, that the method has actually been declared because you test if the MonoBehaviour “is a” interface, and can then cast the to the interface to invoke the method without throwing a run-time exception without having to resort to reflection to verify that the function exists in the first place. Of course, Unity3D provides for this functionality built-in via the SendMessage method, but the interface makes the method declaration explicit whereas the reflection via SendMessage makes the method declaration implicit.

Let us assume that we wish to invoke the method Update() on a MonoBehaviour from another script, for whatever particular reason we may wish to do this:

The incorrect way:

public class Enemy01Behaviour : MonoBehaviour
{
    public void Reset()
    {
        // reset our resetable stuff
    }

}

public class Enemy02Behaviour : MonoBehaviour
{
    // we don't declare a reset function in here
}

public class EnemyMonitor : MonoBehaviour
{
    void ProcessAI()
    {
        MonoBehaviour[] enemyIntelligence = GameObject.FindObjectsOfType(typeof(MonoBehaviour)) as MonoBehaviour[];
        foreach (MonoBehaviour ai in enemyIntelligence)
        {
            // this won't even compile because we are trying to invoke a method that MonoBehaviour does not declare
            ai.Reset();
        }
    }

}

The interface way:

public interface EnemyAI
{
    void Reset();
}

public class Enemy01Behaviour : MonoBehaviour, EnemyAI
{
    public void Reset()
    {
        // reset our resetable stuff
    }

}

public class Enemy02Behaviour : MonoBehaviour, EnemyAI
{
    // this won't compile because we inherit from EnemyAI interface but do not implement the Reset function
    // we don't declare a reset function in here
}

public class EnemyMonitor : MonoBehaviour
{
    void ProcessAI()
    {
        EnemyAI[] enemyIntelligence = GameObject.FindObjectsOfType(typeof(MonoBehaviour)) as EnemyAI[];
        foreach (EnemyAI ai in enemyIntelligence)
        {
            ai.Reset();
        }
    }

}

The Unity3D way:

public class Enemy01Behaviour : MonoBehaviour
{
    public void Reset()
    {
        // reset our resetable stuff
        // this function will be invoked both by our code and also by the Unity3D inspector/editor
    }

}

public class Enemy02Behaviour : MonoBehaviour
{
    // we don't declare a reset function in here
}

public class EnemyMonitor : MonoBehaviour
{
    void ProcessAI()
    {
        MonoBehaviour[] enemyIntelligence = GameObject.FindObjectsOfType(typeof(MonoBehaviour)) as MonoBehaviour[];
        foreach (MonoBehaviour ai in enemyIntelligence)
        {
            // this will throw a run-time exception because one our MonoBehaviours does not have a Reset() method
            // we can fix it by appending using the DontRequireReceiver enum
            ai.SendMessage("Reset");
        }
    }

}

FindObjectsOfType doesn’t search for System.Object. It searches for UnityEngine.Object. (Also, it’s a method of Object, not GameObject. But you don’t need GameObject in your code anyway because MonoBehaviour derives from Object.) Your code yields the error

I got the same thing earlier. If this weren’t the case, then it would be nice and clean:

foreach (IResettable resettableObject in FindObjectsOfType(typeof(IResettable)) ) resettableObject.Reset();

I don’t think I have worked with this stuff enough to know what to be confused about. :wink:

I don’t like that because it doesn’t yield the compile-time error that the interface method would. I also don’t like the fact that the error at the top of this post isn’t compile-time, either. Know why?

Also, why do you call it Unity3D? You seem to know stuff. :stuck_out_tongue: Do you think it’s helpful to differentiate it from other Unitys?

Thanks for the information, guys. I don’t understand this Unity Reflection-based magic yet, but at least I know the reason for my trouble.

I usually call it Unity3D to distinguish itself from Cisco’s Unity which has nothing to do with games. :slight_smile:

I agree, catch it at compile time, hence my preference for interfaces when it makes sense.

I made a mistake earlier, apologies. I incorrectly recalled/thought that FindObjectsOfType would retrieve all objects, not just UnityEngine.Object derived objects.

In case anyone else comes across this page an appropriate solution for this problem is as follows

foreach(MonoBehaviour monoBehaviour in FindObjectsOfType<MonoBehaviour>()) {
    if(monoBehaviour is IResettable resettable) {
        resettable.Reset();
    }
}