初始化类的意义是什么?

10

我正在阅读一本关于C#初学者的书,目前来到了“理解值和引用”的部分,但有些东西我不太明白。我看到书上(以及在YouTube上的一些教学视频中)试图向我解释,类被用来创建……一个类的对象(??)。我已经读完了之前发生这种情况的整个章节,但并没有完全理解它,认为在接下来的章节中会更加清晰。然而并没有变得更加清晰,所以我认为在理解我之前讲解的概念之前继续阅读是不明智的。

以下是书中的一部分:

请记住,在初始化诸如类这样的引用变量时,您可以创建该类的新实例并将引用变量分配给新对象,如下所示:

Circle c = new Circle(42);
Circle copy = new Circle(99);
//Circle refc = c;
...
copy = c;

这个示例代码有什么用处?它可以做什么?请给出相关的示例和解释。提前谢谢!


3
这是什么书?它将被列入我的“不建议新手阅读的书”列表。我甚至看不出他们可能想表达什么。术语只是因为那个作者的滥用而失去了意义。 - harold
1
Visual C# 2010 - 逐步学习 - DutchLearner
引用是指内存中的一个点,例如您有一个名为Aclass的类,当您实例化它时Myclass a = new Myclass(),将保留一些内存单元(例如1000、1001、1002、1003),当您将此对象(a)与同一类的另一个对象进行比较Myclass b = new Myclass()时,将比较内存位置,它们保存的数据不重要,a和b都可以包含“1”作为数据,但它们不相等(谈论内存位置)。 - Mahdi Tahsildari
但对于值类型来说,情况就不同了。当比较两个值类型时,它们的数据是比较的基础,因此 int a = 10; int b = 10; bool isEqual = a == b; //isEqual = true - Mahdi Tahsildari
11个回答

9
听起来,你还没有完全理解书中的解释:
以下内容看起来是制作两个大小为3和4的圆,并复制第一个并将其变成5.. 但是当你打印它时,它并不是这样工作的。
class Program
{
    static void Main(string[] args)
    {

        circle a = new circle(3);
        circle b = new circle(4);
        circle d = a;
        d.Diameter = 5;

        Console.WriteLine("a is {0}", a.Diameter); // shows 5
        Console.WriteLine("b is {0}", b.Diameter); // shows 4
        Console.WriteLine("d is {0}", d.Diameter); // shows 5
    }
}

class circle
{
    public int Diameter;
    public circle(int d)
    {
        Diameter = d;
    }
}

因为你没有为d创建一个新的圆圈,实际上d是a的别名,就像有人的名字是Peter,他也可以被称为Pete。


5
请记住,要初始化类等引用变量,可以创建该类的新实例并将引用变量分配给新对象[sic] 他展示了引用类型和值类型之间的区别。对于值类型,它已经被初始化。 double d; 你无需做更多事情。但是对于类或引用类型,您必须为其提供一个对象实例。 Circle c; 还没有被分配对象。 d = c.Radius 错误。引用不指向对象。内存访问冲突。 Circle c = new Circle(); 现在有了一个对象。

“Circle c;” 没有被赋值,它的值是 “null”。引用类型和值类型之间的区别远不止它们如何初始化这么简单;这只是最终结果中非常微小的一部分。 - Servy
@Servy,我从未说过它没有价值(如果你称null为一个值的话)。你在拿细节纠结。我说过,“它还没被分配到一个对象。” 我回答了问题,也就是引用类型需要手动将对象分配给它们。你还想要我做什么?写一篇关于引用类型和值类型区别的论文吗? 不要这么迂腐了。 - Lee Louviere

4

嗯,有时候有可能很方便,但不总是必要的,可以将新对象位置分配给类型引用。在这种情况下,在最后一行 copy = c; 中,您说 copy 现在指向先前由 c 分配的内存位置,换句话说,它们两个在该行之后指向相同的内存位置。


你为什么要这样做呢:Circle c = new Circle()??这段代码的含义是什么,它可以用在哪里? - DutchLearner
@DutchLearner:我认为这只是一个示例,压缩成几行代码,以便让人们了解作者在谈论什么。但它并没有提供真正的可用性。真正的可用性可以在实际程序和实际代码库中确定。 - Tigran
是的,我认为它只是试图解释堆栈上的引用对象。给出的例子有点傻,这种方法的实用性肯定存在于某个地方,只是不太常见。 - ledgeJumper
这种情况相当普遍。我的意思是,经常会因为各种原因切换对象的引用,但不是像所解释的那样。但我认为,在几行文字中解释它的真正用途也很困难,这可能是个问题。 - Tigran
1
Field a = new Field(); Field b = new Field(); Field focused = a; ... b_OnClick(...) { focused = b; } - Lee Louviere
好的,但是为什么我想要这样做呢?脑海中可能会浮现出许多例子,为了让读者清楚地知道为什么他必须使用它,这并不容易,以我个人的看法。 - Tigran

3
这里需要一个关于面向对象编程的速成课,但您现在所需的是对OO编程的理解,这是一种编程范例,许多编程语言都有,当然包括C#。您在帖子中描述的问题如下:在OO编程中,您编写由字段和方法(大致)组成的类。这些类具有静态特性,并且在应用程序的开发期间存在(请不要混淆我在此处使用的“静态”一词和关键字“static”)。在应用程序运行时,您可以使用这些静态类来创建这些类的对象,这意味着特定类作为任意数量的相同对象的建筑计划。与类不同,这些对象也称为类的实例,并且仅在应用程序运行时存在。对象是动态的。简而言之,当您拥有一个类时...
class House {

// ...
}

你有一个静态的房子描述,可以用来创建动态的对象,比如房子。你可以使用所谓的“new运算符”(在C#和其他现代语言中,如Java)来实现这一点:
House house = new House();

你在这里做的是声明一个类型为House的变量。这个类型是你用class House定义的。具体来说,就是这一部分:
House house;

目前,它只是一个名为“House”的变量,什么也不做。这里重要的术语是它指向了空值,这个空值被称为null

接下来,您可以使用以下语法创建此类的实例(动态对象):

house = new House();

现在你已经创建了一个对象,并让你的变量house指向这个对象。从现在开始,你可以用变量house来引用这个对象。
我告诉你这个是为了指出你在上面的帖子中提到的一个重要问题:值类型和引用类型之间的区别。
在前面的行中,你所做的是创建一个指向对象的新变量。这被称为引用类型(再次粗略解释)。相比之下,你有像int, byte, short等原始类型。
使用
int i = 4;

你再次声明一个变量。但这一次,变量直接保存它的值而不是指向它。这被称为值类型

值类型和引用类型的区别非常重要,例如当你将这些变量作为参数传递给方法时。


2

我会尝试修正这个引用,因为它太糟糕了。

原文:

请记住,要初始化类似类的引用变量,您可以创建类的新实例并将引用变量分配给新对象,例如:

修改:

请记住,要初始化引用类型的变量,您可以创建该变量类型的新实例并将变量更改为指向新对象,例如:

虽然我不知道为什么这应该是您需要记住的事情。是的,您可以创建对同一对象的几个引用,并且它本身可能很有用,但对我来说,它不是一个特别有用的初始化策略。

更好地解释:

您可以使引用类型的变量引用同一实例,但请记住,变量本身不是别名,因此 覆盖 其中一个不会更改另一个。


1

你可以将类视为数据的模式,就像规范或蓝图一样。当你使用“new”关键字时,你告诉计算机使用该蓝图为你创建一个对象。一个对象是该类的具体实例。

因此,如果你有一个蓝图(类定义),你可以调用 new 多次,以产生任意数量的该类实例。就像如果你有一辆汽车的蓝图,你可以制作出任意数量的该汽车副本(只要有足够的材料)。

所以做:

Circle c = new Circle(42);
Circle copy = new Circle(99);

告诉计算机使用Circle类定义和实例化两个对象(因此您在计算机的内存中有两个存在的圆)。 这些是具有不同属性的不同对象(一个半径为42,另一个为99)。 它们被分配到变量c和copy中。

您代码的最后一行 copy = c; 将由变量c指向的圆放入变量copy中。


1
类定义是蓝图。可以将其视为房子。每次实例化一个对象Circle c = new Circle(42),您都在使用该蓝图构建一座房子。每座房子都有一个街道地址(内存中的地址)。
您拥有的每个变量都像一张纸条,列出了房子的地址。因此,您在123 main street建造了一座蓝色的两层楼房,并在一张纸上写下了123 main street并标记为“A”。接下来,您在456 King Street建造了一座红色的四层楼房,并在一张纸上写下了该地址并标记为“B”。接下来,您拿一张纸条并写下第一座房子的地址(123 Main Street),并将该纸条标记为“C”。
然后,您去找一位画家,请他把房子涂成黄色。您给他纸条“C”。
在我们的示例中只有两座房子。由于C和A“指向”相同的地址,它们现在指向一座黄色的房子。
House a = new House( blue, 2 );
House b = new House( red, 4 );
House c = a;
c.Color = yellow;
//Now a.Color is yellow as well

1
一个变量不是对象,它只是对对象的引用。因此,在下面的例子中,有两个变量都引用同一个对象:
Label label1 = new Label();
Label label2 = label1;
label1.Text = "1";
label2.Text = "2";

执行该代码后,您会发现label1.Text等于"2"而不是"1"。这是因为它们都引用同一个Label对象,所以当您设置label2.Text时,它将对两个变量进行更改。但是,如果您实例化了两个单独的标签对象,则结果将不同,例如:
Label label1 = new Label();
Label label2 = new Label();
label1.Text = "1";
label2.Text = "2";

在这个第二个例子中,每个变量都指向不同的Label对象(即Label类的不同实例)。因此,在运行此代码后,label1.Text将等于“1”,label2.Text将等于“2”,正如您所期望的那样。
这两个选项之所以重要且可用,有很好的理由。例如,假设您想要创建一个设置LabelText属性的方法,例如:
void SetLabelText(Label labelToSet)
{
    labelToSet.Text = "text";
}

你可以这样调用该方法:

Label label1 = new Label();
SetLabelText(label1);

在这种情况下,SetLabelText 方法中的 labelToSet 变量将引用与 label1 变量相同的对象,因此当 'SetLabelTextlabelToSet.Text 设置为 "text" 时,它不会创建一个新的 Label,而是只是在传递给方法的现有 Label 对象上设置文本,这正是您想要发生的事情。
由于任何变量都可以设置为新对象或现有对象,因此在分配给对象之前,它被视为“null”。正如我一开始所说,变量不是对象,它只是对象的引用。如果变量根本没有引用任何对象(它的初始状态),则它是null,如果尝试使用它,则会抛出异常。例如:
Label label1; 
label1.Text = "1";  // Throws a null reference exception

你可以声明任意数量的变量,但在实例化至少一个对象并将其设置为该对象之前,它们都将是null。要实例化一个对象(即创建一个类的新实例),必须使用new关键字(例如:new Label())。

然而,到目前为止我所说的一切只适用于“引用类型”(类)。这不适用于“值类型”(结构体)。当您将变量声明为值类型时,从实际目的来看,它实际上就是对象。许多简单的数据类型,如int,都是值类型。例如:

int x;
int y;
x = 1;
y = x;
y = 2;

运行以上代码后,x 的值将等于 1y 的值将等于 2。设置 y = x 不会导致 y 引用与 x 相同的对象。相反,它会将值从 x 复制到 y,从而创建一个新的值类型对象。

0
如果我们在这个上下文中提到人类,那么类定义了什么是人类以及它能做什么(例如它的成员如身高、宽度等,以及它的方法如eat()、drink()等),而对象则代表实际的人类(一个名叫安德鲁的人,他有身高、宽度,可以吃和喝)。

0

如果我理解有误,请纠正我,但是当我看代码时,我看到两个对象被创建并指向它们自己的内存位置中的一个值。

copy = c; 这一行只是说明复制对象不会指向 c 对象的内存位置,而是指向它最初设置的那个位置。


@BugFinder赢得了最准确和易懂的答案。 - ledgeJumper

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