to keep things simple I have two classes: ActionTaken and MovementTaken. MovementTaken is a derived class from ActionTaken. I have a Queue full of actions, and each action has a char that determines the type of action. (Every action has a correct type) I want to pass elements of the Queue to a function, that works specifically with a MovementTaken parameter, but since I'm using polymorphism the parameter is of type ActionTaken, but then I cannot use member variables from MovementTaken, but don't exist in ActionTaken. But if I set the function activateMovement's parameter to type MovementTaken, I believe there would be an error, saying you cannot convert a base type to a derived type. Here's the code:
public abstract class ActionTaken : MonoBehaviour
{
public char type;
public Transform minionTakingAction;
}
public class MovementTaken : ActionTaken
{
public int targetTileH;
public int targetTileV;
public MovementTaken(Transform _minionTakingAction, int _targetTileH, int _targetTileV)
{
type = 'M';
minionTakingAction = _minionTakingAction;
targetTileH = _targetTileH;
targetTileV = _targetTileV;
}
}
Queue<ActionTaken> actionTaken;
public void activateMovement(ActionTaken toActivate)
{//some code using toActivate's members targetTileH and targetTileV}
If you know the argument passed to the method is a MovementTaken instance, you can just downcast it:
public void activateMovement(ActionTaken toActivate)
{
MovementTaken casted = toActivate as MovementTaken;
// Do something with casted.targetTileH and/or caster.targetTileV
The advantage of Abstract classes is defining base implementation, or to force derived types into implementation details:
public abstract class ActionTaken : MonoBehaviour
{
public char Type { get; protected set; }
public Transform Target { get; }
// base ctor
protected ActionTaken(Transform target)
{
Type = '\0';
Target = target;
}
// Force implementation onto sub class
public abstract void Activate();
}
public class MovementTaken : ActionTaken
{
public int TileH { get; set; }
public int TileV { get; set; }
public MovementTaken(Transform target, int tileH, int tileV)
: base(target)
{
Type = 'M';
TileH = tileH;
TileV = tileV;
}
public override void Activate()
{
//some code using TileH and TileV
}
}
Therefore your calling code would be:
Queue<ActionTaken> actionTaken;
public void activateMovement(ActionTaken action)
{
action.Activate();
}
I'm also not sure what Type is being used for, but if you still need it, it might be better off as a constant defined in each class that derives from ActionTaken if you have more.
This can make sense if you end up filling your Queue<ActionTaken> with various derived movement types. Otherwise your ActivateMovement method could end up being a long switch statement.
An interface also might be advantageous here:
public interface IActionTaken
{
Transform Target { get; }
void Activate();
}
Which you would then replace your queue: Queue<IActionTaken> Actions
The code for invoking all of the actions in the queue could then be extremely straightforward:
while(Actions.Count > 0)
{
IActionTaken current = Actions.Dequeue();
current.Activate();
}
Related
I have an ability data factory which has a generic function for creating specific types of abilities. The abilities themselves need to have their own unique AbilityData object passed in which contains both unique and common immutable settings of the ability. The factory contains has the list of this data, which it uses to initialize the abilities upon creation.
My problem is that the solution I have is not type safe. The ability essentially has to validate that the data belongs to it and the factory passes it in. I want to know if there's a type safe way of achieving this, since it feels like a work around solution and maybe a code smell.
public class AbilityFactory : MonoBehaviour
{
[SerializeField]
private List<AbilityData> abilityData;
public IReadOnlyList<AbilityData> AbilityData => abilityData;
public int AbilityCount => abilityData.Count;
public Tability CreateAbility<Tability>() where Tability : Ability
{
Tability instance = Activator.CreateInstance<Tability>();
for (int i = 0; i < AbilityData.Count; i++)
{
if(instance.MatchAbilityData(AbilityData[i]))
{
instance.Initialize(AbilityData[i]);
}
}
return instance;
}
}
public abstract class Ability
{
protected int level;
public abstract bool MatchAbilityData(AbilityData abilityData);
public abstract void Initialize<TabilityData>(TabilityData data) where TabilityData : AbilityData;
public void IncreaseLevel()
{
level++;
}
public void Update()
{
OnUpdate();
}
protected virtual void OnUpdate()
{
}
}
public abstract class Ability<T> : Ability where T : AbilityData
{
public T Data { get; private set; }
public sealed override void Initialize<TabilityData>(TabilityData data)
{
this.Data = data as T;
}
public sealed override bool MatchAbilityData(AbilityData abilityData)
{
return abilityData is T;
}
}
I'm trying to design a system in C# for different character types in a video game. I have a simple inheritance tree for my character types with a Humanoid base class that has several derived classes such as Human, Alien, etc. Each of these character types includes a body with several parts. Currently these parts are stored in an instance of a Body class and each part is described by an instance of BodyPart class.
The main complication comes when I want to have derived classes for BodyPart since each character type's body parts will have some additional specific properties. The main reason I want the BodyPart instances to be grouped in a Body class is so that I can use an indexer since I need to able to iterate over them and still be able to use dot notation to access specific parts easily.
The following pseudocode should give you an idea of what I'm trying to achieve and how I have already tried to implement it.
public class BodyPart
{
public float health;
//...
}
public class HumanBodyPart : BodyPart
{
public float exampleHumanStuff;
//.. human specific stuff
}
public class AlienBodyPart : BodyPart
{
public float exampleAlienStuff;
//.. alien specific stuff
}
public class Humanoid
{
public class Body<T> where T : BodyPart
{
public T head;
public T leftArm;
public T rightArm;
//...
public T this[int i]
{
get
{
switch (i)
{
case 0:
return head;
case 1:
return leftArm;
//...
}
}
}
}
public virtual Body<BodyPart> body { get; }
public void Example {
body.head.health = 50;
body[2].health = 55;
}
}
public class Human : Humanoid
{
public Body<HumanBodyPart> humanBody;
public override Body<BodyPart> body { get { return (Body<BodyPart>)humanBody; } }
public void Example
{
body.rightArm.exampleHumanStuff = 5;
}
}
public class Alien : Humanoid
{
public Body<AlienBodyPart> alienBody;
public override Body<BodyPart> body { get { return (Body<BodyPart>)alienBody; } }
public void Example
{
body.leftArm.exampleAlienStuff = 5;
}
}
The dead end with this approach is that the Body class is not contravariant (I think?) so casting a Body<HumanBodyPart> to Body<BodyPart> won't work. But I can't figure out another way to access the Body<HumanBodyPart> class instance in Human from the base Humanoid class so that in the base class it's treated as a Body<BodyPart>. Is there a better way of doing this?
You can make Humanoid generic of BodyPart itself:
public class Humanoid<T> where T : BodyPart
{
public class Body
{
public T head;
//...
public T this[int i]
{
get
{
switch (i)
{
case 0:
return head;
//...
}
return default;
}
}
}
public Body body { get; }
public void Example()
{
body.head.health = 50;
body [2].health = 55;
}
}
public class Human : Humanoid<HumanBodyPart>
{
public void Example()
{
body.rightArm.exampleHumanStuff = 5;
}
}
public class Alien : Humanoid<AlienBodyPart>
{
public void Example()
{
body.leftArm.exampleAlienStuff = 5;
}
}
Also this class hierarchy does not support all possible use-cases (for example collection of different Humanoid's, but you can workaround some with non-generic interfaces (for Humanoid and Body)).
As for variance it is supported only for interfaces and delegates (and arrays, but don't use it) in C#. In your case something like this will work:
public interface IBody<out TInner>
{
public TInner head { get; }
public TInner leftArm { get; }
public TInner this[int i] { get; }
}
IBody<HumanBodyPart> humanBody = ...;
IBody<BodyPart> body = humanBody;
I assume you want to assign a specific BodyPart of the Humanoid class. I would use this:
public class Humanoid <T> where T : BodyPart
{
public class Body<T>
{....
And in the end when you describing a Human you can use:
public class Human : Humanoid <HumanBodyPart>
{.......
I have the following classes:
public interface IService
{
void ApplyChanges<T>(T parameters) where T : ParamBase;
}
public class ServiceBase : IService
{
public virtual void ApplyChanges<T>(T parameters) where T : ParamBase
{ }
}
public abstract class Service : ServiceBase
{
public override void ApplyChanges<T>(T parameters) where T : ParamBase
{
Console.WriteLine(parameters.Param2);
//Apply changes logic here...
}
}
public abstract class ParamBase
{
public string Param1 { get; set; }
}
public class ParamA : ParamBase
{
public string Param2 { get; set; }
}
Here my test main class:
void Main()
{
var service = new Service();
var paramA = new ParamA();
paramA.Param2 = "Test2";
service.ApplyChanges<ParamA>(paramA);
}
What is wrong with that implementation? How can I access parameters.Param2from the overriden ApplyChanges method in my Service class?
The general idea is that I have a ServiceBase and I want to be able for its derived classes to pass different parameter types to the ApplyChanges method.
I'm making a leap here, but it sounds like you intend to have multiple "services", each with an associated parameter type.
Putting a type parameter on the method, as you have done in the example, forces all implementations of that method to be polymorphic. (The technical term for this is higher-rank quantification.)
Instead, you should associate the type parameter with the service itself. This allows a given implementation of the contract to declare which parameter type it's associated with. While you're at it, I wouldn't bother with the base classes or the type bounds.
interface IService<in T>
{
void ApplyChanges(T param);
}
class Param1
{
public int X { get; set; }
}
class Service1 : IService<Param1>
{
public void ApplyChanges(Param1 param)
{
param.X = 123;
}
}
class Param2
{
public int Y { get; set; }
}
class Service2 : IService<Param2>
{
public void ApplyChanges(Param2 param)
{
param.Y = 456;
}
}
You shouldnt impose stronger constraints for method overrides. An overridden method should expand the possible input parameters and reduce the possible outcomes. Otherwise it breaks Liskov Substitution Principle. C# does not allow you to do that.
That said, if you really want it, you could. You won't get compiler warnings in the calling code though. Use that solution if you cannot change the base class.
public class Service<TParam> : Service where TParam : ParamA
{
public override void ApplyChanges<T>(T parameters)
{
Console.WriteLine((parameters as TParam).Param2);
}
}
A better solution would be to add a type parameter to ServiceBase and IService.
public interface IService<TParam>
where TParam : ParamBase
{
void ApplyChanges(TParam parameters);
}
public abstract class ServiceBase<TParam> : IService<TParam>
where TParam : ParamBase
{
public virtual void ApplyChanges(TParam parameters)
{ }
}
public class Service : ServiceBase<ParamA>
{
public override void ApplyChanges(ParamA parameters)
{
Console.WriteLine(parameters.Param2);
}
}
Really, instead of replacing the interface's generic type, it is cleaner to use a "Type Guard". I say cleaner because the interface's method signature stays consistent, and really, what's more important than how your interface is used? (Obviously puppies are more important)
Within the method itself, you can make sure that the type is the one desired as such...
public void Method(ParentRequest req){
if(req is ChildRequest request){
//Do logic here
} else {
throw new Exception($"request is of type {req.GetType().Name} and must be of type ParentRequest");
}
}
I'm trying to setup some classes like:
public abstract class AnimalBase {
public string SpeciesName { get; private set; }
public AnimalBase(string speciesName) {
this.SpeciesName = speciesName;
}
public abstract void CopyFrom(AnimalDefaultClass defaultVals);
}
public class Mammal : AnimalBase {
public bool WalksUpright { get; private set; }
public Mammal(string speciesName) : base(speciesName) {
this.CopyFrom(new MammalDefaultClass(speciesName));
}
public override void CopyFrom(MammalDefaultClass defaultVals) {
this.WalksUpright = defaultVals.WalksUpright;
}
public void Cripple() {
this.WalksUpright = false;
}
}
public class MammalDefaultClass : AnimalDefaultClass {
public bool WalksUpright { get; private set; }
public MammalDefaultClass(string speciesName) {
using (var dataStore = theoreticalFactory.GetDataStore()) {
this.WalksUpright = dataStore[speciesName].WalksUpright;
}
}
}
Obviously that's not quite what I'm trying to accomplish, but the idea is:
Several classes (Mammal, Fish, Insect, etc) which inherit from an abstract base (Animal).
Each child class has a corresponding class it can use (in this case to populate mutable default values) as a parameter for a method which was defined as abstract in the base class.
Each of those corresponding classes (MammalDefaultClass, FishDefaultClass, InsectDefaultClass, etc) inherit from a common base class (AnimalDefaultClass).
Those AnimalDefaultClass derivatives exist because each class of Animal will have different properties, but by definition there will always be a class capable of getting those values for any Animal.
My problem is:
That overridden version of CopyFrom(MammalDefaultClass) isn't being recognized as a valid override of the abstract CopyFrom(AnimalDefaultClass), even though MammalDefaultClass inherits from AnimalDefaultClass
Is it possible to specify a base class as an abstract member's parameter? Is there a simple* workaround? Or is this whole thing just laid out wrong?
-edit: my resolution-
After playing around some with MWB and sza's suggestions, I ended up having each subclass implement the method using the base parameter and then cast the input as appropriate, something like:
public class Mammal : AnimalBase {
...
// implements the abstract method from the base class:
public override void CopyFrom(AnimalDefaultClass defaultVals) {
this.CopyFrom((MammalDefaultClass)defaultVals);
}
public void CopyFrom(MammalDefaultClass defaultVals) {
this.WalksUpright = defaultVals.WalksUpright;
}
}
This solution forces me to always implement a CopyFrom(AnimalDefaultClass) , which was the point of the putting the abstract method in the base class in the first place.
I think you can try Abstract Factory pattern. Basically you want to handle some construction logic during the creating the object, and for each different subtype of the Product, you can do differently.
public abstract class AnimalBase
{
public string SpeciesName { get; private set; }
protected AnimalBase(string speciesName)
{
this.SpeciesName = speciesName;
}
}
public class Mammal : AnimalBase
{
public bool WalksUpright { get; set; }
public Mammal(string speciesName) : base(speciesName)
{
}
public void Cripple()
{
this.WalksUpright = false;
}
}
public interface IAnimalFactory<T> where T : AnimalBase
{
T CreateAnAnimal(string speciesName);
}
public class MammalFactory: IAnimalFactory<Mammal>
{
public Mammal CreateAnAnimal(string speciesName)
{
var mammal = new Mammal(speciesName);
var mammalDefault = new MammalDefaultClass(speciesName);
mammal.WalksUpright = mammalDefault.WalksUpright;
return mammal;
}
}
And when you want to create a sub-typed object, you can do e.g.
var mammalFactory = new MammalFactory();
var bunny = mammalFactory.CreateAnAnimal("Bunny");
So it turns out that even though MammalDefaultClass is a subclass of AnimalDefaultClass, you cannot override a function that takes an AnimalDefaultClass with one that takes a MammalDefaultClass.
Consider this block of code:
public class Dinosaur : AnimalDefaultClass;
Dinosaur defaultDinosaur;
public void makeDinosaur(AnimalDefaultClass adc)
{
adc.CopyFrom(defaultDinosaur);
}
MammalDefaultClass m;
makeDinosaur(m);
In this case MammalDefaultClass is a subclass of AnimalDefaultClass, so m can be passed to makeDinosaur as adc. Furthermore the CopyFrom for an AnimalDefaultClass only needs another AnimalDefault class, so I can pass in a dinosaur. But that class is actually a Mammal, and so needs a MammalDefaultClass, which dinosaur is not.
The work around would be to take the original type signature and throw an error if the argument is the wrong type (similar to how arrays act in Java).
I have the following abstract class:
public abstract class BaseClass{
public object contents { get; set; }
public Action<BaseClass> mutator;
public abstract void Initialise();
}
This will be used by several classes, which will override the Initialize method to assign a value to contents, which will in turn be mutated using the mutator delegate at specific points in time.
I have the following static class, with each method intended to be used as a mutator:
public static class Mutators{
public static void VariantA(A inputObj){
// inputObj.contents = something else
}
public static void VariantB(A inputObj) { } // etc. etc.
}
I then have class A, which implements BaseClass. I am trying to assign Mutators.VariantA to the mutator delegate, but i'm not able to.
public class A : BaseClass{
public A(){
mutator = Mutators.VariantA;
}
public override void Initialise(){
/* set the value of contents property here */
}
}
Specifically I get the following error: A method or delegateMutators.VariantA(A)' parameters do not match delegate System.Action<BaseClass>(BaseClass)' parameters (CS0123)
I understand that Mutators.VariantA(A) requires an object of type A, and the Action was declared to accept an input of type BaseClass, however as class A implements BaseClass I thought I would have been able to do this ?
Coming from dynamically typed languages i'm having a tough time getting to grips with working with types in this way :(
Is there any way I can point to a function with an input of the abstract type in this way ? Do I need to look at some other design pattern ?
Thanks
I understand that Mutators.VariantA(A) requires an object of type A, and the Action was declared to accept an input of type BaseClass, however as class A implements BaseClass I thought I would have been able to do this ?
Absolutely not.
An Action<BaseClass> has to be able to accept any BaseClass object. So for example, if your code were valid, I would be able to write:
Action<BaseClass> mutator = Mutators.VariantA;
mutator.Invoke(new B());
(Where B is another class derived from BaseClass.)
The fact that B derives from BaseClass makes it valid for the invocation - but it's not going to help your VariantA method work nicely.
It's not really clear why you have a mutator here - I strongly suspect you should abstract BaseClass from its mutations. I still don't follow what you're trying to achieve, but this design pattern isn't going to help you get there in a type-safe way.
You could write:
public abstract class BaseClass<T> where T : BaseClass<T> {
public object Contents { get; set; }
public Action<T> Mutator { get; set; }
public abstract void Initialise();
}
... then:
public class A : BaseClass<A> {
public A() {
Mutator = Mutators.VariantA;
}
}
... as then you'd be writing something which can mutate "A" values. But in my experience this sort of generic nesting gets really messy, really quickly.
I've used your current example and changed the Method Signature of one of the classes to the following and it works
public abstract class BaseClass
{
public object contents { get; set; }
public Action<BaseClass> mutator;
public abstract void Initialise();
}
public static class Mutators
{
public static void VariantA(BaseClass baseClass)
{
// inputObj.contents = something else
}
public static void VariantB(A inputObj) { } // etc. etc.
}
public class A : BaseClass
{
public A()
{
mutator = Mutators.VariantA;
}
public override void Initialise()
{
/* set the value of contents property here */
}
}