C#(FCL)字符串对象的不可变性

在一篇博文上看到引用类型和值类型的区别如下
- 引用类型与值类型相同的是,结构体也可以实现接口;
- 引用类型可以派生出新的类型,而值类型不能;
- 引用类型可以包含null值,值类型不能(可空类型功能允许将 null 赋给值类型);
- 引用类型变量的赋值只复制对对象的引用,而不复制对象本身。而将一个值类型变量赋给另一个值类型变量时,将复制包含的值。

对于最后一条,经常混淆的是string。我曾经在一本书的一个早期版本上看到String变量比string变量效率高;我还经常听说String是引用类型,string是值类型,等等。

但后来我验证这是错误的,String类型和string类型是完全一回事,它们都是System.String。String是引用类型,也就是说其本质都是指向托管堆内存的指针。它和用户定义的引用类型实际是一回事,只有一个特性和用户定义的引用类型不一样,那就是所谓字符串对象的不可变性:

MSDN 字符串(C# 编程指南)字符串对象是不可变的:即它们创建之后就无法更改。 所有看似修改字符串的 String 方法和 C# 运算符实际上都以新字符串对象的形式返回结果。 在下面的示例中,当连接 s1 和 s2 的内容以形成一个字符串时,不会修改两个原始字符串。 += 运算符会创建一个包含组合内容的新字符串。 这个新对象赋给变量 s1,而最初赋给 s1 的对象由于没有其他任何变量包含对它的引用而释放,用于垃圾回收。

string s1 = "A string is more ";
string s2 = "than the sum of its chars.";

// Concatenate s1 and s2. This actually creates a new// string object and stores it in s1, releasing the// reference to the original object.
s1 += s2;

System.Console.WriteLine(s1);
// Output: A string is more than the sum of its chars.

由于“修改”字符串实际上是创建新字符串,因此创建对字符串的引用时必须谨慎。 如果创建了对字符串的引用,然后“修改”原始字符串,则该引用指向的仍是原始对象,而不是修改字符串时创建的新对象。 下面的代码说明了这种行为:

C#
string s1 = "Hello ";
string s2 = s1;
s1 += "World";

System.Console.WriteLine(s2);
//Output: Hello

再看一段示例程序

using System;
namespace CSTest
{
    classProgram
    {
        unsafestaticvoid Main(string[] args)
        {
            string a = "原始值";
            string b = a;
            a = "修改值";
            Console.WriteLine("string b="+b);
            //string b = 原始值

            String c = "原始值";
            String d = c;
            c = "修改值";
            Console.WriteLine("String d=" + d);
            //string d=原始值

            Console.Read();
        }
    }
}

string和String是完全一样的
至于简化的名称其实是string
通过这篇文章,可以认识到两点:
1. 在C#中,string和String是完全一样的。
1. string是引用类型,只不过每次修改都会新建一个对象存储修改后的值,而不是在原始对象上修改。

6月2日更新

考虑通过以下代码将string包装为一个普通class

namespace Class1
{
    public class testclass
    {
        private string str;
        public testclass(string s)//构造函数
        {
            str = s;
        }
        public void set(string s)//修改字符串值
        {
            str = s;
        }
        public string get()//查询字符串值
        {
            return str;
        }
    }
}

后通过类似过程创建该类对象e和f


using System;
using Class1;

namespace CSTest
{
    class Program
    {
        unsafe static void Main(string[] args)
        {
            testclass e = new testclass("原始值");
            testclass f = e;
            e.set("修改值");
            Console.WriteLine("testclass f.str=" + f.get());
            //testclass f.str=修改值

            Console.Read();
        }
    }
}

此时C#展现了它类Java的一面,即:将一个对象赋值给另一个对象直接传递该对象的引用,而不是在托管堆上构造一个新的对象,这一点与C++不同。

C#提供的拷贝构造函数:
另有值得注意的一点是:在C#中,所有对象均继承自System.Object,这意味着所有对象都继承了一个“拷贝构造函数”,它的用法和C++有所不同,但这并不是重点。这个拷贝构造函数名叫MemberwiseClone,从名字上看起来,它是一个Memberwise的深拷贝,然而,我多次在Demo程序,MSDN,各类参考书籍上确认,它其实是个bitwise的浅拷贝(所以我很困惑为什么这个方法要叫MemberwiseClone)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值