每日一练 之 c#中的字符串驻留

本文探讨了C#中字符串的实现方式,特别是在CLR中的存储形式。通过实例演示了字符串常量如何被复用,以及字符串变量在不同操作下如何分配内存。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

无论在哪种语言与平台中,字符串都是我们最常使用的对象。
.Net与C#语言把字符串在表面上做的相当易用,但是只有在深入理解字符串在CLR的驻留形式后才能更加合理高效的使用字符串对象。

1 字符串的驻留形式
先看一个sample

        static void Main(string[] args)
        
{
            
//.Net Framework中将一个应用程序的string都存放在一个Hashtable中
            
//key就是值 value是堆地址

            
string str1 = "hello"//申请一块堆内存 把地址放在StringHashtable的key为hello的元素中
            string str2 = "hello";//由于上一句已经创建了key为hello的元素 所以不使用newobj 申请新的堆内存
            string str3 = "h" + "e" + "l" + "l" + "o"//编译成MSIL语言时 已经与string str3 = "hello"一样了
            string str4 = new string(new char[] 'h''e''l''l''o' }); //自己显示进行new

            
string str5 = "hello2"//申请一块堆内存 key为hello2


            Console.WriteLine(
object.ReferenceEquals(str1, str2).ToString()); //True 引用同一块堆内存
            Console.WriteLine(object.ReferenceEquals(str1, str3).ToString()); //True 也是引用同一块堆内存
            Console.WriteLine(object.ReferenceEquals(str1, str4).ToString()); //False 引用了不同的堆内存

            str2 
= "hello2"// 每次对字符串对象赋值时 都先从StringHashcode中检索是否有重复的key 检索到了str5

            Console.WriteLine(
object.ReferenceEquals(str1, str2).ToString()); //False str2与str1已经不引用同一个堆
            Console.WriteLine(object.ReferenceEquals(str2, str5).ToString()); //True 变成与str5引用同一个堆内存

            str1 
= Console.ReadLine();
            str2 
= Console.ReadLine();

            Console.WriteLine(
object.ReferenceEquals(str1, str2).ToString());
            Console.WriteLine(
object.ReferenceEquals(str1, string.Empty).ToString());

            Console.ReadLine();
        }

开始时候的str1,str2,str3其实在翻译为MSIL后,都是相同的string str = "hello";
然后该应用程序会有一个字符串池,应该是一个hashtable,保存着所有的字符串,后面的str2="hello2",使str2与str5指向了同一块内存。
最后我又试验了一下字符串池的机制是否在运行时也是启作用的,发现字符串池仅仅是编译时,优化了应用程序的大小,应用程序启动时,优化了内存占用大小,而运行时没有这个机制,每次对一个string的赋值都会导致在堆内存分配一块新的空间。

转载于:https://www.cnblogs.com/coderlee/archive/2008/01/02/1023316.html

#region 1:一列数的规则如下: 1、1、2、3、5、8、13、21、34......求第30位数是多少, 用递归算法实现。 //int n = FindNum(30); //Console.WriteLine(n); //Console.ReadKey(); #endregion #region 2:有一个3*4矩阵,输出最大元素的值,及其所在的行号和列号, int a[3][4]={{1,2,3,4},{9,8,7,6}, {-10,10,-5,2}}。 //int[,] a = { { 1, 2, 3, 4 }, { 9, 8, 7, 6 }, { -10, 10, -5, 2 } }; //Max1(a); //Console.ReadKey(); #endregion #region 3:实现二分法查找,int a[8] = {3,12,24,36,55,68,75,88},查找24需要几次查找出来。 //int[] a = { 3,12,24,36,55,68,75,88 }; //int value = 24; //Select(a, value); //Console.ReadKey(); #endregion #region 4:实现冒泡排序, int[] array = { 23, 45, 16, 7, 42 }。 //int[] array = { 23, 45, 16, 7, 42 }; //Sort(array); //foreach(int n in array) //{ // Console.WriteLine(n); //} //Console.ReadKey(); #endregion #region 5:求出0-1000中能被7整除的数,并计算输出每五个数的和。 //int sum = 0;//每五个数的和 //int count = 0;//计算个数 //for(int i=1;i<=1000;i++) //{ // if(i%7==0) // { // count++;//个数加一 // sum = sum + i; // if (count % 5 == 0) // { // Console.WriteLine(sum); // sum = 0; // } // } //} //Console.WriteLine("1 - 1000中一共有{0}个数被7整除", count); //Console.ReadKey(); #endregion #region 6:编写一个类,其中包含一个排序的方法 Sort(), 当传入的是一串整数,就按照从小到大的顺序输出 如果传入的是一个字符串,就将字符串反序输出。 //SortAndReverse sa = new SortAndReverse(); //string intput = "woaini"; //Console.WriteLine(sa.Sort(intput)); //Console.ReadKey(); #endregion #region 7:编写一个矩形类,私有数据成员为矩形的长(len)和宽(wid),无参构造函数将len和wid设置为0,有参构造函数设置len和wid的值;另外,类还包括矩形的求周长、求面积、取矩形的长度、取矩形的宽度、修改矩形的长度和宽度为对应的形参值等公用方法。 //Rectangular rt = new Rectangular(); //rt.Len = 10; //rt.Wid = 15; //Console.WriteLine(rt.Area()); //Console.ReadKey(); #endregion #region 8:编写一个控制台应用程序,接收一个长度大于3的字符串,完成 下列功能:1 输出字符串长度。  2 输出字符串中第一个出现字母a的位置。  3 在字符串的第3个字符后面插入子串“hello”,输出新字符串。 4 将字符串“hello”替换成“me”,输出新字符串。  5 以字符“m”为分隔符,将字符串分离,并输出分离后的字符串。) //Console.WriteLine("请输入一个长度大于3的字符串:"); //string str= Console.ReadLine(); ////输出字符串长度 //Console.WriteLine("字符串长度为:{0}", str.Length); ////输出字符串中第一个出现字母a的位置 //if (str.IndexOf("a") == -1) //{ // Console.WriteLine("未找到字母a"); //} //else //{ // Console.WriteLine("第一个出现字母a的位置是{0}", str.IndexOf("a")); //} ////在字符串的第3个字符后面插入子串“hello”,输出新字符串。 //str = str.Insert(3, "hello"); //Console.WriteLine(str); ////将字符串“hello”替换成“me”,输出新字符串。 //str = str.Replace("hello", "me"); //Console.WriteLine(str); ////以字符“m”为分隔符,将字符串分离,并输出分离后的字符串 //string[] str1 = str.Split('m'); //Console.Write("分离后的字符串为:"); //foreach(string n in str1) //{ // Console.Write(n + " "); //} //Console.ReadKey(); #endregion
1. 声明两个变量:int n1 = 10, n2 = 20;要求将两个变量交换,最后输出n1为20,n2为10。扩展(*):不使用第三个变量如何交换? 2. 用方法来实现:将上题封装一个方法来做,方法有两个参数分别为num1,num2,将num1与num2交换。提示:方法有两个参数n1,n2,在方法中将n1与n2进行交换,使用ref。(*) 3. 请用户输入一个字符串,计算字符串中的字符个数,并输出。 4. 用方法来实现:计算两个数的最大值。思考:方法的参数?返回值?扩展(*):计算任意多个数间的最大值(提示:使用可变参数,params)。 5. 用方法来实现:计算1-100之间的所有整数的和。 6. 用方法来实现:计算1-100之间的所有奇数的和。 7. 用方法来实现:判断一个给定的整数是否为“质数”。 8. 用方法来实现:计算1-100之间的所有质数(素数)的和。 9. 用方法来实现:有一个整数数组:{ 1, 3, 5, 7, 90, 2, 4, 6, 8, 10 },找出其中最大值,并输出。不能调用数组的Max()方法。 10. 用方法来实现:有一个字符串数组:{ "马龙", "迈克尔乔丹", "雷吉米勒", "蒂姆邓肯", "科比布莱恩特" },请输出最长的字符串。 11. 用方法来实现:请计算出一个整型数组的平均值。{ 1, 3, 5, 7, 90, 2, 4, 6, 8, 10 }。要求:计算结果如果有小数,则显示小数点后两位(四舍五入)。Math.Round() 12. 请通过冒泡排序法对整数数组{ 1, 3, 5, 7, 90, 2, 4, 6, 8, 10 }实现升序排序。 13. 有如下字符串:【"患者:“大夫,我咳嗽得很重。” 大夫:“你多大年记?” 患者:“七十五岁。” 大夫:“二十岁咳嗽吗”患者:“不咳嗽。” 大夫:“四十岁时咳嗽吗?” 患者:“也不咳嗽。” 大夫:“那现在不咳嗽,还要等到什么时咳嗽?”"】。需求:①请统计出该字符中“咳嗽”二字的出现次数,以及每次“咳嗽”出现的索引位置。②扩展(*):统计出每个字符的出现次数。 14. 将字符串" hello world,你 好 世界 ! "两端空格去掉,并且将其中的所有其他空格都替换成一个空格,输出结果为:"hello world,你 好 世界 !"。 15. 制作一个控制台小程序。要求:用户可以在控制台录入每个学生的姓名,当用户输入quit(不区分大小写)时,程序停止接受用户的输入,并且显示出用户输入的学生的个数,以及每个学生的姓名。效果如图:
<think>好的,我现在需要回答用户关于C#字符串拼接和删除操作的问题。首先,用户希望看到具体的示例和方法,所以我得确保覆盖这两个方面。根据用户提供的引用内容,特别是引用2、3、4,这些提到了StringBuilder的拼接方法、String.Intern以及string.Join的使用。此外,用户可能还关心删除操作,比如如何移除字符串中的某些字符或子串。 首先,字符串拼接方面,常见的几种方法包括使用加号运算符、String.Concat、String.Join、StringBuilder以及插值字符串。需要分别给出示例,并说明它们的适用场景。例如,加号适合少量拼接,而大量拼接时用StringBuilder更高效。引用2提到了StringBuilder的append方法,可以引用这一点。引用4中的string.Join在生成CSV时很有用,也应该包括进去。 然后是删除操作,主要涉及String.Remove方法。可能需要说明如何根据索引和长度删除字符,以及结合IndexOf或LastIndexOf来定位要删除的部分。例如删除特定子串后的内容,或者删除指定位置之后的字符。此外,替换方法String.Replace也可以间接实现删除,比如将某个子串替换为空字符串。 接下来,我需要检查是否有其他相关方法,比如Substring或者Trim等,但用户主要关注拼接和删除,所以可能不需要扩展太多。不过,Trim方法也可以作为删除空格的操作提一下。 要确保每个示例都正确,并且使用中文注释。同时,注意引用对应的参考资料。例如,在提到StringBuilder时引用[^2],提到string.Join时引用[^4],提到String.Intern时引用[^3]。 可能的思考点:用户是否知道不同的拼接方法在性能上的差异?比如,多次使用加号拼接字符串会导致大量临时对象,而StringBuilder则更高效。这点需要说明,特别是引用中的内容。另外,String.Intern用于字符串驻留,可能和拼接相关但用户可能更关注基础操作,所以可能作为补充内容。 最后,生成相关问题时要围绕拼接和删除,可能涉及性能优化、其他操作方法或应用场景。比如,如何避免拼接时的性能问题,或者删除特定模式的内容等。 现在,整理结构,先回答拼接,再删除,每个部分给出多种方法示例,最后相关问题。确保每个代码示例正确,并符合C#语法,比如变量命名和使用正确的类名(如StringBuilder在C#中属于System.Text命名空间,需要添加using指令吗?不过用户可能只需要代码片段,所以可能不需要完全代码,但需要正确的方法调用)。 可能的问题点:在删除示例中,用户可能希望删除特定子串而不是位置,所以需要给出两种示例:基于位置删除和基于子串替换删除。例如,使用Remove(int startIndex, int length)和Replace(string oldValue, "")。 现在开始组织内容,确保每个方法都有示例,并引用对应的参考文献。同时,保持回答结构清晰,分点说明,代码用格式块表示。</think>### C#字符串拼接与删除操作示例 #### 一、字符串拼接方法 1. **`+`运算符** 适用于少量拼接,代码简洁但效率较低(频繁拼接时会产生临时对象): ```csharp string s1 = "Hello"; string s2 = "World"; string result = s1 + ", " + s2 + "!"; // 输出: Hello, World! ``` 2. **`String.Concat`方法** 合并多个字符串或数组: ```csharp string[] words = { "C#", "is", "powerful" }; string sentence = string.Concat(words); // 输出: C#ispowerful ``` 3. **`String.Join`方法** 用分隔符连接字符串数组(适合生成CSV等格式): ```csharp string[] data = { "Apple", "Banana", "Cherry" }; string joined = string.Join("|", data); // 输出: Apple|Banana|Cherry ``` 4. **`StringBuilder`类** 高频次拼接时性能更优(避免内存浪费)[^2]: ```csharp using System.Text; StringBuilder sb = new StringBuilder(); sb.Append("Hello"); sb.AppendLine(" World!"); sb.Append("-- End"); string output = sb.ToString(); // 包含换行和完整内容 ``` 5. **插值字符串(`$`语法)** 直接嵌入变量,提升可读性: ```csharp string name = "Alice"; int age = 30; string info = $"{name} is {age} years old."; // 输出: Alice is 30 years old. ``` --- #### 二、字符串删除操作 1. **`String.Remove`方法** - 删除指定位置后的字符: ```csharp string original = "abcdefg"; string result1 = original.Remove(3); // 输出: abc(删除索引3之后的所有字符) string result2 = original.Remove(1, 2); // 输出: adefg(从索引1开始删除2个字符) ``` - 结合`IndexOf`定位删除点: ```csharp string path = "C:\\Docs\\file.txt"; int index = path.LastIndexOf('\\'); string fileName = path.Remove(index); // 输出: C:\Docs\ ``` 2. **`String.Replace`方法** 替换或删除特定子串: ```csharp string text = "Hello, [placeholder]!"; string cleaned = text.Replace("[placeholder]", "World"); // 替换 string trimmed = text.Replace(" ", ""); // 删除所有空格 ``` 3. **`Trim`系列方法** 删除首尾空白或指定字符: ```csharp string input = " example "; string trimmed = input.Trim(); // 输出: "example" string customTrim = "**text**".Trim('*'); // 输出: "text" ``` --- ### 性能与注意事项 - **拼接性能**:高频操作优先选择`StringBuilder`,避免`+`的多次内存分配。 - **驻留优化**:需重用字符串时,可用`String.Intern`减少内存占用[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值