简体   繁体   中英

Understanding properties C#

I'm doing a C# course now and going through the basics. I've been trying to understand properties and how exactly get and set work in C# but I keep confusing myself. I'm starting to get it I think, but I'd appreciate it if someone let me know how right/wrong I've got it.

I'm going to post an exercise I'm reviewing, about an application that will print rectangles. The point of the exercise is actually practicing with interfaces, but properties are the thing that is confusing me most of all

I've got the code for the Rectangle class here and I've put comments under some of the statements saying what I think is happening here. If anyone has any advice or guidance it would be greatly appreciated

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

Actually properties are the regular methods and that's all.

For example, let's take class with auto-property:

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

it will be compiled into something like this:

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

When you write Console.WriteLine(exampleClass.ExampleProperty) it means Console.WriteLine(exampleClass.get_ExampleProperty()) and for exampleClass.ExampleProperty = 2 it means exampleClass.set_ExampleProperty(2) . So think of them just like of the regular methods.

And example from your code:

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


It will be compiled into:

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

Usage:

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

batangaming provides a good explanation on how the properties work under the hood. But I wanted to provide some additional recommendations on how to use properties. While you can do just about anything inside a property setter or getter it is important to consider the users expecations. For example

myRectangle.Scale = -1;

This will compile and run just fine, but will do nothing, and is probably an error. So it might be better to write the setter as

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

This will ensure the error is noticed and handled.

In the same way if I run the code

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

I would expect that the printed value to be 10, but that is not the case if scale.= 1. So you might want to divide the value with the scale in the setter.

You might also consider mutability vs immutability . Ie if objects are allowed to change after creation or not. My personal preference is to use immutable objects whenever possible. This has a number of advantages:

  • Objects are threadsafe per default

  • The behavior is identical for both classes (ie reference types) and structs (ie value types)

  • Validation is only needed in the constructor

  • It makes it clearer where objects change. Ie if I give a immutable object to a method I do not have to worry if the object is modified or not by the method.

  • It tend to make the code quite compact. For example:

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

I would also suggest looking into IEqualityComparer<T> and IComparer<T> . Instead of having a IsGreater method you define a AreaComparer class. This allow you to compare objects in different ways, Ie you can have both an AreaComparer and an WidthComparer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM