You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 00:10:20