繁体   English   中英

在C#中创建一个不包含状态的对象实例时会发生什么?

[英]What happens when you create an instance of an object containing no state in C#?

我认为在算法编程方面没问题,如果这是正确的术语? 我曾经在20世纪80年代作为业余爱好使用turbo pascal和8086汇编语言。 但是只有非常小的项目,而且从那以后的20年里,我还没有真正做过任何编程。 因此,我正像溺水的游泳运动员一样努力去理解。

所以也许这是一个非常非常的问题,或者我根本没有任何意义,但是我说有一个像这样的对象:

class Something : IDoer
{
    void Do(ISomethingElse x)
    {
         x.DoWhatEverYouWant(42);
    }
}

然后我做

var Thing1 = new Something();
var Thing2 = new Something();

Thing1.Do(blah);
Thing2.Do(blah);

Thing1 = Thing2? “new Something()”会创造什么吗? 或者与静态类没什么不同,除了我可以传递它并将其交换出来等。

Thing1(blah)和Thing2(blah)对象在内存中的“Do”程序是否在同一位置? 我的意思是在执行它时,是否意味着有两个Something.Do程序或只有一个?

它们是两个独立的对象; 他们只是没有国家。

考虑以下代码:

var obj1 = new object();
var obj2 = new object();

Console.WriteLine(object.ReferenceEquals(obj1, obj2));

它将输出False

仅仅因为一个对象没有状态并不意味着它不像任何其他对象那样被分配。 它只占用很小的空间(就像一个object )。

在回答问题的最后部分时:只有一种Do方法。 方法不是按实例存储,而是按类存储。 如果你考虑一下,每个实例存储它们会非常浪费。 Something对象上调用Do每个方法实际上都是相同的指令集; 来自不同对象的调用之间的所有不同之处是底层对象的状态(如果Something类具有任何开始状态,那就是)。

这意味着类对象上的实例方法在行为上与静态方法完全相同。

您可能会认为它好像所有实例级方法都被秘密翻译如下(我不是说这是严格正确的,只是你可以这样想它并且它确实有意义):

// appears to be instance-specific, so you might think
// it would be stored for every instance
public void Do() {
    Do(this);
}

// is clearly static, so it is much clearer it only needs
// to be stored in one place
private static Do(Something instance) {
    // do whatever Do does
}

有趣的注意事项:上面假设的“翻译”几乎解释了扩展方法的工作原理:它们是静态方法,但是通过使用this关键字限定它们的第一个参数,它们突然看起来像实例方法。

内存中绝对有两个不同的对象。 每个对象将在堆上消耗8个字节(至少在32位系统上); 4表示同步块,4表示类型句柄(包括方法表)。 除了系统定义的状态数据之外,在您的情况下没有其他用户定义的状态数据。

Something.Do方法有一个代码实例。 每个对象保存的类型句柄指针是CLR如何定位类的不同方法。 因此,即使内存中有两个不同的对象,它们也会执行相同的代码。 由于Something.Do被宣布为一个实例方法就会产生this指针传递给它的内部,以使代码可以修改取决于哪个对象被调用方法的正确实例成员。 在你的情况下, Something类没有实例成员(因此没有用户定义的状态),所以这是无关紧要的,但仍然会发生。

不,他们不一样。 它们是Something类的两个独立实例。 它们碰巧被同样实例化,就是全部。

您将创建2个“空”对象,堆上的每个对象都会有一个小的分配。

但是“Do”方法总是在同一个地方,这与缺少状态无关。 代码不存储在类/对象中。 只有1件的对应DO()的代码,它有一个“隐藏”的参数, this指向的东西,它被称为上的实例。

从概念上讲,Thing1和Thing2是不同的对象,但只有一个Something.Do程序。

.Net运行时为您创建的每个对象分配一点内存 - 一个块到Thing1,另一个块到Thing2。 这块内存的目的是存储(1)对象的状态和(2)属于该对象的任何过程的地址。 我知道你没有任何状态,但是运行时并不关心 - 它仍然保留两个单独的内存块的单独引用。

现在,Thing1和Thing2的“Do”方法是相同的,运行时只在内存中保留一个版本的过程。

分配的内存Thing1包含Do方法的地址。 当您在Thing1上调用Do方法时,它会查找Thing1的Do方法的地址并运行该方法。 同样的事情发生在另一个对象Thing2上。 虽然对象不同,但Thing1和Thing2都调用相同的Do方法。

归结为Thing1和Thing2的不同之处在于,名字“Thing1”和“Thing2”指的是不同的记忆区域。 在这两种情况下,这个内存的内容都是相同的 - 一个指向“Do”方法的地址。

好吧,无论如何,这就是理论。 在幕后,可能会进行某种优化(如果您有兴趣,请参阅http://www.wrox.com/WileyCDA/Section/CLR-Method-Call-Internals.id-291453.html ),但对于大多数实际目的,我所说的是事情的运作方式。

Thing1!= Thing2
这些是内存中的两个不同对象。

Do方法代码在两个对象的相同位置。 无需存储方法的两个不同副本。

每个引用类型(Thing1,Thing2)指向主存储器中的不同物理地址,因为它们已单独实例化。 在内存中指向的东西是对象使用的字节,无论它是否具有状态(它总是具有状态,但它是否具有声明/初始化状态)。

如果您将引用类型分配给另一个引用类型(Thing2 = Thing1;),那么它将是两个不同引用类型使用的相同内存部分,并且不会发生新的实例化。

考虑新构造函数()的一个好方法是,你实际上只是在类中调用方法,唯一的责任是为你生成一个从类中切割出来的对象的新实例。

所以现在你可以在运行时运行同一类的多个实例来处理各种情况:D

就CLR而言,你在内存中获得了2个单独的实例,每个实例都包含指向它的指针,它与任何其他OOP语言非常相似,但我们不必实际与指针交互,它们的翻译方式与非引用类型,所以我们不必担心它们!

(如果您希望剔除[不安全]关键字,C#中有指针!)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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