1

Potentially an argumentative topic, but... I really hate that I can't do the following:

class User 
{
   public string ImageURL {get;set;}
}

class UserUI : User 
{
   public Brush ImageBrush {get;set;}
}

User user = GetFromUserFromServer(); //returns User type object, not UserUI
UserUI userUI = user;
userUI.ImageBrush = CreateBrush(userUI.ImageURL);

Instead, I use something like following:

class User 
{
   public virtual string ImageURL {get;set;}
}

class UserUI : User 
{
   public override string ImageURL 
    {
        get
        {
            return base.ImageURL ;
        }
        set
        {
            if (value != base.ImageURL )
            {                    
                base.ImageURL = value;
                ImageBrush = CreateBrush(value);
            }
        }
    }

   public Brush ImageBrush {get;set;}

   public UserUI(User user)
   {
       ImageURL = user.ImageURL;
   }
}

User user = GetFromServer();
UserUI userUI = new UserUI(user);

It's a lot more verbose and any time I add fields to User, I need to make sure to copy them over in the UserUI constructor.

Is it fair to say that allowing down-casting would make all of this a lot easier and less error-prone?

Jamona Mican
  • 1,574
  • 1
  • 23
  • 54
  • 2
    I think this would be a more acceptable question if you phrased it as "Is there an easier/better way to do this, since downcasting isn't allowed in C#?" – FishBasketGordo Aug 09 '12 at 17:52
  • 2
    `UserUI userUI = user` is so wrong and against polymorphism. – Ankush Aug 09 '12 at 17:52
  • 3
    You can't safely downcast. In your code your doing it because you know the rules to convert the different objects. You can always say that Child **is** Base, but the opposite is not always true. – Andre Calil Aug 09 '12 at 17:53
  • How would it be less error prone? It might make this case less error prone, but in general I don't see this feature doing that. – Rangoric Aug 09 '12 at 17:57
  • You are right Fish. I have changed it to be less offensive :) – Jamona Mican Aug 09 '12 at 17:59
  • http://en.wikipedia.org/wiki/Liskov_substitution_principle#A_typical_violation – Hans Passant Aug 09 '12 at 18:02

4 Answers4

5

Err...no. What do you think should happen when you do this, after all, this is just as valid as your example:

string whatever = "";
UserUI userUI = whatever;
userUI.ImageBrush = CreateBrush(userUI.ImageURL);

After all, both inherit from object. Does userUI.ImageURL throw an exception since that property doesn't exist? Or...what? This is madness. :)

Edit: You say in a comment above:

"Yeah. My argument is that there should maybe be a way of casting to a derived type, and the framework would automatically copy fields that exist in both types, and leave the others empty. In this case ImageURL would be copied.

If that's what you want, then use Automapper: https://github.com/AutoMapper/AutoMapper/wiki

aquinas
  • 23,318
  • 5
  • 58
  • 81
  • You could just create a blank UserUI and copy any fields that match type + name! So in this case you would get an empty UserUI since object and UserUI have no matching fields. I see what you are saying though, it could lead to mistakes. Maybe what is needed is an explicit command to do this. – Jamona Mican Aug 09 '12 at 17:56
  • It might be a more comparable example if you said `object whatever = new object();` since object is a direct ancestor class of UserUi, but string is not. Agree that it's madness though. – hatchet - done with SOverflow Aug 09 '12 at 17:56
  • @hatchet If you can downcast any class to any other, and C# already lets you upcast to any other, then it actually does mean you would be able to implicitly convert anything to anything. It would implicitly cast to object (which is always valid) and then implicitly downcast to what you are assigning it to. – Servy Aug 09 '12 at 17:58
  • @Servy - I would call that a sideways cast. Downcasting is the act of casting a reference of a base class to one of its derived classes. http://en.wikipedia.org/wiki/Downcasting – hatchet - done with SOverflow Aug 09 '12 at 18:01
  • @hatchet Neither I, nor aquinas, said that the code he provided is a downcast. We are simply saying that if implicity downcasting was implemented this would be valid C# code that would need to have some way of compiling. It's not a downcast, it's two casts (one up and one down). – Servy Aug 09 '12 at 18:03
4

If you read up on the Liskov substitution principle you find that if you need down casting then your design is flawed.

NominSim
  • 8,447
  • 3
  • 28
  • 38
3

This is a good case where you should reference the phrase, "Favor composition over inheritance". Rather than having UserUI inherit from User, have it contain a user:

public class UserUI
{
  public User MyUser {get; set;} //or have it set in just a constructor, if that makes more sense
  public Brush ImageBrush {get;set;}
}

Then rather than setting a User to a UserUI you merely add an existing User to a UserUI.

User someUser = ...;
UserUI = someUserUI = new SomeUserUI();
someUserUI.MyUser = someUser;
Servy
  • 202,030
  • 26
  • 332
  • 449
0

If the object returned from GetFromServer() is in fact a UserUI object, then you can downcast with no problem:

User user = GetFromServer();
UserUI userUI = (UserUI) user; // or: UserUI userUI = user as UserUI;

If the user reference is not actually a UserUI object, then there's nothing to downcast to (so the cast will throw an exception). If that's your situation, then you need to create a new UserUI object based on the original User object, which is what you're doing and is quite different than downcasting.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • Yeah. My argument is that there should maybe be a way of casting to a derived type, and the framework would automatically copy fields that exist in both types, and leave the others empty. In this case ImageURL would be copied. – Jamona Mican Aug 09 '12 at 18:04
  • @HarryMexican, see my edited answer. Basically, it sounds like you want AutoMapper. – aquinas Aug 09 '12 at 18:08
  • Cool aquinas. That's exactly what I need. Wished I realized it had existed a couple months ago! Will accept your answer.. – Jamona Mican Aug 09 '12 at 18:18
  • Actually the automapper requires me to redefine all the User fields in UserUI, so still some boilerplate and not ideal, but the cleanest solution it seems. – Jamona Mican Aug 09 '12 at 19:55