在C#中直接设置属性的属性

6

假设我有一个名为AvatarSize的公共属性,如下所示:

public class Foo
{
  ...

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

  ...
}

现在,如果目标类想要设置这个属性,那么他们需要按照以下方式进行操作:
myFoo.AvatarSize = new Size(20, 20);  //this is one possible way

但如果我尝试这样设置:
myFoo.AvatarSize.Height = 20;  //.NET style
myFoo.AvatarSize.Width = 20;  //error

编译器给我报错,说它无法修改返回值。我知道为什么会这样,但我希望它也支持第二种方式。请帮我想一个解决方案。

注:如果标题很荒谬,请原谅我。


请问您能具体说明为什么您希望它以这种方式工作吗?除了Petoj所说的,恐怕没有太多选择。 - Gerrie Schenck
请展示一下“Size”的外观。 - David Schmitt
大小是System.Drawing命名空间中的标准.Net值类型。 - bang
7个回答

10

Size是一个结构体。作为值类型,它是不可变的。如果你像这样改变它的属性,它只会修改堆栈中的对象实例,而不是实际的字段。在你的情况下,AvatarSize属性只能使用结构体构造函数来设置:new Size(20, 20)


实际上,不是不可变性使这成为问题 - 即使这是一个可变结构体,您仍将面临相同的问题,因为从属性返回的值将与变量本身分离。 - Jon Skeet
实际上,OP说他知道问题出在哪里。我猜他只是想在只需设置其中一个属性时减少输入量...所以,我的答案是将其包装在类中。我并不认为这是最好的做法,但如果你真的想要,它是可以做到的。 - Wayne Bloss

4
你想要做的唯一方法是定义。
public class MutableSize{
  public int Height{get;set;}
  public int Width{get;set;}
}

然后让AvatarSize返回其中之一,而不是返回Size。


4

这实际上是对Petoj的回答的扩展。我不会选择可变大小 - 在我看来,使事物可变会导致更难理解的代码。

相反,引入两个新属性:AvatarHeightAvatarWidth

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); }
  }

  ...
}

你仍然不能写成myFoo.AvatarSize.Width = 20,但你可以写成myFoo.AvatarWidth = 20。请注意,分别设置宽度和高度的效率比一次性设置它们要低。

顺便说一下,Windows Forms采用了这种方法。


2
你可以创建2个新属性myFoo.AvatarSizeWidth,使这些属性改变宽度,同样适用于高度。

我不想这样做。这并不能解决问题。myFoo.AvatarSize.Height = 20; //.NET风格 myFoo.AvatarSize.Width = 20; //错误这个问题 - Sudarsan Srinivasan
@Sudarsan:你误解了答案。它建议你写myFoo.AvatarSizeHeight = 20。 - Jon Skeet

2
唯一的方法是将您自己的Size类型作为类(引用)而不是结构体(值)来创建。
  public class Size
  {
    public Int32 Width;
    public Int32 Height;
  }

但这当然会使Size失去一些作为值类型的优势...

1

这些 Size 对象是否具有 Height 和 Width 的可写属性?还是只读的?


不,大小是不可变的。你无法更改其任何值。这就是编译器抱怨的原因。 - R. Martinho Fernandes

1
如果您想将结构体视为类,则必须创建一个包装器类,该类具有对结构体类型的隐式转换。下面是包装器类(名为Size,您可以随意更改名称,这并不重要),具有Size属性的Class1类,以及在Form1类中如何使用它们的示例:
编辑:我更新了Size类,添加了反向转换(从System.Drawing.Size到此Size类)和一些注释。
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);


    }
}

如果是这种情况,那么我该如何处理类型为IList<string>的属性的Add()方法呢?也就是说,我该如何允许使用myFoo.AvatarList = new IList<string>()以及myFoo.AvatarList.Add("foo")?如果我必须按照您的方式进行操作,那么这不是很繁琐吗? - Sudarsan Srinivasan
我认为Size的解决方案也应该解决IList<string>的问题。我认为这些问题是相同的。 - Sudarsan Srinivasan
请说明“如何允许myFoo.AvatarList = new IList<string>()”。我不理解,因为您不能执行“new IList<string>()”,因为IList是一个接口。 - Wayne Bloss
我认为你在谈论两件不同的事情。在你的myFoo.AvatarSize属性中,AvatarSize的类型是System.Drawing.Size,对吗?而AvatarList的类型是IList<T>,是吗?如果是这样的话,那么它们是不同的例子,因为一个是值类型,另一个是类引用。 - Wayne Bloss
你应该为你的其他问题提出一个新的问题,这样它才能得到适当的关注。否则,你应该编辑你的原始问题,并发布一个清晰的示例来说明你后来遇到的问题是什么。 - Wayne Bloss
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接