I don't know if it's what i'm suppose to be looking for, but i'm assuming so because that's how you override Equal and ToString. Say we have this struct Code (CSharp): public struct SomeStruct { public float valueA; public int valueB; public bool valueC; } how can I say Code (CSharp): SomeStruct ss; void Blah(SomeStruct ss){ this.ss += ss; } I never did this before but I thought you just need to write the logic(a+a, b+b, w/e with c) in some override, I tried searching google but I don't think I'm verbalizing my question all that good to it, haha.
This is called operator overloading, you have to define static operator methods for your types. See here: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/operator Note, if you implement a + operator, += implicitly gets implemented. (+= is technically just syntax sugar) Here's a list of all the operators you can overload: https://docs.microsoft.com/en-us/do...-expressions-operators/overloadable-operators
Just to clarify, if I have this for example: Code (CSharp): public struct SomeStruct { float value; public static SomeStruct operator *(SomeStruct a, SomeStruct b){ return new SomeStruct(a.value * b.value); //suppose there's a constructor, heh } } and somewhere I say Code (CSharp): someStructVariable *= otherSomeStructVariable; Am I guaranteed that 'someStructVariable' will be what is 'a' in the operator declartion and 'otherSomeStructVariable' be 'b'? *I know that doesn't really matter in this example.
So lets see what actually happens under the hood in IL. Here I wrote up your example: https://sharplab.io/#v2:C4LglgNgNAJ...1PUoJcbnFw6uvNzkAHYPQ0PMzjURiqq91zexwM64qKAA= And we get this IL for your code where you use *=: Code (csharp): IL_0019: ldloc.0 IL_001a: ldloc.1 IL_001b: call valuetype SomeStruct SomeStruct::op_Multiply(valuetype SomeStruct, valuetype SomeStruct) IL_0020: stloc.0 What this code is saying is: Code (csharp): load variable 0 onto stack (this would be 'a' in my code) load variable 1 onto stack (this would be 'b' in my code) call the static method SomeStruct.op_Multiply with signature that takes 2 SomeStruct's as parameters pop value at top of stack into variable 0 (this would be a in my code) Note that how methods are called in IL is that you push N number of values onto the memory stack that match the method signature, then call the method. This is how values are actually passed in memory to methods... via the stack this way. Same with returns... when you return a value really the IL pushes the return value onto the stack, and the calling code pops it off the stack into its place in memory. Next, really the *= is just syntax sugar for a = a * b... and that's really just syntax sugar for a = SomeStruct.op_Multiply(a,b). Which compiles into that IL. Now when you say: I will say... The 'value' that is 'someStructVariable' will be what is in a. And the value that is 'otherSomeStructVariable' will be was in in b. But I'm not sure what assumptions you're making beyond that. It'd be like if you said: a = 3; b = 5; a *= b; In the op_Multiply of that, the 1st parameter will be 3, and the 2nd will be 5. But if you're hoping some sort of 'state' will pass along... well no. Because a struct by design shouldn't pass state (unless you use 'ref' or 'out'). But order wise... yes... the order is guaranteed to be preserved. Otherwise non-commutative operations (division, subtraction, quaternion multiplication) wouldn't work. And since we know quat1 *= quat2 is guaranteed valid, we know it must respect order.
As an aside and if you're at all interested in more about this topic... check out this spoiler. Spoiler In your code I'd actually write the multiply method as: Code (csharp): public static SomeStruct operator *(SomeStruct a, SomeStruct b) { a.value *= b.value; return a; } I say this because if you look at my link and how this version is implemented: https://sharplab.io/#v2:C4LglgNgNAJ...EM4G1PUoJcbnFw6uvNy1EYqqvfC7sZrc5AB2dSnYoA=== Note that yours actually calls a new fuction (the constructor): Code (csharp): IL_0000: nop IL_0001: ldarg.0 IL_0002: ldfld float32 SomeStruct::'value' IL_0007: ldarg.1 IL_0008: ldfld float32 SomeStruct::'value' IL_000d: mul IL_000e: newobj instance void SomeStruct::.ctor(float32) //THIS LINE HERE IL_0013: stloc.0 IL_0014: br.s IL_0016 Where as mine does not: Code (csharp): IL_0000: nop IL_0001: ldarga.s a IL_0003: ldflda float32 SomeStruct::'value' IL_0008: dup IL_0009: ldind.r4 IL_000a: ldarg.1 IL_000b: ldfld float32 SomeStruct::'value' IL_0010: mul IL_0011: stind.r4 IL_0012: ldarg.0 IL_0013: stloc.0 IL_0014: br.s IL_0016 (You could alternatively create a 3rd struct without calling your parameterized constructor) The problem here is you're actually loading a new stack frame to call that constructor. Which is actually less performant.
I realize now writing the example with multiplication was a bad idea, I do need it for subtraction and division, I don't like messing with quaternions myself though. Thanks for break down and all the input man, you're really helpful. Thank you for that, I tend to do this kind of optimization from day 1(I wrote it like that in the example because it's more "conventional" / how it is on the microsoft docs) and seeing tutorials just going willy nilly with that sort of stuff made me think I'm obsessing over nothing, glad to have a reinforcement that I'm on the right path with this.(all in moderation ofc, I don't go crazy over it)