Instantiating an inherited object - c#

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.

Related

Creating an object of an inherited class in 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.

Can I require that a child initialize variables declared in its parent class?

I have an abstract class Animal, with classes that inherit variables from that class. For instance, a Name string will hold the name of the animal that class represents.
My question is, can I require that those variables be initialized in the child's constructor? I want to ensure that Cat.cs, Dog.cs, etc actually store a name. I believe I can create a virtual Start() or Awake() method, but that will only assure that the Name string have something in it, but not the actual name of the animal.
If you'll add this constructor to the base Animal class:
public abstract class Animal
{
protected Animal(string name)
{
this.Name = name;
}
public string Name { get; set; }
}
Any type derives from it will be required to supply that argument to the constructor:
public class Dog : Animal
{
public Dog(string name) : base(name)
{
}
}
See MSDN
This is almost exactly the same as haim770, but shows you can have default constructors as well (and one other thing I'll point out at the bottom)
public abstract class Animal
{
public string Name { get; private set; }
public Animal(string name)
{
Name = name;
}
}
public class Dog : Animal
{
public Dog()
: base("Rover")
{
}
public Dog(string name)
: base(name)
{
}
}
The only other thing to note here is that the constructor of Animal is not declared protected. Why? Because it doesn't really matter. Because Animal is an abstract class you can't create new instances of it anyway, so making the constructor public is functionally equivalent to making it protected in this case.

How to inherit a static property with a unique value for each subclass?

I have a series of objects, lets call them buildings, that each share certain properties that are static for that building, but different for each building, such as price. I assumed that the best way to implement this was to create an abstract superclass with the shared price attribute and set the values in each subclass, but I cannot figure out how to get this to work. Here is an example of something I have tried:
using System;
public abstract class Buildings
{
internal static int price;
internal static int turnsToMake;
}
using System;
public class Walls : Buildings
{
public Walls()
{
price = 200;
turnsToMake = 5;
}
}
This works fine for construction, but if I want to check the price before creating it (to check if the player has enough money) then it just returns a null value. I'm sure that it is is a super simple fix, but I can't figure it out. Any help?
There is a "patchy" yet simple solution that's worth to consider. If you define your base class as a Generic class, and in deriving classes set T as the class itself, It will work.
This happens because .NET statically defines a new type for each new definition.
For example:
class Base<T>
{
public static int Counter { get; set; }
public Base()
{
}
}
class DerivedA : Base<DerivedA>
{
public DerivedA()
{
}
}
class DerivedB : Base<DerivedB>
{
public DerivedB()
{
}
}
class Program
{
static void Main(string[] args)
{
DerivedA.Counter = 4;
DerivedB.Counter = 7;
Console.WriteLine(DerivedA.Counter.ToString()); // Prints 4
Console.WriteLine(DerivedB.Counter.ToString()); // Prints 7
Console.ReadLine();
}
}
Don't use static. Static says that all instances of Building have the same value. A derived class will not inherit its own copy of the statics; but would always modify the base class statics. In your design there would only be one value for price and turnsToMake.
This should work for you:
public abstract class Buildings
{
internal int price;
internal int turnsToMake;
}
However, most people don't like using fields these days and prefer properties.
public abstract class Buildings
{
internal int Price { get; set; }
internal int TurnsToMake { get; set; }
}
I want to check the price before creating it […]
I suppose that's how you got to static fields; however, static and virtual behaviour cannot be combined. That is, you would have to re-declare your static fields for each subclass. Otherwise, all your subclasses share the exact same fields and overwrite each others' values.
Another solution would be to use the Lazy<T, TMetadata> type from the .NET (4 or higher) framework class library:
public class Cost
{
public int Price { get; set; }
public int TurnsToMake { get; set; }
}
var lazyBuildings = new Lazy<Buildings, Cost>(
valueFactory: () => new Walls(),
metadata: new Cost { Price = 200, TurnsToMake = 5 });
if (lazyBuildings.Metadata.Price < …)
{
var buildings = lazyBuildings.Value;
}
That is, the metadata (.Metadata) now resides outside of the actual types (Buildings, Walls) and can be used to decide whether you actually want to build an instance ( .Value) of it.
(Thanks to polymorphism, you can have a whole collection of such "lazy factories" and find a building type to instantiate based on the metadata of each factory.)
Building on Uri Abramson's answer above:
If you need to access the static property from within the Base class, use reflection to get the value from T. Also, you can enforce that Base must be inherited using T of the derived type.
e.g.
class Base<T> where T : Base <T> {
static int GetPropertyValueFromDerivedClass<PropertyType>(BindingFlags Flags = BindingFlags.Public | BindingFlags.Static, [CallerMemberName] string PropertyName = "")
{
return typeof(T).GetProperty(PropertyName, Flags)?.GetValue(null);
}
static int Counter{ get => GetPropertyValueFromDerivedClass(); }
}
static int DoubleCounter{ return Counter*2; } //returns 8 for DerivedA and 14 for DerivedB
}
If you have a better way to do this, please post.
Not as easy for the inheritor, but workable...
public abstract class BaseType
{
public abstract contentType Data { get; set; }
}
public class InheritedType : BaseType
{
protected static contentType _inheritedTypeContent;
public override contentType Data { get => _inheritedTypeContent; set => _inheritedTypeContent = value; }
}

Are class initializers possible in C#?

In C# you have object initializers to initialize fields of objects at creation time without using a constructor.
Now I'm wondering if there is an equivalent to classes which means that you can 'initialize' properties of classes when defining subclasses without actually using an override syntax but simply declaring what the value of a known property is.
Example:
public abstract class Car {
public abstract string Name { get; }
}
// usual approach
public class Mustang : Car {
public overwrite string Name { get { return "Ford Mustang"; } }
}
// my idea of avoiding boilerplate code
public class Mustang : Car { Name = "Ford Mustang" }
Is there a way to accomplish this? If there is none, could T4 templates be of any help?
To make rekire's example clearer, you'd write something like:
public abstract class Car
{
public string Name { get; private set; }
protected Car(string name)
{
this.Name = name;
}
}
public class Mustang : Car
{
public Mustang() : base("Mustang")
{
}
}
EDIT: Another option is to use attributes, where you'd write:
[CarName("Mustang")]
public class Mustang : Car
... having written appropriate reflection code in Car. I would strongly recommend that you don't though. Of course, attributes may be useful in your real context.
You could do this via a construtor, where you need to call the base class constructor.
class car {
Public string Name {public get; protected set}
}
That should basically work too.

What is the best way to inherit an array that needs to store subclass specific data?

I'm trying to set up an inheritance hierarchy similar to the following:
abstract class Vehicle
{
public string Name;
public List<Axle> Axles;
}
class Motorcycle : Vehicle
{
}
class Car : Vehicle
{
}
abstract class Axle
{
public int Length;
public void Turn(int numTurns) { ... }
}
class MotorcycleAxle : Axle
{
public bool WheelAttached;
}
class CarAxle : Axle
{
public bool LeftWheelAttached;
public bool RightWheelAttached;
}
I would like to only store MotorcycleAxle objects in a Motorcycle object's Axles array, and CarAxle objects in a Car object's Axles array. The problem is there is no way to override the array in the subclass to force one or the other. Ideally something like the following would be valid for the Motorcycle class:
class Motorcycle : Vehicle
{
public override List<MotorcycleAxle> Axles;
}
but the types have to match when overriding. How can I support this architecture? Will I just have to do a lot of run-time type checking and casting wherever the Axles member is accessed? I don't like adding run-time type checks because you start to lose the benefits of strong typing and polymorphism. There have to be at least some run-time checks in this scenario since the WheelAttached and Left/RightWheelAttached properties depend on the type, but I would like to minimize them.
Use more generics
abstract class Vehicle<T> where T : Axle
{
public string Name;
public List<T> Axles;
}
class Motorcycle : Vehicle<MotorcycleAxle>
{
}
class Car : Vehicle<CarAxle>
{
}
abstract class Axle
{
public int Length;
public void Turn(int numTurns) { ... }
}
class MotorcycleAxle : Axle
{
public bool WheelAttached;
}
class CarAxle : Axle
{
public bool LeftWheelAttached;
public bool RightWheelAttached;
}
2 options spring to mind. 1 is using generics:
abstract class Vehicle<TAxle> where TAxle : Axle {
public List<TAxle> Axles;
}
The second uses shadowing - and this assumes you have properties:
abstract class Vehicle {
public IList<Axle> Axles { get; set; }
}
class Motorcyle : Vehicle {
public new IList<MotorcycleAxle> Axles { get; set; }
}
class Car : Vehicle {
public new IList<CarAxle> Axles { get; set; }
}
void Main() {
Vehicle v = new Car();
// v.Axles is IList<Axle>
Car c = (Car) v;
// c.Axles is IList<CarAxle>
// ((Vehicle)c).Axles is IList<Axle>
The problem with shadowing is that you have a generic List. Unfortunately, you can't constrain the list to only contain CarAxle. Also, you can't cast a List<Axle> into List<CarAxle> - even though there's an inheritance chain there. You have to cast each object into a new List (though that becomes much easier with LINQ).
I'd go for generics myself.
I asked a similar question and got a better answer, the problem is related to C#'s support for covariance and contravariance. See that discussion for a little more information.

Categories

Resources