如何在C#中检查同值字符串是否为同一实例?
C#中如何判断两个字符串是否指向同一内存实例
C#里的string是引用类型,但它重写了==和Equals()方法用来比较字符串的值。要检查两个字符串是否指向同一块内存的同一实例,其实有可靠的方法:
核心结论:用Object.ReferenceEquals或Assert.AreSame就够
你之前误以为Object.ReferenceEquals("A", "A")是调用了==,这是错误的——Object.ReferenceEquals是Object类的静态方法,它不会被任何子类重写,逻辑就是直接比较两个对象的引用地址,完全不受string重写的==影响。
至于为什么字面量"A"和"A"用它会返回true,是因为CLR的**字符串驻留(String Interning)**机制:相同的字符串字面量会被CLR放入驻留池复用,所以它们本来就是同一个实例。
你测试代码里的问题
你写的unsafe代码里,用__makeref获取的是变量本身的内存地址(比如变量a、b、c在栈上的地址),而不是它们指向的string对象在堆上的地址。所以ptra是变量a的地址,ptrc是变量c的地址,这两个变量本身是不同的栈上存储,地址自然不一样,但它们指向的是同一个string实例。
如果要直接获取string对象的内存地址,可以用GCHandle或者fixed块(注意需要开启unsafe编译选项):
unsafe { // 方式1:用GCHandle获取对象的 pinned 地址 GCHandle handleA = GCHandle.Alloc(a, GCHandleType.Pinned); IntPtr addrA = handleA.AddrOfPinnedObject(); GCHandle handleB = GCHandle.Alloc(b, GCHandleType.Pinned); IntPtr addrB = handleB.AddrOfPinnedObject(); GCHandle handleC = GCHandle.Alloc(c, GCHandleType.Pinned); IntPtr addrC = handleC.AddrOfPinnedObject(); Assert.AreEqual(addrA, addrB); // 字面量驻留,地址相同 Assert.AreEqual(addrA, addrC); // c是a的引用,地址相同 handleA.Free(); handleB.Free(); handleC.Free(); // 方式2:用fixed块获取字符指针,再推导对象起始地址(需要知道对象头大小) fixed (char* charPtrA = a) { // 64位系统下,对象头(同步块+类型句柄)占16字节,所以对象起始地址是charPtrA - 16 IntPtr objAddrA = (IntPtr)charPtrA - IntPtr.Size * 2; // 同理处理其他变量,结果和GCHandle方式一致 } }
补充验证场景
如果是动态创建的非驻留字符串,比如:
string d = new string('A', 1); string e = new string('A', 1);
这时候Object.ReferenceEquals(d, e)会返回false,因为它们是堆上的两个不同实例,值相同但引用不同,这就能准确区分是否为同一实例。
内容的提问来源于stack exchange,提问作者Ryan Liu
相关产品推荐
相关产品推荐

