1

Is there any way in C# to be able to do this:

map.player.x = 5;

while keeping player of type Point (or silently convertible to one) and still be able to check the value for correctness when doing the assignment?

To be clearer:

  • Point is a struct defined by the C#/.net standard libraries and is passed by value.
  • Making a wrapper class and implementing all of Point's methods as call-throughs to Point is long and tedious.
  • Having a wrapper class with nothing but an implicit coversion to/from Point will not allow the above. It would have to be written as (Point)(map.player).x, which is clumsy.
8
  • Should that be X (not x)? The standard Point has X and Y, and c# is fussy about case...? Commented Jan 8, 2012 at 20:19
  • 1
    Also, is player a property or a field? Presumably a field here? (else it wouldn't compile, IIRC) Commented Jan 8, 2012 at 20:21
  • @MarcGravell Yes, it should be X. If it is a property or a field is what I am debating. I want to get the advantages of both. Commented Jan 8, 2012 at 20:25
  • Does your Point really have to have anything to do with the "standard" Point in the .NET libraries? Mutable struct are evil. Avoid them unless you have a very good reason. Commented Jan 8, 2012 at 20:28
  • @Jason It is just more convenient then implementing all the operators myself. Commented Jan 8, 2012 at 20:32

4 Answers 4

6

If I understand you right, you seem to want to get some flexibility by defining type-conversions. That won't go very far.

Avoid mutable structs (value types). struct Point is an unfortunate leftover from the old Win32 API.

So aim for map.player = new Point(5, map.player.y);

Sign up to request clarification or add additional context in comments.

1 Comment

@baruch: Everything :) Seriously, search for "evil mutable struct" and you'll find lots of opinions, for and against. Mostly, you end up with awkward situations like this, and unexpected behaviour in other situations.
4

No. Either player is a variable, in which case you don't get any chance to verify the change during the assignment, or player is a property, in which case the expression map.player is categorized as a value and you're not allowed to set the x part (as it would be usless).

I would suggest you create a method, so you can call:

map.SetPlayerX(5);

... or you make changing the x value part of a larger operation which is owned by the Player class. That's likely to be a more OO approach - usually if you find yourself wanting to do something like this, it's worth taking a look at the design and responsibilities.

Comments

1

1

What about assigning both coordinates at the same time?

map.player = new Point(5, 7);

Now you can have the validation logic inside of the player property.


2

Do you really need to implement all the stuff of System.Drawing.Point? It should not be all too difficult to implement your own version with only the things you really need. There is not much logic in Point.

Comments

1

Some people would suggest using a class type to hold the X and Y values. I am leery of that approach, and would suggest that in many cases the semantics obtained by exposing player as a field would be much cleaner (though I'm unclear why map has a member called player. If an map is reference to MapType, and instance of MapType has a field of struct type player, with a field x of type integer, it is clear that a change to map.player.x will affect one instance of MapType. Also, saying map1.player = map2.player; will copy the fields from map2.player to map1.player. By contrast, if player were of a class type and one did map1.player = map2.player; map1.player.x = 5;, that would affect both map1 and map2.

The question of whether to use a mutable struct versus a mutable class is pretty straightforward, but the notion of "If it's mutable make it a class" is just plain wrong. Simply ask yourself, given:

  thing1 = thing2;
  thing1.x = 5;

would you want the second statement to affect the value of thing2.x? If yes, use a class. If no, use a struct.

BTW, abother pattern to consider if you don't want your map type to expose player as a field would be to have it expose an ActOnPlayer method:

  public delegate ActionByRef<T>(ref T p);
  public delegate ActionByRef<T1,T2>(ref T1 p1, ref T2 p2);

  public void ActOnPlayer(ActionByRef<playerType> proc;)
  {
    proc(ref _player);
  }
  public void ActOnPlayer<T>(ActionByRef<playerType,T> proc, ref T param;)
  {
    proc(ref _player, ref param);
  }
... sample usage:
  map.ActOnPlayer((ref playerType it, ref int theParam) ->
    {it.x = theParam;}, someIntVariable);

Note that the latter approach would look something like using a closure, but unlike using a closure it would not generate any new temporary heap objects, and would thus not create any pressure on the garbage collector.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.