Lets say I have monobehavior A and B. Both A and B have implemented code from the start function, and B inherits from class A. I want B to run A’s code, then run some additional code, so I make A’s Start() protected, add the new keyword to B’s Start(), and call base.Start(). I can not see anything wrong with the practice, but I have never seen anyone use it online or used in any components I download off the asset store which makes me think that it breaks some coding principle I am not smart enough to remember at the current moment.
If someone can just give me a quick “sanity check” on the validity of the aforementioned practice that would be great!
Nothing innately wrong with it except that as you note,
… so…
a) most Unity developers don’t expect it
b) there is no warning about failing to call the base
c) the errors can be VERY subtle (or obvious)
I try not to.
1 Like
I mean when it comes to Unity’s magic methods, they are usually either private
or protected
, and also never intended to be called by the user.
I don’t see the point in hiding a base member’s Start (or any magic method) with the new
keyword as you generally never intend to call them manually anyway and never have them publicly accessible.
It’d make more sense to make it virtual
so the intent is more clear. Either to people using your code, or to yourself in the future.
Though I’m not a fan of this either because it’s never clear when you need to call the base
implementation. So I generally do something like this:
private void Start()
{
// stuff
OnStart();
}
protected virtual void OnStart() { }
2 Likes
Well, there’s nothing wrong with it based on how Unity works. Usually when using inheritance you should / have to make sure that the base class is virtual and the derived class overrides the base implementation. However that is not necessary for Unity since Unity calls the actual implementation in the derived class anyways. Unity will never treat your “B” type as an “A” type. Unity actually doesn’t care about the type of your class since it just calls the magic methods based on the signature.
Not having virtual methods could actually be slightly faster, but in general it’s less clean. Whenever I make a Unity message protected to be overridden in a derived class I go the usual OOP route and make it virtual in the base class. You can use seperate virtual methods like suggested by Spiney. However when you use inheritance with MonoBehaviours there’s always the risk that the derived class also implements one of the Unity messages / callbacks which would prevent the base implementation from being called. So whenever you plan to use inheritance, make sure the Unity messages are protected. That way the compiler would at least complain when you try to re-implement them. Also when you override / reimplement them you know that you do so and that you may want / need to call the base implementation. When Start is a private method, the derived class can also implement its own Start method and the one in the base method would never be called.
2 Likes
The question at hand has already been answered but I’m always vary of the overuse of inheritance, so here’s a few thoughts on that.
Rule of thumb: do not inherit from a class that inherits from MonoBehaviour unless there is no other way to do so respectively where it would be highly inconvenient not to use a common base class.
For example, Unity created the NetworkBehaviour deriving from MonoBehaviour to add more callback methods to it. Same with NetworkTransform and NetworkRigidbody and a couple others. While there could have been done other ways to accomplish these callbacks, it’s just closer to what Unity developers expect from a Unity framework.
Personally, I made a UI Toolkit base class that combines some boilerplate code to set up any GUI. This makes sense because it wraps API calls towards Unity. In another case I made a generic MonoBehaviour to allow it to use a T data of different types without having to change the code.
But most commonly misused inheritance chains are about actual gameplay code, such as an Enemy MonoBehaviour that has subclasses like Knight and Alien or whatever. We know for a fact that this kind of hierarchy is an antipattern that should be avoided.
Besides that, it’s best practice to call the base implementation of any overridden method. The problem is often: when? Typically (best practice) it should be the first line but there’s no guarantee the caller will adhere to that or even whether it will call base. The problem is so intricate that you’ll find, from time to time, that a bug appears exactly because you forgot to call base or called it at a later point in the method.
Hence inheritance should be applied with great care and consideration, definitely not the go-to tool first thing you need code re-use or some other reasons that may make you want to subclass. Always consider alternatives, such as a common but separate class used by both classes as a field ie always favor composition over inheritance.
1 Like