Hello!
I’ll try to do my best to explain myself, but English is not my first language.
In summary: I want to override a variable type in a child class or something similar so, my child classes have always the “latest” version of each class.
Right now, I have 2 classes > Animal and AnimalAnimator and both classes has their child class Dog and DogAnimator.
Animal class looks like this:
public class Animal : MonoBehaviour
{
public AnimalAnimator Animator { get; private set; }
private virtual void Awake()
{
Animator = GetComponent<AnimalAnimator>();
[code...]
}
}
I want to do something like this with Dog:
public class Dog : Animal
{
public new DogAnimator Animator { get; private set; }
private override void Awake()
{
base.Awake();
Animator = GetComponent<AnimalAnimator>();
}
}
I want Dog to extend Animal, and override the AnimalAnimator component with DogAnimator (that extend DogAnimator. But Unity is giving me this error:
The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(Dog) <Animator>k__BackingField
Right. Overriding of variables isn’t even a thing. You can override properties (which would be the case here). However when overriding a method / property you can not change the type or signature.
Implementing a new property is probably the best approach. However I wouldn’t create a second backing field / auto-property. Even though it’s a slight performance penalty, I would recommend to create a property like this:
public DogAnimator dogAnimator
{
get => (DogAnimator)Animator;
private set => Animator = value;
}
In addition you “may” want to declare the Animator property as virtual, so it can actually be overwritten, and also override the Animator property to prevent the assignment of illegal AnimalAnimators and only allow DogAnimator(s).
public override AnimalAnimator Animator
{
get => base.Animator;
private set
{
if (value is DogAnimator dogA)
base.Animator = dogA;
else
throw new System.Exception("Can't assign non DogAnimator");
}
}
The point of having a base class is to have a standard interface so all derived classes can be treated like it’s just a base class (which it actually is). Don’t just create class hierarchies because you can. You really should think about if you really need polymorphism. In many cases using interfaces is more flexible and doesn’t create the usual straight-jackets that class inheritance brings to the table.
public abstract class Animal<TAnimator> : MonoBehaviour where TAnimator : AnimalAnimator
{
[field: SerializeField]
public TAnimator Animator { get; private set; }
}
public class Dog : Animal<DogAnimator> { }
Also I believe your original attempt should work perfectly fine if you just remove [field: SerializeField] from the Animator property in all the derived types.
public class Animal : MonoBehaviour
{
[field: SerializeField]
public AnimalAnimator Animator { get; private set; }
}
public class Dog : Animal
{
//[field: SerializeField] <- remove this
public new DogAnimator Animator { get; private set; }
}
It’s only Unity’s serialization system that is causing the warning to occur.
Note that generics essentially has the opposite goal than polymorphism. Your “Dog” and a potential “Cat” instance would be completely incompatible with each other. Generics actually diverge classes into distinct separate classes which has the same functionality but with different types.
Polymorphism is about abstracting a concept and have different implementation which can be interfaced in a general way. That’s why the method definitions are the same. So inheritance means you inherit all the data and methods from your parent but you can change / override the method implementation.
Generics is about providing a certain algorithm that works the same on different types. So here you inherit the functionality but work on different data. That’s why you can’t treat the same generic class with different generic arguments in a generic way. The best example is the List<T> class. The functionality the class provides is set in stone and works the same regardless of the type used. However a List<int> and a List<string> are completely separate classes and can never be treated in some generic way.
So like I said in my last post, be careful to not step into a dead end as inheritance and generics put you into a straight-jacket.
Good point @Bunny83 ! To create methods that work with all different types of Animal types, those methods would also need to be generic. So if those public properties are accessed a lot from the outside, it could become a big nuisance.
public static class AnimalUtils
{
public static void Animate<TAnimator>(Animal<TAnimator> animal, AnimalAnimation animation) where TAnimator : AnimalAnimator
=> animal.Animator.Play(animation);
}
To avoid this, a non-generic base class, or an interface, could be added:
public interface IAnimal
{
AnimalAnimator Animator { get; }
}
public abstract class Animal<TAnimator> : MonoBehaviour, IAnimal where TAnimator : AnimalAnimator
{
[field: SerializeField]
public TAnimator Animator { get; private set; }
AnimalAnimator IAnimal.Animator => Animator;
}
public class Dog : Animal<DogAnimator> { }
This would simplify accessing the AnimalAnimator from the outside:
public static class AnimalUtils
{
public static void Animate(IAnimal animal, AnimalAnimation animation)
=> animal.Animator.Play(animation);
}
Generics is also a form of polymorphism (parametric polymorphism), like subtyping is, but it’s an entirely different approach, so the two can’t be mixed and matched freely.