views:

545

answers:

7

Lets consider that I have a public property called AvatarSize like the following,

public class Foo
{
  ...

  public Size AvatarSize
  {
    get { return myAvatarSize; }
    set { myAvatarSize = value; }
  }

  ...
}

Now if a target class wants to set this property, then they need to do it the following way,

myFoo.AvatarSize = new Size(20, 20);  //this is one possible way

But if I try to set it like this,

myFoo.AvatarSize.Height = 20;  //.NET style
myFoo.AvatarSize.Width = 20;  //error

the compiler get me an error stating that it cannot modify the return values. I know why it happens, but I would like it to support the second way also. Please help me with a solution.

P.S. Sorry if the title is absurd

+2  A: 

You could create 2 new properties myFoo.AvatarSizeWidth and make this property change the Width same goes for Height..

Petoj
I don't wanna have it that way. It will not solve, myFoo.AvatarSize.Height = 20; //.NET stylemyFoo.AvatarSize.Width = 20; //errorthis issue
@Sudarsan: You've misunderstood the answer. It's suggesting you'd write myFoo.AvatarSizeHeight = 20.
Jon Skeet
+1  A: 

Does these Size object have write properties for Height and Width? Or are they readonly?

Fortyrunner
They have write properties
No. Size is imutable. You cannot change any of its values. That's why the compiler moans.
Martinho Fernandes
+10  A: 

Size is a structure. Being a ValueType it's immutable. If you change it's property like that, it will only modify an object instance in the stack, not the actual field. In your case AvatarSize property can only be set using a struct constructor: new Size(20, 20).

DreamSonic
It's not actually the immutability that makes this a problem - even if this were a mutable struct, you'd still have the same issue because the value returned from the property would be disassociated from the variable itself.
Jon Skeet
Actually, the OP said that he knows why the problem occurs. I assume that he was just looking to do less typing when he only has to set one of the properties... So, my answer was to wrap it in a class. I don't really think it's the best idea to actually do, but if you really want to, it can be done.
wizlb
+4  A: 

The only way you can do what you want is by defining

public class MutableSize{
  public int Height{get;set;}
  public int Width{get;set;}
}

and then having AvatarSize return one of those instead of Size.

Rasmus Faber
+2  A: 

The only way is to make your own Size type as a class (reference) instead of struct (value).

  public class Size
  {
    public Int32 Width;
    public Int32 Height;
  }

But this will of course make the Size loose some of the advantages of being a value type...

bang
+1  A: 

If you want to treat a struct like a class, you have to make a wrapper class that has an implicit conversion to the struct type. Below you can see the wrapper class (named Size, you can name it whatever you want, it doesn't matter), the Class1 class that has a Size property and then an example of how it's all used in the Form1 class:

EDIT: I updated the Size class with the inverse conversion (from System.Drawing.Size to this Size class) and some comments.

public class Size
{
    public Size(){}

    public Size(int width, int height)
    {
     this.Width = width;
     this.Height = height;
    }

    public int Width { get; set; }

    public int Height { get; set; }

    static public implicit operator System.Drawing.Size(Size convertMe)
    {
       return new System.Drawing.Size(convertMe.Width, convertMe.Height);
    }

    static public implicit operator Size(System.Drawing.Size convertMe)
    {
       return new Size(convertMe.Width, convertMe.Height);
    }
}

class Class1
{
    public Class1()
    {
     this.TheSize = new Size();
    }

    public Size TheSize { get; set; }
}

public partial class Form1 : Form
{
    public Form1()
    {
     InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
     /// Style 1.
     Class1 a = new Class1();

     a.TheSize = new Size(5, 10);

     /// Style 2.
     Class1 c = new Class1();

     c.TheSize.Width = 400;
     c.TheSize.Height = 800;

     /// The conversion from our Size to System.Drawing.Size
     this.Size = c.TheSize;

     Class1 b = new Class1();

     /// The opposite conversion
     b.TheSize = this.Size;

     Debug.Print("b Size: {0} x {1}", b.TheSize.Width, b.TheSize.Height);


    }
}
wizlb
If this is the casse, then how do I handle the Add() for a property of type IList<string> ?? i.e., how do I allow the usage myFoo.AvatarList = new IList<string>() also myFoo.AvatarList.Add("foo"); If I had to follow your way, then it becomes tedious isn't it ??
I'm sorry I don't see how that relates to the original question. Your question was about the AvatarSize property, there was nothing about a list.
wizlb
See the Form1 class in my code, it does everything you asked for.
wizlb
I think the solution for the Size should also answer the problem with IList<string>. I think the problems are the same.
Please explain "how do I allow myFoo.AvatarList = new IList<string>()". I don't understand because you cannot do "new IList<string>()" because IList is an interface.
wizlb
I think you are talking about 2 different things. In your myFoo.AvatarSize property, the Type for AvatarSize is System.Drawing.Size is it not? And the Type for AvatarList is IList<T> then? If that's the case, then it's different examples because one is a value type and one is an class reference.
wizlb
Hmm. If thats the issue, Ok. Noew. For the later what may be the solution ?? Please share it if you have one.
You should ask a new Question for your other issue so it gets the proper attention. Otherwise, you should edit your original question and post a clear example of what your later issue is.
wizlb
+4  A: 

This is really just an expansion of Petoj's answer. I wouldn't go for a mutable size - making things mutable leads to harder to understand code in my view.

Instead, introduce two new properties, AvatarHeight and AvatarWidth:

public class Foo
{
  ...

  public Size AvatarSize
  {
    get { return myAvatarSize; }
    set { myAvatarSize = value; }
  }

  public int AvatarHeight
  {
    get { return AvatarSize.Height; }
    set { AvatarSize = new Size(AvatarWidth, value); }
  }

  public int AvatarWidth
  {
    get { return AvatarSize.Width; }
    set { AvatarSize = new Size(value, AvatarHeight); }
  }

  ...
}

You still can't write myFoo.AvatarSize.Width = 20 but you can write myFoo.AvatarWidth = 20. Note that setting the width and height separately will be less efficient than setting them in one go.

This is the approach Windows Forms takes, by the way.

Jon Skeet