今天我们来介绍一下C#语言的字符串花式拼接方式
1. 基本使用
废话少说,我们直接上代码,介绍一下字符串的拼接的方式
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CSharpPrimeNumber
{
public class StringSplicingDemo
{
public void RunDemo()
{
Console.WriteLine("---------------------------------基本使用---------------------------------");
Console.WriteLine("PlusSignString:" + PlusSignString("小蔡同学", "非常帅"));
Console.WriteLine("StringFormatString:" + StringFormatString("小蔡同学", "非常帅"));
Console.WriteLine("StringBuilderString:" + StringBuilderString("小蔡同学", "非常帅"));
Console.WriteLine("StringFormatVS2017:" + StringFormatVS2017("小蔡同学", "非常帅"));
Console.WriteLine("--------------------------------------------------------------------------");
}
/// <summary>
/// +号运算
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
public string PlusSignString(string str1,string str2)
{
string strReturn = str1 + str2;
return strReturn;
}
/// <summary>
/// stringFormat拼接字符串
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
public string StringFormatString(string str1, string str2)
{
string strReturn = string.Format("{0}{1}",str1,str2);
return strReturn;
}
/// <summary>
/// StringBuilder拼接字符串
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
public string StringBuilderString(string str1, string str2)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(str1);
stringBuilder.Append(str2);
return stringBuilder.ToString();
}
/// <summary>
/// VS2017以上提供的字符串拼接
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
public string StringFormatVS2017(string str1, string str2)
{
string strReturn = string.Format($"{str1}{str2}");
return strReturn;
}
}
static void Main(string[] args)
{
StringSplicingDemo pStringSplicingDemo = new StringSplicingDemo();
pStringSplicingDemo.RunDemo();
Console.ReadKey();
}
}
好了,到这里,今天的博客就水到这里了
哈哈哈,各位看客老爷别走,还没有分析完。我们继续说一下以上这几种方式的适用场景。
2. 具体分析
2.1. 算法效率
我们建立几个测试数据,我们将他们拼接,看看效率差距有多大
- 简单拼接
n
次,将str1="小菜同学"
和str2="非常帅"
,进行一定次数的拼接 - 复杂拼接
n
次,将滕王阁序
节选拆分后拼接,进行一定次数的拼接
先看结果
---------------------------------1.基本使用---------------------------------
PlusSignString:小蔡同学非常帅
StringFormatString:小蔡同学非常帅
StringBuilderString:小蔡同学非常帅
StringFormatVS2017:小蔡同学非常帅
--------------------------------------------------------------------------
---------------------------------2.算法效率---------------------------------
---------------------------------2.1.简单拼接---------------------------------
---------------------------------2.1.1.简单拼接10000次---------------------------------
PlusSignString:计算10000总共花费1.5047 ms.
StringFormatString:计算10000总共花费3.3441 ms.
StringBuilderString:计算10000总共花费2.7871 ms.
SingletonStringBuilderString:计算10000总共花费2.6882 ms.
StringFormatVS2017:计算10000总共花费1.5376 ms.
--------------------------------------------------------------------------
---------------------------------2.1.2.简单拼接100000次---------------------------------
PlusSignString:计算100000总共花费10.0948 ms.
StringFormatString:计算100000总共花费34.3649 ms.
StringBuilderString:计算100000总共花费16.5996 ms.
SingletonStringBuilderString:计算100000总共花费17.6449 ms.
StringFormatVS2017:计算100000总共花费62.0455 ms.
---------------------------------2.1.2.简单拼接100000次---------------------------------
PlusSignString:计算1000000总共花费117.9943 ms.
StringFormatString:计算1000000总共花费216.8216 ms.
StringBuilderString:计算1000000总共花费170.7078 ms.
SingletonStringBuilderString:计算1000000总共花费103.9131 ms.
StringFormatVS2017:计算1000000总共花费226.8915 ms.
--------------------------------------------------------------------------
--------------------------------------------------------------------------
---------------------------------2.2.复杂拼接---------------------------------
---------------------------------2.2.1.复杂拼接10000次---------------------------------
披绣闼,俯雕甍,山原旷其盈视,川泽纡其骇瞩,闾阎扑地,钟鸣鼎食之家,舸舰弥津,青雀黄龙之舳,云销雨霁,彩彻区明,落霞与孤鹜齐飞,秋水共长天一色,渔舟唱晚,响穷彭蠡之滨,雁阵惊寒,声断衡阳之浦,遥襟甫畅,逸兴遄飞,爽籁发而清风生,纤歌凝而白云遏,睢园绿竹,气凌彭泽之樽,邺水朱华,光照临川之笔,四美具,二难并,穷睇眄于中天,极娱游于暇日,天高地迥,觉宇宙之无穷,兴尽悲来,识盈虚之有数,望长安于日下,目吴会于云间,地势极而南溟深,天柱高而北辰远,关山难越,谁悲失路之人,萍水相逢,尽是他乡之客,怀帝阍而不见,奉宣室以 何年,嗟乎,时运不齐,命途多舛,冯唐易老,李广难封,屈贾谊于长沙,非无圣主,窜梁鸿于海曲,岂乏明时,所赖君子见机,达人知命,老当益壮,宁移白首之心,穷且益坚,不坠青云之志,酌贪泉而觉爽,处涸辙以犹欢,北海虽赊,扶摇可接,东隅已逝,桑榆非晚,孟尝高洁,空余报国之情,阮籍猖狂,岂效穷途之哭。
PlusSignString:计算10000总共花费107.0751 ms.
StringFormatString:计算10000总共花费211.6322 ms.
StringBuilderString:计算10000总共花费24.7743 ms.
SingletonStringBuilderString:计算10000总共花费13.1791 ms.
StringFormatVS2017:计算10000总共花费667.0529 ms.
--------------------------------------------------------------------------
---------------------------------2.2.2.复杂拼接100000次---------------------------------
PlusSignString:计算100000总共花费708.8968 ms.
StringFormatString:计算100000总共花费1498.7978 ms.
StringBuilderString:计算100000总共花费181.1703 ms.
SingletonStringBuilderString:计算100000总共花费103.6469 ms.
StringFormatVS2017:计算100000总共花费4302.9795 ms.
--------------------------------------------------------------------------
其次结论
- 当简单拼接时,
+
的方式是最优的 - 当复杂拼接时,
StringBuilder
的方式最优 - 至于
string.Format
代码的可读性和维护性高一些
再说原因
- 简单拼接时,
StringBuilder
生成实例的方式不划算,且需要对对象进行一定操作清理,如使用SingletonStringBuilderString
单例StringBuilder
- 复杂拼接时,则是由于
string类型具有不可变性,在底层的存储方式是采用享元的方式存储的,对string字符串的操作(如拼接、Trim()等)都会在内存中产生一个新的字符串对象,在对字符串进行频繁修改的情况下,如在For循环中进行操作等,那么将会频繁的创建新的字符串对象,造成系统的不必要开销,所以这种情况下大家都推荐使用StringBuilder类来对字符串进行操作,那么到底是怎么实现的呢?可以参见下方代码
/// <summary>
/// Appends a string to the end of this builder.
/// </summary>
/// <param name="value">The string to append.</param>
public StringBuilder Append(string? value)
{
if (value != null)
{
// We could have just called AppendHelper here; this is a hand-specialization of that code.
char[] chunkChars = m_ChunkChars;
int chunkLength = m_ChunkLength;
int valueLen = value.Length;
if (((uint)chunkLength + (uint)valueLen) < (uint)chunkChars.Length) // Use strictly < to avoid issues if count == 0, newIndex == length
{
if (valueLen <= 2)
{
if (valueLen > 0)
{
chunkChars[chunkLength] = value[0];
}
if (valueLen > 1)
{
chunkChars[chunkLength + 1] = value[1];
}
}
else
{
Buffer.Memmove(
ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(chunkChars), chunkLength),
ref value.GetRawStringData(),
(nuint)valueLen);
}
m_ChunkLength = chunkLength + valueLen;
}
else
{
AppendHelper(value);
}
}
return this;
}
最后贴代码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CSharpPrimeNumber
{
public class StringSplicingDemo
{
public void RunDemo()
{
Console.WriteLine("---------------------------------1.基本使用---------------------------------");
Console.WriteLine("PlusSignString:" + PlusSignString("小蔡同学", "非常帅"));
Console.WriteLine("StringFormatString:" + StringFormatString("小蔡同学", "非常帅"));
Console.WriteLine("StringBuilderString:" + StringBuilderString("小蔡同学", "非常帅"));
Console.WriteLine("StringFormatVS2017:" + StringFormatVS2017("小蔡同学", "非常帅"));
Console.WriteLine("--------------------------------------------------------------------------");
Console.WriteLine("---------------------------------2.算法效率---------------------------------");
Console.WriteLine("---------------------------------2.1.简单拼接---------------------------------");
Console.WriteLine("---------------------------------2.1.1.简单拼接10000次---------------------------------");
SimpleTogether(10000, PlusSignString);
SimpleTogether(10000, StringFormatString);
SimpleTogether(10000, StringBuilderString);
SimpleTogether(10000, SingletonStringBuilderString);
SimpleTogether(10000, StringFormatVS2017);
Console.WriteLine("--------------------------------------------------------------------------");
Console.WriteLine("---------------------------------2.1.2.简单拼接100000次---------------------------------");
SimpleTogether(100000, PlusSignString);
SimpleTogether(100000, StringFormatString);
SimpleTogether(100000, StringBuilderString);
SimpleTogether(100000, SingletonStringBuilderString);
SimpleTogether(100000, StringFormatVS2017);
Console.WriteLine("---------------------------------2.1.2.简单拼接100000次---------------------------------");
SimpleTogether(1000000, PlusSignString);
SimpleTogether(1000000, StringFormatString);
SimpleTogether(1000000, StringBuilderString);
SimpleTogether(1000000, SingletonStringBuilderString);
SimpleTogether(1000000, StringFormatVS2017);
Console.WriteLine("--------------------------------------------------------------------------");
Console.WriteLine("--------------------------------------------------------------------------");
Console.WriteLine("---------------------------------2.2.复杂拼接---------------------------------");
Console.WriteLine("---------------------------------2.2.1.复杂拼接10000次---------------------------------");
List<string> listInfo = new List<string>() {"披绣闼","俯雕甍","山原旷其盈视","川泽纡其骇瞩","闾阎扑地","钟鸣鼎食之家","舸舰弥津","青雀黄龙之舳","云销雨霁","彩彻区明","落霞与孤鹜齐飞","秋水共长天一色","渔舟唱晚","响穷彭蠡之滨","雁阵惊寒","声断衡阳之浦",
"遥襟甫畅","逸兴遄飞","爽籁发而清风生","纤歌凝而白云遏","睢园绿竹","气凌彭泽之樽","邺水朱华","光照临川之笔","四美具","二难并","穷睇眄于中天","极娱游于暇日","天高地迥","觉宇宙之无穷","兴尽悲来","识盈虚之有数","望长安于日下","目吴会于云间","地势极而南溟深","天柱高而北辰远","关山难越","谁悲失路之人","萍水相逢","尽是他乡之客",
"怀帝阍而不见","奉宣室以何年",
"嗟乎","时运不齐","命途多舛","冯唐易老","李广难封","屈贾谊于长沙","非无圣主","窜梁鸿于海曲","岂乏明时",
"所赖君子见机","达人知命","老当益壮","宁移白首之心","穷且益坚","不坠青云之志","酌贪泉而觉爽","处涸辙以犹欢","北海虽赊","扶摇可接","东隅已逝","桑榆非晚","孟尝高洁","空余报国之情","阮籍猖狂","岂效穷途之哭" };
Console.WriteLine(string.Join(",", listInfo) + "。");
ComplexTogether(10000, listInfo, PlusSignString);
ComplexTogether(10000,listInfo, StringFormatString);
ComplexTogether(10000, listInfo, StringBuilderString);
ComplexTogether(10000, listInfo, SingletonStringBuilderString);
ComplexTogether(10000, listInfo, StringFormatVS2017);
Console.WriteLine("--------------------------------------------------------------------------");
Console.WriteLine("---------------------------------2.2.2.复杂拼接100000次---------------------------------");
ComplexTogether(100000,listInfo, PlusSignString);
ComplexTogether(100000,listInfo, StringFormatString);
ComplexTogether(100000,listInfo, StringBuilderString);
ComplexTogether(100000, listInfo, SingletonStringBuilderString);
ComplexTogether(100000, listInfo, StringFormatVS2017);
Console.WriteLine("--------------------------------------------------------------------------");
}
StringBuilder SingletonStringBuilder = new StringBuilder();
#region 复杂字符串拼接代码段
/// <summary>
/// 复杂字符串拼接
/// </summary>
/// <param name="num"></param>
/// <param name="list"></param>
/// <param name="action"></param>
public void ComplexTogether(int num, List<string> list, Func<List<string>, string> action)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < num; i++)
{
action(list);
}
sw.Stop();
TimeSpan ts2 = sw.Elapsed;
Console.WriteLine($"{action.Method.Name}:计算{num}总共花费{ts2.TotalMilliseconds} ms.");
}
public string PlusSignString(List<string> listInfo)
{
string strReturn = "";
for (int i = 0; i < listInfo.Count; i++)
{
strReturn += listInfo[i];
}
return strReturn;
}
public string StringFormatString(List<string> listInfo)
{
string strReturn = "";
for (int i = 0; i < listInfo.Count; i++)
{
strReturn =string.Format("{0}{1}",strReturn,listInfo[i]);
}
return strReturn;
}
public string StringBuilderString(List<string> listInfo)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (var item in listInfo)
{
stringBuilder.Append(item);
}
return stringBuilder.ToString();
}
/// <summary>
/// StringBuilder拼接字符串
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
public string SingletonStringBuilderString(List<string> listInfo)
{
SingletonStringBuilder.Clear();
foreach (var item in listInfo)
{
SingletonStringBuilder.Append(item);
}
return SingletonStringBuilder.ToString();
}
public string StringFormatVS2017(List<string> listInfo)
{
string strReturn = "";
for (int i = 0; i < listInfo.Count; i++)
{
strReturn = string.Format($"{strReturn}{listInfo[i]}");
}
return strReturn;
}
#endregion
#region 简单字符串拼接代码段
/// <summary>
/// 简单字符串拼接
/// </summary>
/// <param name="num"></param>
/// <param name="action"></param>
public void SimpleTogether(int num, Func<string,string, string> action)
{
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < num; i++)
{
action("小蔡同学", "非常帅"+i);
}
sw.Stop();
TimeSpan ts2 = sw.Elapsed;
Console.WriteLine($"{action.Method.Name}:计算{num}总共花费{ts2.TotalMilliseconds} ms.");
}
/// <summary>
/// +号运算
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
public string PlusSignString(string str1,string str2)
{
string strReturn = str1 + str2;
return strReturn;
}
/// <summary>
/// stringFormat拼接字符串
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
public string StringFormatString(string str1, string str2)
{
string strReturn = string.Format("{0}{1}",str1,str2);
return strReturn;
}
/// <summary>
/// StringBuilder拼接字符串
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
public string SingletonStringBuilderString(string str1, string str2)
{
SingletonStringBuilder.Clear();
SingletonStringBuilder.Append(str1);
SingletonStringBuilder.Append(str2);
return SingletonStringBuilder.ToString();
}
/// <summary>
/// StringBuilder拼接字符串
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
public string StringBuilderString(string str1, string str2)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(str1);
stringBuilder.Append(str2);
return stringBuilder.ToString();
}
/// <summary>
/// VS2017以上提供的字符串拼接
/// </summary>
/// <param name="str1"></param>
/// <param name="str2"></param>
/// <returns></returns>
public string StringFormatVS2017(string str1, string str2)
{
string strReturn = string.Format($"{str1}{str2}");
return strReturn;
}
#endregion
}
}
2.2 代码可读性
- 简单情况下
+
运算,是最清晰的 - 极度复杂且重复的情况下
StringBuilder
是最佳的方式 - 当只是拼接一次
长字符串是
,我认为string.format
可读性是最强的
-
string.Format("{0}",str) 和 string.Format($"{str}")
-
string.Format($"{str}")
我非常推崇这种写法
-
举例说明:现在有一个如下代码,使用string.Format("{0}",str)
string strUpdateSql=string.Format("update {0} set FieldA='{1}' where ID ={2}",TableName,FieldAValuem,IDValue);
以上代码是拼接一个sql语句,这样相较+
和StringBuilder
更加具有优势。
如果这个strUpdateSql
不发生改变就还好,但是如果发生改变,如现在需要多更新几个字段,代码转变为如下:几种情况
string strUpdateSql=string.Format("update {0} set FieldA='{1}',FieldA='{2}' where ID ={3}",TableName,FieldAValuem,FieldBValuem,IDValue);
string strUpdateSql=string.Format("update {0} set FieldA='{1}',FieldA='{3}' where ID ={2}",TableName,FieldAValuem,IDValue,FieldBValuem);
如果仔细思考的话,我们应该想到:当字段复杂的时候上述两种拼接都是一个毫无必要的拼接
,第一种情况:遵循强迫症或者说为了代码有一个从始至终的编码逻辑,就需要调整
,第二种情况:可以预想到当经过几轮的没有原则的修改,这个长代码的维护性及其的差
一下我们贴一下+
的代码,仔细对比一下,个人认为在代码的引号上非常容易出错
string strUpdateSql="update "+TableName+" set FieldA='"+FieldAValuem+"',FieldA='"+FieldBValuem+"' where ID ="+IDValue;
在这里我们写一下string.Format($"{str}")
string strUpdateSql=string.Format($"update {TableName} set FieldA='{FieldAValuem}',FieldA='{FieldBValuem}' where ID ={IDValue}");
以上代码的可读性以及维护性及其的好。
但是
,凡是都担心但是,这种写法是在VS2017之后出现的,这种写法是编译器
行为,如果需要在低版本VS中继续编写代码时,就不建议使用这种代码编写格式了。
3. 小技巧
我个人还是看了蛮多上古代码
的,其中有一种代码我见过很多,现在有三个人名,需要用、分隔拼接起来
,大部分代码如下方式。
List<string> listName=new List<string>(){"张三","李四","王五"};
string strReturn=listName[0];
for(int i=1;i<listName.Count;i++)
{
strReturn+="、"+listName[0];
}
// string strReturn="";
// for(int i=0;i<listName.Count;i++)
// {
// strReturn+="、"+listName[0];
// }
// strRetuern=strReturn==""?strReturn:strReturn.SubString(1);
return strReturn;
个人的写法如下:
List<string> listName=new List<string>(){"张三","李四","王五"};
string strReturn=string.Join("、",listName);
C#源码如下:mscorlib_Source\System\String.cs
[ComVisible(false)]
public static string Join(string separator, IEnumerable<string> values)
{
if (values == null)
{
throw new ArgumentNullException("values");
}
if (separator == null)
{
separator = Empty;
}
using (IEnumerator<string> enumerator = values.GetEnumerator())
{
if (!enumerator.MoveNext())
{
return Empty;
}
StringBuilder builder = new StringBuilder();
if (enumerator.Current != null)
{
builder.Append(enumerator.Current);
}
while (enumerator.MoveNext())
{
builder.Append(separator);
if (enumerator.Current != null)
{
builder.Append(enumerator.Current);
}
}
return builder.ToString();
}
}
我个人写代码和改代码都相较蛮快的,是因为在每一个细节上都考虑可读性、简洁、是不是常人思路以及这个地方如果改怎么方便改