2

So I have a struct like this:

public struct Attributes {
    public int vitality;
    public int intelligence;
    public int dexterity;
    public int agility;
}

And I use it like this:

Attributes a = new Attributes();
Attributes b = new Attributes();

And what I want to achieve is this:

Attributes c = new Attributes();
c = a + b;

I want this to give me the sum of these 4 variables I specified above of those two Attributess.

Inside the struct, I tried to have this:

public static Attributes operator +(Attributes x, Attributes y) {
        PropertyInfo[] info = typeof(Attributes).GetType().GetProperties();
        for (int i = 0; i < info.Length; i++) {                
            info[i].SetValue(x, (int) info[i].GetValue(x) + (int) info[i].GetValue(y), null);
        }

        return x;
}

This apparently doesn't work, giving me an error.

Can you guys help me about this? What could I do to achieve what I want? Thanks.

Masea
  • 103
  • 2
  • 10

1 Answers1

2

Just use the following:

public static Attributes operator +(Attributes x, Attributes y) {
        return new Attributes 
        {
            vitality = x.vitality + y.vitality,
            intelligence = x.intelligence + y.intelligence,
            dexterity = x.dexterity+ y.dexterity,
            agility = x.agility + y.agility
        };
}

If you don't have to, there's no need to be fancy and use reflection. It's a powerful tool but don't fall into the golden hammer fallacy. Only use it where truly necessary.

EDIT: if you really do want to use Reflection, this is a working version of your code:

public static Attributes operator +(Attributes x, Attributes y)
{
    FieldInfo[] info = typeof(Attributes).GetFields();
    object boxedResult = new Attributes();
    foreach (FieldInfo fi in info)
    {
        fi.SetValue(boxedResult, (int)fi.GetValue(x) + (int)fi.GetValue(y));
    }

    return (Attributes)boxedResult;
}

I think it warrants some explanation for what changes I made:

  1. I would consider it unusual if operator+ modified one of its operands, so I made it return a new Attributes struct instead.
  2. You called typeof(Attributes).GetType() which basically took the type of Attributes and got the type of the type, which is definitely not what you want.
  3. You were checking for property info, but Attributes does not have properties, only fields.
  4. I explicitly boxed the Attributes struct before setting its fields. Boxing a struct makes a copy of it, and boxing happens when you take a value type (like any struct for example) and cast it to object. What happens is your value type (which lives on the stack) is put into a neat little reference-type box and stored on the heap, since only reference types can live on the heap. Actually, a copy of it is stored on the heap. So since SetValue takes an object parameter as the "target", the struct would be boxed every time, effectively taking your changes and applying them to a copy which is then promptly thrown away. By explicitly boxing it, I make all the changes on the same copy of your struct, and then returning that after unboxing it. This step would not be necessary if Attributes was a reference type.
Daniel Crha
  • 675
  • 5
  • 13
  • Yeah, that'd absolutely work. However, I have many more variables than that (which all of them are an integer) and it is possible to add even more in the future. So I really want to keep it with my way. – Masea Dec 06 '19 at 21:30
  • Using reflection to save yourself work of writing operators for your class seems like a bad idea. In your place I would either stick to your attributes being typed and write the operator in the way I proposed, or make your attributes into something like `Dictionary` and sum them with LINQ. – Daniel Crha Dec 06 '19 at 21:41
  • Sure, thanks. On the other hand, I'd really like to learn a few things about reflections as well, since I am really new into C#. So, by any chance, could you help me fix my code? I think you have understood it. – Masea Dec 06 '19 at 21:45
  • I edited my answer to include working code and explanations for why it was broken. It might take you a while to wrap your head around point nr. 4, but just go throught it a few times and read up on it on the internet. I think it's a nice thought exercise about understanding what objects really are and where they live. – Daniel Crha Dec 06 '19 at 22:07
  • Oh my god. I don't know how can I thank you, look at that beautiful code and as well as the explanation. Thank you very much. Yes, I am really new into C# and I don't even know the difference between a property and a field. So my bad. Not even saying getting the type of a type... I made this a struct and that's because I am told you can set yourself a new operator on those and not on a class. Am I wrong? And this was kinda tricky since it isn't a reference type, I'd take me days to accomplish such a thing. – Masea Dec 06 '19 at 22:25
  • Operator overloading is possible on a class as well as a struct, if that's what you mean. You can go ahead and change `Attributes` to a class and see that it still works. You can also play around with removing the explicit boxing, and then changing from a struct to a class. You'll see the exact issue the boxing avoids. – Daniel Crha Dec 06 '19 at 22:32
  • Yeah, I switched it to a class and it works even better, with cleaner code. Actually, now I get it that making it a struct was a terrible idea. Thanks again. – Masea Dec 06 '19 at 22:46