Is there a way to write a C# extension method that interacts with the getter/setter of properties with a given type?
Here's a simplified example to show what I mean. The end objective is to make code that gets and sets properties in more complex ways cleaner to read.
// Assume this is provided by your API:
struct Vector {
public float x;
public float y;
}
class Line {
// The structs make these pass-by-value, so Fn(this ref Vector v) won't help
public Vector start { get; set; }
public Vector end { get; set; }
}
Given the above, can you somehow write an extension on the property type? Something like this:
// Obviously this syntax doesn't work, but hopefully this gets the idea across
public static void MoveX(this IProperty<Vector> prop, float val) {
Vector v = prop.GetValue();
v.x += val;
prop.SetValue(v);
}
So that in your code you can write this:
public static void UsePropExensions(Line myLine) {
myLine.start.MoveX(1f);
myLine.end.MoveX(2f);
}
Instead of this:
public static void UsePropsDirectly(Line myLine) {
myLine.start = new Vector(myLine.start.x + 1f, myline.start.y);
myLine.end = new Vector(myLine.start.x + 2f, myline.start.y);
}
Why:
The best it seems you can do is this:
public static void AddX(this Vector v, float x) {
return new Vector(v.x + x, v.y);
}
void DoStuff(Line myLine) {
myLine.start = myLine.start.AddX(1f);
}
But that leads to copy-paste errors when the path to the property is more complex and repetitive. Humans may be pretty good at spotting differences, but they're pretty bad at spotting inconsistencies between differences. So the more you repeat yourself with subtle variations, the easier it is to introduce bugs.
void MoveStuffAround() {
obj.left.top.scale = obj.left.top.scale.AddX(-1f);
obj.left.bottom.pos = obj.left.bottom.pos.AddY(-0.5f);
obj.right.top.invScale = obj.right.top.invScale.AddX(1f);
obj.right.bottom.pos = obj.left.bottom.pos.AddY(0.5f); // oops!
}
// But if you don't repeat yourself, you make fewer errors
void MoveWithProperties() {
obj.left.top.scale.IncrX(-1f);
obj.left.bottom.pos.IncrY(-0.5f);
obj.right.top.invScale.IncrX(1f);
obj.right.bottom.pos.IncrY(0.5f);
}