了解属性 C#

Understanding properties C#

我现在正在学习 C# 课程并学习基础知识。我一直在尝试理解属性以及 C# 中 get 和 set 的确切工作方式,但我一直让自己感到困惑。我想我开始明白了,但如果有人让我知道我是如何做到的,我将不胜感激right/wrong。

我将要 post 我正在复习的练习,关于将打印矩形的应用程序。练习的重点实际上是练习接口,但是最让我困惑的是属性

我在这里得到了矩形 class 的代码,并且我在一些陈述下面添加了注释,说明我认为这里发生的事情。如果有人有任何建议或指导,将不胜感激

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Interfaces_3
{
    class Rectangle : Scalable, Comparable
    {
        double width, height, scale = 1.0;

        public double Scale
        {
            get { return scale; }
            set { if (value > 0) { scale = value; } }
            /* the Scale property will take the scale field value 
             * will update the scale double in the Scalable interface,
             * but only if the value given to scale is positive*/
        }

        public double Width
        {
            get { return width * scale; }
            set { width = value; }
            /* using the value supplied by the user later,
             * width field will be set. this will then be multiplied by the scale 
             * field to get the value of Width property */
        }

        public double Height
        {
            get { return height * scale; }
            set { width = value; }
            /* height * scale will be read, and then the width field will
             * be set to the Height property? I think? */
        }

        public double Area
        {
            get { return Width * Height; }
            /* Width * Height is read 
             * there's no 'set' here because... not sure */
        }

        public Rectangle(double width, double height)
        //constructor
        {
            this.width = width;
            this.height = height;
        }

        public bool IsEqual(Object o)
        {
            if (o is Rectangle)
            {
                if (((Rectangle)o).Area == this.Area)
                /* if the Area of "o" is equal to
                 * the Area of the other specified object*/
                {
                    return true;
                }
            }
            return false;
        }

        public bool IsGreater(Object o)
        {
            if (o is Rectangle)
            {
                if (((Rectangle)o).Area < this.Area)
                /* if the Area of "o" is smaller than  
                 * the Area of the other specified object*/
                {
                    return true;
                }
            }
            return false;
        }

        public bool IsLess(Object o)
        {
            if (o is Rectangle)
            {
                if (((Rectangle)o).Area > this.Area)
                /* if the Area of "o" is larger than  
                 * the Area of the other specified object*/
                {
                    return true;
                }
            }
            return false;
        }

        public override string ToString()
        {
            string returnString = "";

            for (int i = 0; i < Math.Ceiling(this.Height); i++)
                /* while i is smaller than the value of Height,rounded up*/ 
            {
                for (int j = 0; j < Math.Ceiling(this.Width); j++)
                /* while j is smaller than the value of Width,rounded up*/
                {
                    Console.Write("/\");
                }
                Console.WriteLine();
            }
            return returnString;
        }
    }
}

实际上属性就是常规方法而已。

例如,让我们将 class 与 auto-属性:

public class ExampleClass {
  public int ExampleProperty {get;set;}
}

它会被编译成这样:

public class ExampleClass {
 private int _exampleProperty;
 public int get_ExampleProperty() { return _a; }
 public void set_ExampleProperty(int value) { _a = value; }
}

当您写 Console.WriteLine(exampleClass.ExampleProperty) 时,它表示 Console.WriteLine(exampleClass.get_ExampleProperty()),对于 exampleClass.ExampleProperty = 2,它表示 exampleClass.set_ExampleProperty(2)。因此,请将它们视为常规方法。

以及您的代码示例:

public class Rectangle {
 ...
 public double scale;
 public double Scale
 {
  get { return scale; }
  set { if (value > 0) { scale = value; } }
 }
 ...
}


会编译成:

public class Rectangle {
 ...
 public double scale
 public double get_Scale() { return scale; }
 public void set_Scale(double value) {
  if (value > 0) {
    scale = value
  }
 }
 ...
}

用法:

var rectangle = new Rectangle(2,2);
rectangle.Scale = 3; // set_Scale(3) call
Console.WriteLine(rectangle.Scale); // get_Scale() call

batangaming 很好地解释了这些属性在幕后是如何工作的。但我想就如何使用属性提供一些额外的建议。虽然您几乎可以在 属性 setter 或 getter 中执行任何操作,但重要的是要考虑用户的期望。例如

myRectangle.Scale = -1;

这将编译并且 运行 很好,但什么也不做,并且可能是一个错误。所以最好把 setter 写成

public double Scale
    {
        get { return scale; }
        set { 
             if (value > 0) { scale = value; }
             else{ throw new ArgumentException("Invalid scale value, must be positive");
          }
    }

这将确保发现并处理错误。

以同样的方式,如果我 运行 代码

myRectangle.Width = 10;
Console.WriteLine(myRectangle.Width.ToString());

我希望打印的值为 10,但如果比例 != 1 则情况并非如此。因此您可能希望将该值除以 setter.[=21 中的比例=]

您也可以考虑 mutability vs immutability。 IE。是否允许对象在创建后更改。我个人的偏好是尽可能使用不可变对象。这有很多优点:

  • 对象默认是线程安全的

  • classes(即引用类型)和结构(即值类型)的行为相同

  • 只需要在构造函数中验证

  • 它使对象更改的位置更加清晰。 IE。如果我将一个不可变对象赋予一个方法,我就不必担心该对象是否被该方法修改了。

  • 它往往使代码非常紧凑。例如:

    public class MyRectangle 
    {
      public double Width { get; }
      public double Height { get; }
      public double Area => Width * Height;
      public MyRectangle Scale(double scale) => new MyRectangle(Width * scale, Height * scale);
      public MyRectangle(double width, double height) => (Width, Height) = (width, height);
    }
    

我还建议研究 IEqualityComparer<T>IComparer<T>。您定义了 AreaComparer class 而不是 IsGreater 方法。这允许您以不同的方式比较对象,即你可以同时拥有 AreaComparerWidthComparer.