3

I've been working with C# and XAML and was wondering if there was any better way to access members of a class after casting them. This is an example of what I'd normally end up having to do:

private void MyButton_Clicked(object sender,  RoutedEventtArgs e)
{
    ((PlaneProjection)((Button)sender).Projection).RotationX = 20;
}

Which'd rotate the button every time you clicked it, but with more complex actions I'd need more and more parentheses. I know I can use as which'd make it a little easier to read, but that'd still make it:

((sender as Button).Projection as PlaneProjection).RotationX = 20;

Which is still more parentheses than I'd like to have.

Am I doing this the long way or missing something? Or is this just the way it is and I'll have to deal with the possibility of harder to read code like this?

Enfyve
  • 916
  • 10
  • 22
  • +1 for the question that always bugged me too. I really don't understand why there's no friendlier syntax for cast-and-access. Here, the syntax was borrowed from C++. In "friendlier" language, I'd expect at least i.e. `sender.(Button.Projection).(PlaneProjection.RotationX) = 20;` which effectively would mean the same, and would be so much readable.. – quetzalcoatl Feb 07 '13 at 15:21
  • Yeah that makes sense, it'd even be easier if you could just do `(Button)sender.(PlaneProjection)Projection.RotationX = 20;` or even something where () has higher priority than member access. – Enfyve Feb 07 '13 at 15:32
  • yes, if it had. However, in such case you wouldn't be able to access a member value and then cast it. i.e. `(double)Button.Size.Height` would try to cast the Button to double, and here you'd need extra parens again:) – quetzalcoatl Feb 07 '13 at 21:18

4 Answers4

1
var button = sender as Button;

if (button == null)
    return;

var projection = button.Projection as PlaneProjection;

if (projection == null)
    return;

projection.RotationX = 20;

is a much safer way since you're making sure your casts won't throw NullReferenceException.

However, I must mention that I would rather create a ViewModel holding the Double X {get;set;} and a Command to which the button can be bound, and not manipulate UI elements in code behind.

Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154
  • Thanks for pointing that out, I'd likely have forgotten to null check prior to using - but since it is a auto-generated event for a XAML object, sender and "most" of it's members should never be null, right? – Enfyve Feb 07 '13 at 15:24
  • @Night5h4d3 sender wont be null, but casting sender or any member to an object type it actually isn't in runtime (which could happen) is going to return null – Federico Berasategui Feb 07 '13 at 15:30
  • That is a good point, and considering ease of human error it's entirely possible. I'd +1, but I think I need 15 rep or something to do that. – Enfyve Feb 07 '13 at 15:43
1

(A) Speaking strictly, nope, there's no way. After you make a cast, you're left with a temporary value of a different type - and almost no syntax branches are left after the cast, be it normal cast or the AS operator. You must either parenthesize it and immediatelly use it, or you must store it in a variable.

(B) Speaking loosely, there's a way: just do not make the cast at all. But be informed that it will:

  • require the newer .Net platform
  • be less safe (compile time name checks are OFF)
  • be even less safe (runtime TYPE checks are off, if property-name is found then it used, regardless of what exact obejct type you had in the variable)
  • has a bit worse performance (simply, dynamic ops are slower than compiled ones)

well, and as I said dynamic, it uses the CLR4.0 dynamic expression:

private void MyButton_Clicked(object sender,  RoutedEventtArgs e)
{
    dynamic tmp = sender;
    tmp.Projection.RotationX = 20;
}

But, I really don't suggest doing it just to skip some parenthesis.. There is ZERO type checking in this expression. It is 100% duck typing. For a Button, it is OK, as the API of the button is really not likely to change. But for your own code which will often mutate, be careful - or include proper unit tests for property existence.

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • Option B seems extremely convenient, but I see what you mean with it only resolving at runtime. Also, I think you mean tmp.Projection.RotationX = 20; – Enfyve Feb 07 '13 at 15:20
  • yes, indeed it should have been 'tmp', I've corrected the example, thanks;) – quetzalcoatl Feb 07 '13 at 21:15
0

If you don't like parenthesis, you can create variables instead.

var button = (Button)sender; // or 'sender as Button'
var planeProjection = (PlaneProjection)button.Projection; // or 'button.Projection as PlaneProjection'
Matthew
  • 24,703
  • 9
  • 76
  • 110
0

I don't see any way to get reed of the parentheses and casts.

can't you just divide it

var planeProjection = (PlaneProjection)((Button)sender).Projection;

and then

planeProjection.RotationX = 20;
planeProjection.RotationY = 20;
planeProjection.RotationZ = 20;
Pedro.The.Kid
  • 1,968
  • 1
  • 14
  • 18