如何在 C# 中正确封装以便 Main 无法更改值

How to encapsulate properly in C# so Main cannot change values

关于 C# 中的封装,我有一个看似简单的问题。

在我们的一项学校作业中,我们应该创建一个简单的二维坐标计算系统,并正确封装文件,以便我们无法从 Main 方法编辑点的位置或长度。

我创建了两个 C# 文件,即 Line 和 Point。文件 Program.cs 已提供给我们,因此我们应该解决这个问题,并且不允许我们更改该文件中的任何代码。我们应该封装的文件是 Line.cs 或 Point.cs 文件,因此 Main 方法无法通过编写例如从那里编辑点的位置p1.X = 0;。 以下是我到目前为止编写的文件:

Program.cs

using System;
using coordinationSystem;
class Program
{
    public static void Main()
    {
        Point p0 = new Point();
        Point p1 = new Point(-10, -10);
        Console.WriteLine("Point p0, position = (" + p0.X + ", " + p0.Y + ")");
        Console.WriteLine("Point p1, position = (" + p1.X + ", " + p1.Y + ")");

        Line theLine = new Line(p0, p1); //This is the correct line.
        outdata("Print 1 of the line", theLine);

        p1.X = 0; //This is the thing I'm trying to prevent...
        outdata("Print 2 of the line", theLine);

        Point startposition = theLine.Position();
        startposition.Y = 5; //... same with this one
        outdata("Print 3 of the line", theLine);
    }
    private static void outdata(string text, Line theLine)
    {
        double length = theLine.Length();
        Point startPos = theLine.Position();
        Console.WriteLine("\n" + text);
        Console.WriteLine("==================================");
        Console.WriteLine("Length = {0 :f4} units", length);
        Console.WriteLine("Position = ({0},{1})", startPos.X, startPos.Y);
        Console.WriteLine("----------------------------------");
    }
}

Point.cs

namespace coordinationSystem
{
    public class Point
    {
        private int x, y;

        public Point() : this(0, 0) { } //Default-constructor.

        public Point(int X, int Y) //Constructor.
        {
            this.X = X;
            this.Y = Y;
        }

        public int X //Property for the X-coordinate.
        {
            get { return x; }
            set
            {
                x = value;
            }
        }

        public int Y //Property for the Y-coordinate.
        {
            get { return y; }
            set
            {
                y = value;
            }
        }
    }
}

Line.cs

using System;

namespace coordinationSystem
{
    public class Line
    {
        private Point p0;
        private Point p1;

        public Line(Point p0, Point p1)
        {
            this.p0 = p0;
            this.p1 = p1;
        }
        public double Length()
        {
            double pointLength = Math.Sqrt(Math.Pow(p0.X - p1.X, 2) + Math.Pow(p0.Y - p1.Y, 2));
            return pointLength;
        }
        public Point Position()
        {
            return p0;
        }
    }
}

我得出的结论是我可能应该使用 getset,但这对我来说没有用,因为 Main 中的 theLine.Position(); 是一种方法(并且正如我之前写的,我们根本不应该编辑程序。cs/Main)。

那么这里有什么想法吗?任何帮助将不胜感激!

编辑:

这是作业文本(从瑞典语大致翻译而来):

Consider whether it is possible to sabotage the line if you have access to references to the lines both endpoints. What happens, for example, with the line if you would change the p0 or p1 in the Main method after you have created the line? What happens to the line when you move the starting position in the Main method? Modify the code in the class Line and Point so that the above problems are avoided.

Modify the code in the class Line and Point so that the above problems are avoided.

为了避免从Main方法访问的问题,最好在编译时阻止,所以需要修改如下。

对属性使用 private set :

public int X //Property for the X-coordinate.
{
    get { return x; }
    private set
    {
        x = value;
    }
}

public int Y //Property for the Y-coordinate.
{
    get { return y; }
    private set
    {
        y = value;
    }
}

由于您无法更改 Main() 中明显损坏的代码,因此您基本上有两个选择:

  1. set 访问器中抛出异常。
  2. set 访问器中不执行任何操作(即,实际上不更改值)。

看起来像这样

public int X {
    get {
        return x;
    }
    set {
        // do nothing
    }
}

或:

public int X {
    get {
        return x;
    }
    set {
        throw new Exception("Cannot change value of X");
    }
}

您可以对 Y 和其他属性执行相同的操作。

好吧,这个问题有多种解决方案,这里有两个:

1-将点转换为结构。

结构是按值传递的,而不是按引用传递的,所以当你在方法上 return p0 时,它将是一个副本,如果有人更改 returned 点,原始行将保持不变.

2-复制点。

不是直接returning p0,只是return new Point(p0.X, p0.Y),它会和1[=11=有相同的结果]

无论如何,试着让你的老师被解雇,她不懂 C#,她懂 Java 因此她不明白属性是如何工作的,因为任何 C# 程序员都会使用属性而不是函数来检索来自更高 class 的属性,例如 Line.

编辑:如果您使用方法 2,则还要克隆 Line 的构造函数中使用的点:

    public Line(Point p0, Point p1)
    {
        this.p0 = new Point(p0.X, p0.Y);
        this.p1 = new Point(p1.X, p0.Y);
    }

使用与 y 相同的方法 属性。

publicclass点 { 私人 int x, y;

    public Point() : this(0, 0) { } //Default-constructor.

    public Point(int X, int Y) //Constructor.
    {
        //this.X = X;
        //this.Y = Y;
        setx(X);
        //this.y = Y;
    }

    public int X //Property for the X-coordinate.
    {
        get { return x; }
        private set
        {
            x = value;
        }

    }

    public int Y //Property for the Y-coordinate.
    {
        get { return y; }
    }

    public int setx(int Xvalue)
    {
        return x = Xvalue;
    }
}