为什么字符串地址修改后仍然不变

本文关键字:字符串 地址 修改 为什么 | 更新日期: 2023-09-27 18:11:50

根据文档,类型System.String在设计上是不可变的。所以,当尝试连接字符串等时,我们只需要获得对结果字符串的引用。但如果这是真的,为什么这段代码总是为两种情况返回相同的地址(当然,如果对象不是由GC移动的(:

using System;
using System.Reflection;
namespace StringImmutabilityCheck
{
    class Program
    {
        private static unsafe void PrintAddress(string b)
        {
            var info = typeof(string).GetField("m_firstChar", BindingFlags.Instance | BindingFlags.NonPublic);
            char value = (char)info.GetValue(b);
            char* buffer = &value;
            Console.WriteLine("Addres of {0} is {1}", b, (int)buffer);
        }
        static void Foo(ref string a)
        {
            a += "xxx";
        }
        static void Main()
        {
            string b = "aaaa";
            PrintAddress(b);
            Foo(ref b);
            PrintAddress(b);
        }
    }
}

为什么字符串地址修改后仍然不变

您不是在此处获取字符串的地址

char value = (char)info.GetValue(b);   // 'value' is a local var
char* buffer = &value;                 // buffer points to local var, on the stack
Console.WriteLine("Addres of {0} is {1}", b, (int)buffer);  // no it isn't

因为您正在写入局部变量c的地址,它两次都在堆栈上的同一位置。

注意,m_firstChar不是地址;它是第一个字符。在内部,代码使用相对于对象的第一个字符的地址来访问数据,但是:m_firstChar将在这里为这两种情况报告'a',因为'a'是字符串中的第一个字符串。

真正的乐趣:

[MethodImpl(MethodImplOptions.NoInlining)]
static void PassThru(string b)
{
    PrintAddress(b);
}
...
PrintAddress(b);
PassThru(b);

现在,对于同一字符串,"地址"是不同的。

尝试使用此函数获取字符串的地址:

Func<string, int> getAddress = x =>
{
    unsafe
    {
        fixed (char *p = x)
        {
            return (int)p;
        }
    }
};

然后这可以用来查看地址的变化:

var text = "Foo";
Console.WriteLine(getAddress(text));
text += "Bar";
Console.WriteLine(getAddress(text));

请注意,GC可能会在您使用时移动字符串。