1

I have a class called Weapon which stores public fields called Strength, Dexterity, Vitality, Intelligence. (type Int32)

In the constructor of the weapon object i have a method which randomly chooses one of these 'Attributes' and assigns a value to it.

Random random = new Random();
int primary = random.Next(0, 4);
if (//Condition met)
{
    switch (primary)
    {
        case 0:
            Strength = ...
            break;
        case 1:
            Dexterity = ...
            break;
        case 2:
            Vitality = ...
            break;
        case 3:
            Intelligence = ...
            break;
    }
}

I find this code ugly and hard to maintain.

Is there a way to store these 'Attributes' in a Array or List? Random would then choose between 0 and Attributes.Length and assign a value accordingly.

Kind of like this (this wont work just so you understand)

Random random = new Random();
int[] attributes = {this.Strength, this.Dexterity, this.Vitality, this.Intelligence};
int index = random.Next(0, attributes.Length);
//lets say 0, so index = Strength
if (//Condition met)
{
   //This is the part where index would be 0 so the program would assign 'Strength' a value
}

The value should be assigned to the field chosen in the Array. Not the element in the Array.

This would greatly help when later developing inheriting classes

Unfrieden
  • 69
  • 6
  • Well, you could define a common interface, f.e. `IWeaponAttribute` which has a `AttributeType enum`-value and an `int Value` property. Then you could have a `List`. – Tim Schmelter Dec 12 '16 at 10:29
  • `var fieldStrength = this.GetType().GetProperty("Strength", /*binding flags*/)` then `PropertyInfo[] attributes = { fieldStrength, ... };` and then `attributes[0].SetValue(this, newValue);` But i would not recommend that ;) – mrogal.ski Dec 12 '16 at 10:30
  • check this out. dictionary may help. you can store functions with key http://stackoverflow.com/questions/4233536/c-sharp-store-functions-in-a-dictionary – M.kazem Akhgary Dec 12 '16 at 10:38

5 Answers5

2

It can be done with unsafe code and pointers, but a safer aproach can be with array of methods:

Action<int>[] stats = { i => Strength = i, i => Dexterity = i, i => Vitality = i, i => Intelligence = i };

stats[new Random().Next(stats.Length)](123); // set random stat to 123
Slai
  • 22,144
  • 5
  • 45
  • 53
1

I would recommend you to use a Hashmap.you can avoid multiple switch and case statements by put and get methods.

1

You can use an enum.

enum Attributes {Strength, Dexterity, Vitality, Intelligence};

And then you can switch on this:

switch (chosenAttribute)
    {
        case Attributes.Strength:
            ...
            break;
        case Attributes.Dexterity:
            ...
            break;
        case Attributes.Vitality:
            ...
            break;
        case Attributes.Intelligence:
            ...
            break;
    }
Owen Pauling
  • 11,349
  • 20
  • 53
  • 64
  • I found this to be the simplest approach. I used a List and added "Strength" etc. in the base class constructor. Inheriting weapons can now just remove a non needed entry in the constructor. – Unfrieden Dec 12 '16 at 11:47
1

There are different ways to solve it. For example, you could wrap all your attributes into some class like the following and make your properties wrappers around it:

public class ItemAttribute
{
    public int Value { get; set; }
}

public class Weapon
{
     private readonly ItemAttribute _strengthAttribute = new ItemAttribute();
     // ....

     private readonly ItemAttribute[] _attributes = new[] {_strengthAtribute};

     public int Strength
     {
         get { return _strengthAttribute.Value; }
         set { _strengthAttribute.Value = value; }
     }
}

In such case you can extend your attributes in the future with some additional logic (like handling min/max values for example). It is so called "Value Object" pattern.

Another way to store collection (array or dictionary - it isn't important) of int values directly. And also create wrapper properties.

oryol
  • 5,178
  • 2
  • 23
  • 18
1

You have many possibilities but you should think which one would be the best for your problem. Let's assume that you have one base class for all items :

public abstract class BaseItem { 
    // from this class other items derive
}

If that's the case then you can create simple "map" that will connect properties with some kind of "names". And you can make it once at startup :

public abstract class BaseItem {
    static Dictionary<string, PropertyInfo> _attributes = null;

    public BaseItem() {
        if ( _attributes == null ) { 
            InitializeProperties(); 
        }
    }

    void InitializeProperties() {
        _attributes = new Dictionary<string, PropertyInfo>()

        var properties = typeof(BaseItem).GetProperties().Where( property => Attribute.IsDefined(property, typeof(ItemAttributeAttribute)));
        foreach(var property in properties) {
            string name = ((ItemAttributeAttribute)property.GetCustomAttribute(property, typeof(ItemAttributeAttribute))).AttributeName;
            _attributes.Add(name, property);
        }
    }
}

Now you can add this custom attribute ItemAttributeAttribute

public ItemAttributeAttribute : Attribute { string _Name; public string AttributeName { get { return _Name; } set { _Name = value; } } public ItemAttributeAttribute(string name) { _Name = name; } }

Last thing to do is to bound attributes with names:

public abstract class BaseItem {

    // rest of code here ...

    [ItemAttribute("Agility")]
    int Agility;

    // rest of code here ...
}

This way you can "bind" properties to names easily. Now you can do some method to assign the values :

void SetValue(string name, object value) {
    if(_attributes.ContainsKey(name)) {
        _attributes[name].SetValue(this, value); // you can put this into BaseItem though
    }
}

Using the example that you have shown I'll make 2 classes Sword and Shield and i will fill them with random values :

public class Sword : BaseItem {
    public Sword() : base() {
        string[] names = new string[] { "atk", "def", "weight", "something" };
        string choosen = names[new Random().Next() % names.Length];
        SetValue(choosen, new Random().Next() % 1337);
    }
}

public class Shield : BaseItem {
    public Shield() : base() {
        int rand = new Random().Next() % _attributes.Count();
        SetValue(_attributes.Keys[rand], new Random().Next() % 1337);
    }
}

Of course you have to remember to set all attributes in base class, that may be difficuilt but you can also make some improvements on this method.

For all Reflection haters ( and i know that most of SO users are such ): sometimes it is better to use this kind of approach instead of making useless variables, methods and shit load of code lines.

mrogal.ski
  • 5,828
  • 1
  • 21
  • 30