【问题标题】:Setting property's property directly in C#直接在 C# 中设置属性的属性
【发布时间】:2009-02-04 08:11:08
【问题描述】:

假设我有一个名为 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所说的,恐怕没有太多选择。
  • 请展示“尺寸”的样子。
  • Size 是 System.Drawing 命名空间中的标准 .Net 值类型。

标签: c# .net properties


【解决方案1】:

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

【讨论】:

  • 实际上并不是不变性造成了这个问题——即使这是一个可变结构,你仍然会遇到同样的问题,因为从属性返回的值将与变量本身解除关联。
  • 其实OP说他知道为什么会出现这个问题。我假设他只是想在只需要设置一个属性时减少打字......所以,我的答案是将它包装在一个类中。我真的不认为这是最好的主意,但如果你真的想这样做,它是可以做到的。
【解决方案2】:

你可以做你想做的事情的唯一方法就是定义

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

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

【讨论】:

    【解决方案3】:

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

    相反,引入两个新属性,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 窗体采用的方法。

    【讨论】:

      【解决方案4】:

      您可以创建 2 个新属性 myFoo.AvatarSizeWidth 并让此属性更改 Width 与 Height 相同..

      【讨论】:

      • 我不想这样。解决不了,myFoo.AvatarSize.Height = 20; //.NET 样式 myFoo.AvatarSize.Width = 20; //错误这个问题
      • @Sudarsan:你误解了答案。它建议你写 myFoo.AvatarSizeHeight = 20。
      【解决方案5】:

      唯一的方法是将自己的 Size 类型作为类(引用)而不是结构(值)。

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

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

      【讨论】:

        【解决方案6】:

        这些 Size 对象是否具有高度和宽度的写入属性?还是它们是只读的?

        【讨论】:

        • 没有。大小是不可变的。您不能更改其任何值。这就是编译器抱怨的原因。
        【解决方案7】:

        如果你想把一个结构体当作一个类,你必须创建一个封装类,它可以隐式转换为结构体类型。下面你可以看到包装类(命名为Size,你可以随意命名,没关系),具有Size属性的Class1类,然后是Form1类中如何使用的示例:

        编辑:我用逆转换(从 System.Drawing.Size 到这个 Size 类)和一些 cmets 更新了 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 类型的属性的 Add() ?即,我如何允许使用 myFoo.AvatarList = new IList() 也 myFoo.AvatarList.Add("foo");如果我必须按照你的方式,那它变得乏味不是吗??
        • 很抱歉,我看不出这与原始问题有何关系。您的问题是关于 AvatarSize 属性的,没有关于列表的内容。
        • 查看我的代码中的 Form1 类,它可以满足您的所有要求。
        • 我认为 Size 的解决方案也应该解决 IList 的问题。我认为问题是一样的。
        • 请解释“我如何允许 myFoo.AvatarList = new IList()”。我不明白,因为您不能执行“new IList()”,因为 IList 是一个接口。
        猜你喜欢
        • 1970-01-01
        • 2016-09-12
        • 2011-12-16
        • 1970-01-01
        • 2017-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多