Creating an object of an inherited class in c# - c#

This should be a pretty straight-forward question. I only ask for a simple easy to understand answer. No, I don't want a textbook definition or a link to documentation, please, if possible answer this as simply as possible.
Consider the following:
class Monster
{
public int Hp { get; protected set; }
public string Name { get; protected set; }
public virtual void Attack()
{
Console.WriteLine("Monster attacking!");
}
}
class Skeleton : Monster
{
public Skeleton()
{
Hp = 20;
Name = "Skeleton";
}
public override void Attack()
{
Console.WriteLine("Skeleton attacking!");
}
}
Now imagine I create a new Skeleton object with the type Monster as so.
Monster skeleton = new Skeleton();
I would like to know the difference between creating a Skeleton object with a Monster Type vs creating a Skeleton Object with a Skeleton type.
Skeleton skeleton = new Skeleton();
I don't understand if there's a difference between the two or really how this works. Any and all help appreciated! Thank you!

The benefits to creating a Skeleton object with a Monster type becomes more apparent when you have multiple monsters that you want to hold in a single collection.
For example, you might have a list defined as follows:
List<Monster> EncounterMonsters = new List<Monster>();
Declaring your Skeleton object as Monster allows you to add it to this list, along with any other Monster classes you create.
So, you might have another monster class:
class Ogre : Monster
{
public Ogre()
{
Hp = 50;
Name = "Ogre";
}
public override void Attack()
{
Console.WriteLine("Ogre attacking!");
}
}
You could then do the following:
Monster skeleton = new Skeleton();
Monster ogre = new Ogre();
EncounterMonsters.Add(skeleton);
EncounterMonsters.Add(ogre);
This would then allow you to loop through the EncounterMonsters collection and attack with each using the overridden Attack method for each.

To Expand on the accepted answer, the difference is that if you instantiate your object using the base class Monster, only the properties and methods exposed by the Monster class are available.
Consider this:
public class Monster
{
public Monster(int hp, string name)
{
Hp = hp;
Name = name;
}
public int Hp { get; protected set; }
public string Name { get; protected set; }
}
public class Skeleton : Monster
{
public string Loot { get; set; } // <- Note added property.
public Skeleton(int hp, string name) : base(hp, name)
{
Loot = "Sword";
}
}
public class Vampire : Monster
{
//- some vampire specific properties
public Vampire(int hp, string name) : base(hp, name)
{
// ...
}
}
Now, if you instantiate your skeleton as a Monster.
Monster skeleton = new Skeleton(100, "skully");
skeleton.Loot(); //- Will throw a compile time error.
If you instantiate it as a Skeleton;
Skeleton skeleton = new Skeleton(100, "skully");
skeleton.Loot(); // Will return "Sword";
This is useful when you, for example, have a method or service that will act on common properties of your monsters, say you have a method that logs the stats of a monster.
public string LogMonsterStats(Monster monster)
{
return $"{monster.Name} has {monster.Hp} health points";
}
///....
Skeleton skeleton = new Skeleton(100, "Bob");
LogMonsterStats(skeleton); // returns "Bob has 100 health points"
Notice that we are passing a Skeleton instance to a method that expects a Monster instance. So within the scope of the method Bob is treated as a Monster, not as a Skeleton.

As we know that derive class can call base class constructor with help of "Base()" method.
initialize base class member
initialize subclass class member
We don't have facility to call derived call constructor from base class that is wrong approach.

Related

Fill common attribute value from all scripts

I have multiple scripts that have one common boolean variable with the same name. As shown below:
Script1.cs
public bool isTrigger = false;
Script2.cs
public bool isTrigger = false;
...
...
Now I have a master script, that should find all the scripts with the common boolean name and change it. Here in the example allScripts would be the list of all scripts that have the isTrigger boolean.
MasterScript.cs
for(int i = 0; i<allScripts.Length; i++){
allScripts[i].isTrigger = true;
}
How do I achieve this? How do I find all the scripts that have the same boolean variable and add it to a list?
As said there are basically two options:
Common Base class
Use a common base class for your components like e.g.
public abstract class MyBase : MonoBehaviour
{
public bool IsTrigger;
// more common fields, properties and methods
}
and then instead inherit your other classes from that one
public class ClassA : MyBase
{
// additional A specific stuff
}
public class ClassB : MyBase
{
// additional B specific stuff
}
Since ClassA and ClassB aready inherit the members from MyBase you can then directly use
public class Master : MonoBehaviour
{
// Use the common base type!
// Either populated via the Inspector
public MyBase[] allInstances;
// Or on runtime
private void Awake()
{
// e.g. find everything of the base type in your scene
allInstances = FindObjectsOfType<MyBase>(true);
foreach(var instance in allInstances)
{
instance.IsTrigger = true;
}
}
}
Advantages
Every subclass already has the field IsTrigger and there is no more to do
The base class can already implement common behavior in general and can be extended using virtual, abstract and then override in the subclasses
Disadvantages
You can only inherit from one single class so extending this with another base class is very inflexible
Interface
Instead of a common base class you can use an interface. An interface does not bring any own implementation but is rather a member template.
As in an interface you can't define fields you will have to use properties
public interface IMyThing
{
bool IsTrigger { get; set; }
}
and then
public class ClassA : MonoBehaviour, IMyThing
{
// example using a serialzed auto-property
[field: SerializeField] public bool IsTrigger { get; set; }
}
public class ClassB : MonoBehaviour, IMyThing
{
// Example using a backing field
[SerializeField] private bool isTrigger;
public bool IsTrigger
{
get => isTrigger;
set => isTrigger = value;
}
}
and then in your master
public class Master : MonoBehaviour
{
// reference via Inspector
// Using the SeriaizeReferenceAttribute enables to serialize and reference any instances of
// classes inheriting from UnityEngine.Object and implementing the interface
[SeriaizeReference] public IMyThing[] allInstances;
private void Awake()
{
// since FindObjectOfType can not be sued on interfaces here i gets a bit more complex
// find all instances from all scenes
var list = new List<IMyThing>();
for (var i = 0; i < SceneManager.sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
var roots = scene.GetRootGameObjects( );
foreach (var root in roots)
{
list.AddRange(root.GetComponentsInChildren<IMyThing>(true));
}
}
allInstances = list.ToArray();
foreach(var instance in allInstances)
{
instance.isTrigger = true;
}
}
}
Advantage
Solves the limitation of the base class: You can implement as many interfaces as you wish
Disadvantage
No guarantee anymore that a class implementing IMyThing is a MonoBehaviour! => FindObjectsOfType can not be used and GetComponent, etc might fail on runtime
Every class has to implement according members itself
Unity doesn't directly serialize interfaces
You can create an interface in which you define your property. You then have your scripts inherited from this class.
When instantiating your objects you need to add them to a list.
public interface IExampleA
{
public bool isTrigger { get; set;}
}
public class B : IExampleA
{
}
class masterscript
{
public void function()
{
// Create objects that has class B
// if object has class B add it to a list
// do your loop
}
}

How to programmatically assign two different classes to one variable?

I am creating a game in Unity and I have 2 classes, I want to assign that variable with 2 different classes depending on a boolean value, to later use that class how I wish, here is the code I have so far to give an idea what im looking for, is it even possible for it to be done? Thanks
public GeneticController geneticController;
public GeneticDriver geneticDriver;
public Object Genetic;
void Start() {
if (RaceSelect.SelectedRace == 2) {
Genetic = geneticDriver;
} else {
Genetic = geneticController;
}
}
void FixedUpdate() {
float steer = (float)(Genetic.Steering);
Steering(steer);
}
At the moment It just says, that Object doesn't have a variable called "Steering". What type should Genetic be?
I am making some assumption here, that both GeneticController and GeneticDriver implement a Steering property? If so, and especially if they have additional properties and methods in common, the proper way to implement this is to refactor the classes so that they share a common interface.
public interface ISteering
{
float Steering {get; set;}
}
public class GeneticController : ISteering
{
public float Steering{ get; set; }
}
public class GeneticDriver: ISteering
{
public float Steering{ get; set; }
}
For there you can make your variable Genetic a type of ISteering.
public ISteering Genetic;
However, if Steering is the only property they have in common, I recommend you taking a second look at your overall design. If the two classes share no, or very little, common functions, they probably don't need to share a variable.
Have you tried using a base class that is derived into your 2 sub-classes?
Something along the lines of:
public class BaseClass
{
public float Steering {get;set;}
}
public class GeneticController : BaseClass
{
}
public class GeneticDriver : BaseClass
{
}
then you do the following (and you do not need the cast):
BaseClass Genetic
void Start()
{
if (RaceSelect.SelectedRace == 2)
{
Genetic = geneticDriver;
} else
{
Genetic = geneticController;
}
}
void FixedUpdate()
{
float steer = Genetic.Steering;
Steering(steer);
}
You could set the BaseClass as an interface also. It depends if you have common functionalities, or not, between you 2 derived classes.

Instantiating an inherited object

Still fairly new to inheritance, so I need some help. Currently, I have an assignment to create a basic game that uses inheritance for 3 weapons. My base class:
public class Weapons : MonoBehaviour
{
public int rateOfFire;
public string myName;
public Weapons(string Name)
{
myName = Name;
}
}
And one of the child classes:
public class Rifle : Weapons
{
public Rifle(string Name)
: base(Name)
{
myName = Name;
rateOfFire = 5;
}
}
My first question comes with the fact if I'm even doing this right?
My second question is how would I instantiate these into my Unity scene?
I've written a method to switch weapons, which is an array on my player script, so would I insert an instantiate line here or create another method? Missing some lines, but here's the just of it:
public GameObject[] weapons;
public int currentweapon = 0;
private int invweap;
void Start()
{
//Managing weapons
invweap = weapons.Length;
SwitchWeapon(currentweapon);
}
void SwitchWeapon(int index)
{
for (int i = 0; i<invweap;i++)
{
if (i == index)
{
weapons[i].gameObject.SetActive(true);
}
else
{
weapons[i].gameObject.SetActive(false);
}
}
}
My first question comes with the fact if I'm even doing this right?
Pretty close. Let's look.
public class Weapons : MonoBehaviour
{
I assume it is a restriction of Unity that this type derives from the given base type. Normally one would never say that a weapon is a kind of "monobehaviour", whatever that is.
Is there any weapon that is not a more specific kind of weapon? No. So this should be an abstract class.
Classes should be named singular unless the class itself represents a group of things. This should be "Weapon", not "Weapons".
abstract class Weapon: MonoBehaviour
{
Moving on.
public int rateOfFire;
Already we are in trouble. Public fields are a bad code smell in C#. If you want a public property then make it a property:
public int RateOfFire { get; set; }
More on the nose though: rate of fire only applies to ranged weapons. You have this in the base class, implying that it applies to all weapons. You don't fire a sword.
public string myName;
Again, public field, bad. Make it a property. Don't call it myName. Call it what it is: Name:
public string Name { get; set; }
Moving on.
public Weapons(string Name)
{
myName = Name;
}
Use camelCase for parameters, PascalCase for properties:
public Weapon(string name)
{
Name = name;
}
Will Name ever change? If not, please make it a get-only property.
public string Name { get; }
(Depending on what version of C# you are using that might not be legal. Try { get; private set; } if that doesn't work.)
That the name can be set in the ctor implies that every instance of a weapon has a name. Is that right? That doesn't seem right. Are there swords named Bob's Sword, and swords named "Stormbringer" and whatnot? I would have thought that the name would be associated with the type, not with a given instance. I would expect that the name would be an abstract property, overridden by the derived type.
public abstract string Name { get; }
Moving on to the derived type:
public Rifle(string Name)
: base(Name)
{
myName = Name;
rateOfFire = 5;
}
This is messed up. You've already set the name in the base class constructor; don't set it again! And why is the name set by the base ctor but the rate of fire set by the derived ctor? Either the rate of fire should be a property of the derived type, or it should be passed in along with the name.
Let's get rid of both of them and just make the name and rate of fire abstract properties overridden in a derived class. And let's improve the type hierarchy so that the weapon base class does not include things common only to ranged weapons.
So, put it all together:
public abstract class Weapon : MonoBehaviour
{
public abstract string Name { get; }
}
public abstract class RangedWeapon : Weapon
{
public abstract int RateOfFire { get; }
}
public class Rifle : RangedWeapon
{
public override string Name { get { return "rifle"; } }
public override int RateOfFire { get { return 5; } }
}
public class Sword : Weapon
{
public override string Name { get { return "sword"; } }
}
Make sense?
My second question is how would I instantiate these into my Unity scene?
It is better to ask one question per question on StackOverflow, because usually what happens is the second question doesn't get answered. Like how I'm not answering it right here.

C# Inheritance of same class twice

Let's say I've such code
public class Holded
{
protected internal int holdedID = 0;
}
public class Inventory : Holded
{
public Inventory() { }
public void changeHoldedID()
{
this.holdedID = 100;
}
}
public class Equipment : Holded
{
public Equipment() { }
public void writeHoldedID()
{
Console.WriteLine("Holded ID is: {0}!", this.holdedID);
}
}
public class Cargo : Holded
{
public Cargo() { }
}
If I'd call changeHoldedID and then writeHoldedID, console will still output a string like "Holded ID is: 0!". Now what I want to achieve is to have same base class (Holded) in both of classes. So if I'd change holdedID from Inventory, Equipment's writeHoldedID function would output "Holded ID is: 100!". Thanks and regards!
#Edit: More detailed: I have a game. Each person is a character, that owns Equipment, Inventory and Cargo class. Each class contains about 20 slots for "items". Now the thing is, that if you try to move an item, for ex. from inventory, to equipment, and there's such index of item, then the item is "swapped" - goes holded, and now I may throw such holded item into Equipment, Inventory or Cargo. That's why I'm in need to share such class between Eq/Inv/Cargo.
With this inheritance structure, what you are asking is 99.9% impossible.
When you create an Inventory object, you are also creating a Holded object with its own holdedID member. When you create an Equipment object, you get a new Holded object as well, with no relation to the old one. Thus, changes to one objects member won't affect the other, and you want it this way.
*To be clear, you don't get a seperate Holded object when creating a derived class, but it can be helpful to think of it in the way I described it.
I don't know why you want to do what you are asking, but its a pretty good bet you need to rework your understanding of inheritance, objects, and polymorphsim.
Now, I said this was 99.9% impossible. You can mark the base class member static which shares it among all instances of Holded (and derived classes), making your code possible. However, there is almost no chance you actually want to do this. static should only be used when you understand object-oriented design and the consequences of using it.
Have you considered, instead of "is a" inheritance type relationship a "contains a" relationship?
You could do something along the following lines:
public interface IHolded
{
Bar Foo();
}
public class Holded: IHolded { ... }
And now you have two options in how you want to implent Equipment and Inventory:
Directly exposing holded throgh a readonly property:
public class Inventory
{
public Inventory(IHolded holded) { ... }
public IHolded Holded { get; }
}
Having them implement the IHolded interface and delegating
implementation to holded.
public Equipment
{
private readonly IHolded holded;
public Equipment(IHolded holded) { this.holded = holded; }
public Bar Foo() { return holded.Foo() };
}
This way you are injecting a Holded object when creating Equipment and Inventory instances ensuring a consistent state in both instances.
A Dictionary to store the person and their HoldedId might work
public class Holded
{
protected internal static Dictionary<string, int> _personHoldedIDs;
internal string _person;
public Holded(string person)
{
_person = person;
if (_personHoldedIDs == null)
_personHoldedIDs = new Dictionary<string, int>();
if (!_personHoldedIDs.ContainsKey(_person))
_personHoldedIDs.Add(_person, 0);
}
}
public class Inventory : Holded
{
public Inventory(string person) : base(person) { }
public void changeHoldedID()
{
_personHoldedIDs[_person] = 100;
}
}
public class Equipment : Holded
{
public Equipment(string person) : base(person) { }
public void writeHoldedID()
{
Console.WriteLine("Holded ID is: {0}!", _personHoldedIDs[_person]);
}
}

How to get class constant via .GetType()

I will try to explain.
I have 3 classes : EnemyTeleport, EnemyZigZag, EnemyNormal. They all inherit base class Enemy.
Each of these 3 classes has his own const -> KillPoints.
So in other class. For example Form Class, I need to get these constants.
These EnemyTeleport, EnemyZigZag, EnemyNormal are located in public Enemy EnemyInstance;
For example
EnemyInstance = new EnemyTeleport();
So how can I get Kill Points from EnemInstance? I don't want to to check each possible variant?
Is there smarter way?
Like EnemInstance.GetType().KillPoints (doesn't work)
Again. EnemyInstance is Enemy type which holds EnemyTeleport. Constant keeps in EnemyTeleport
Why don't you expand your base-class Enemy with a property for the KillPoints. In the constructor of a inherited class you can set the KillPoints to the specific value of the certain enemy
But if you don't want to put it into a base-class:
Make the KillPoints as a property in the certain Enemy like:
public class TeleportEnemy : Enemy
{
public int KillPoints{get;private set;}
public TeleportEnemy()
{
this.KillPoints = 666;
}
}
The code to access the Killpoints by an instance of Enemy looks like:
Enemy enemy = new TeleportEnemy();
PropertyInfo propertyInfo = enemy.GetType().GetProperty("KillPoints");
int value = (int)propertyInfo.GetValue(enemy, null);
I suggest you to use
public abstract class Enemy
{
public abstract List<int> KillPoints;
}
For each specific class define the value of KillPoints (You can use TemplateMethod pattern to calculate your KillPoints)
link : http://www.dofactory.com/Patterns/PatternTemplate.aspx
But in your Main you use base class Enemy for your services
So :
Enemy enemy = new EnemyTeleport();
var result = enemy.KillPoints;
Your hierarchy could be defined as follows. Note how Enemy class is abstract, while concrete enemies implement property KillPoints. Your main implementation has to know only about abstract class Enemy, while concrete implementation should be created elsewhere, depending on some flag in your game.
public abstract class Enemy
{
public abstract int KillPoints {get;}
}
public class EnemyTeleport : Enemy
{
public override int KillPoints
{
get{return 6;}
}
}
public class EnemyZigZag : Enemy
{
public override int KillPoints
{
get{return 10;}
}
}
Now you can take advantage of polymorphic call to KillPoints
private Enemy EnemyInstance;
void Main()
{
EnemyInstance = new EnemyTeleport();
Console.WriteLine(EnemyInstance.KillPoints);
}

Categories

Resources