josef_skywalker
josef_skywalker

Reputation: 1107

Generic class<T> with constructor to choose the type

I have three classes that have all the same named Properties. Let's say the classes are Sword, Bow and Hammer -- and the properties are Damage and Range. How could I instantiate a class of Weapon<T> with a constructor where I pass an int to choose the Type of this class?

I am not sure if this is even the right way to do what I want to.

public class Weapon<T>
{
}

public  class Sword
{
    public int Damage { get => 10; }
    public int Range { get => 12; }
}

public class Bow
{
    public int Damage { get => 8; }
    public int Range { get => 28; }
}

public class Hammer
{
    public int Damage { get => 15; }
    public int Range { get => 8; }
}

Upvotes: 1

Views: 188

Answers (5)

Nicky
Nicky

Reputation: 87

You could start by creating interface for parent weapon class. Then extend weapon class with child classes to make swords, hammers and etc... Then you can add custom properties for each child class and still use them as weapon because they all share same interface/parent class and they all have attack method...

public interface IWeapon
{
    int Damage { get; }
    int Range { get; }
    void Attack();
}

public class Weapon : IWeapon
{
    public int Damage { get; private set; }
    public int Range { get; private set; }

    public Weapon(int damage, int range)
    {
        Damage = damage;
        Range = range;
    }

    public virtual void Attack()
    {
        Console.WriteLine("Weapon: Attack");
    }
}

public class Sword : Weapon
{
    //some sword properties here...

    public Sword(int damage, int range) : base(damage, range)
    {

    }

    public override void Attack()
    {
        Console.WriteLine("Weapon Sword: Attack");
    }
}

public class Bow : Weapon
{
    //some bow properties here...

    public Bow(int damage, int range) : base(damage, range)
    {

    }

    public override void Attack()
    {
        Console.WriteLine("Weapon Bow: Attack");
    }
}

public class Hammer : Weapon
{
    //some hammer properties here...

    public Hammer(int damage, int range) : base(damage, range)
    {

    }

    public override void Attack()
    {
        Console.WriteLine("Weapon Hammer: Attack");
    }
}

class Program
{
    public static void Main(string[] args)
    {
        IWeapon hammerWeapon = new Hammer(15, 10);
        hammerWeapon.Attack();
    }
}

Upvotes: 1

Ant P
Ant P

Reputation: 25221

I'd simplify this beyond what you have and also beyond the other solutions proposed.

public class Weapon
{
    private int _range;
    private int _damage;

    public Weapon(int range, int damage)
    {
        _range = range;
        _damage = damage;
    }

    public int Range => _range;
    public int Damage => _damage;
}

There's no real need for polymorphism here - all you want to do is assign different readonly values at runtime. If you later want differing behaviour per weapon, you can achieve that with the strategy pattern.

I'd then just use factories to instantiate different weapons.

What those factories looked like would depend on how they need to be called but in practice your factory method could look something like this:

public Weapon GetWeapon(string weaponType)
{
    var weaponProperties = propertiesFor(weaponType);
    return new Weapon(weaponProperties.Range, weaponProperties.Damage);
}

Where propertiesFor looks up the appropriate values for the given weapon type in a dictionary, file etc. etc.

I would steer clear of an IWeapon interface unless you really need to supply different implementations of weapons at runtime. Don't write it 'til you need it. Declaring an interface just for the purposes of mocking in tests (as some others have suggested) would normally indicate to me that your test boundaries are off or you have some dependencies to isolate better (but that's a broader conversation).

Upvotes: 5

MakePeaceGreatAgain
MakePeaceGreatAgain

Reputation: 37000

What you´ve described is known as factory-pattern. You have some factory that is able to instantiate other objects, in your case depending on an integer:

class WeaponFactory
{
    public static IWeapon CreateWeapon(WeaponType type)
    {
        switch type:
            case WeaponType.Sword: return new Sword();
            case WeaponType.Hammer: return new Hammer();
            case WeaponType.Bow: return new Bow();
            default: throw new ArgumentException("Unknown weaponType");
    }
}
enum WeaponType { Sword, Hammer, Bow }
interface IWeapon
{
    int Damage { get; }
    int Range { get; }
}

Finally all your classes should implement that interface. Now you can easily create instances with the following code:

var hammer = WeaponFactory.CreateWeapon(WeaponType.Hammer);

Upvotes: 6

Łukasz Rembecki
Łukasz Rembecki

Reputation: 41

public interface IWeapon
{
    int Damage { get; }
    int Range { get; }
}

public class Weapon : IWeapon
{
    protected int _damage, _range;

    public int Damage
    {
        get { return _damage; }
    }

    public int Range
    {
        get { return _range; }
    }
}

public class Sword : Weapon
{
    public Sword()
    {
        _damage = 10;
        _range = 12;
    }
}

public class Bow : Weapon
{
    public Bow()
    {
        _damage = 8;
        _range = 28;
    }
}

public class Hammer : Weapon
{
    public Hammer()
    {
        _damage = 15;
        _range = 8;
    }
}

Upvotes: 4

Impostor
Impostor

Reputation: 2050

Inherit from a base class

public class Weapon
{
    public int Damage { get; set; }
    public int Range { get; set; }
}

public class Sword : Weapon
{
}

public class Bow : Weapon
{
}

public class Hammer : Weapon
{
}

and instanciate this way

Weapon item = new Sword() { Damage = 10, Range = 20 };

Upvotes: 2

Related Questions