使用string.Format需要注意的一个性能问题

今天,我在写C#代码时,突然发现一个最熟悉的陌生人 —— string.Format。在写C#代码的日子里,与它朝夕相伴,却没有真正去了解它。只知道在字符串比较多时,用它比用加号进行字符串连接效率更高(当然也更方便)。可是却从来没有问过为什么?

在生活中也有类似的现象,与你朝夕相处、你最熟悉的人,你往往不会进一步去了解她(他),你已经习惯了她(他),你认为你已经太了解她(他)了。。。真的是这样吗?这值得去思考。。。

博问中的一个问题 —— StringBuilder,String.concat(),String+String 哪一个效率高?  激发了我的好奇心,想一探string.Format的究竟,而且在开发中也正好遇到一个字符串连接的问题。

了解.NET世界中的东西其实很简单,只要通过工具反编译出相应的.NET类库代码,我们来看看string.Fomat的代码:

    public static string Format(string format, object arg0, object arg1, object arg2){if (format == null)throw new ArgumentNullException("format");return string.Format((IFormatProvider) null, format, arg0, arg1, arg2);}

实际调用的是另外一个签名的string.Format:

复制代码
    public static string Format(IFormatProvider provider, string format, params object[] args){if (format == null || args == null)throw new ArgumentNullException(format == null ? "format" : "args");StringBuilder stringBuilder = new StringBuilder(format.Length + args.Length * 8);stringBuilder.AppendFormat(provider, format, args);return ((object) stringBuilder).ToString();}
复制代码

哦,原来用的就是StringBuilder(也许你早就知道了),string.Format只是StringBuilder的改装精简版。

既然是StringBuilder,它必然无法避免一个影响StringBuilder性能的问题 —— 初始化容量(capacity)的问题,string.Format是如何解决的呢?从上面的代码一眼就可以看出,初始化容量是这么计算出来的:

format.Length + args.Length * 8

从这个计算公式可以看出,假设需要format的字符串是10个,如果这10字符串累加起来的字符数不超过80,就能发挥StringBuilder的最佳性能;否则,StringBuider需要扩容,从而带来性能损失。

所以,对于大字符串,string.Format不是最佳选择。

那最佳选择是什么?还是StringBuilder,只不过要自己写代码计算初始化容量。分享一下今天我们在实际开发中使用的代码:

复制代码
var bodyFormat = "<span id=\"comment_body_{0}\">{1}</span><br/>";                        
var diggFormat = " <a href=\"javascript:void(0);\" οnclick=\"voteComment({0},'Digg')\">支持({2})</a>";
var buryFormat = " <a href=\"javascript:void(0);\" οnclick=\"voteComment({0},'Bury')\">反对({3})</a>";
var args = new string[]{ comment.ID.ToString(), comment.Body, comment.DiggCount.ToString(), comment.BuryCount.ToString() };
//计算初始化容量
int capacity = bodyFormat.Length + diggFormat.Length + buryFormat.Length;
for (int i = 0; i < args.Length; i++)
{capacity += args[i].Length;
}
var sb = new StringBuilder(capacity);
sb.AppendFormat(bodyFormat,args); 
sb.AppendFormat(diggFormat,args); 
sb.AppendFormat(buryFormat,args);
Post.Text = sb.ToString();
复制代码

这里没有使用string.Format,一是因为comment.Body的字符数会很多,string.Format分配的初始化容量不够。二是因为string.Format不能分批Fomat,格式字符串只能写在一起,造成格式字符串很长,也就是bodyFormat, diggFormat, buryFormat要拼成一个字符串。

麻烦主要在参数字符串(args)的长度计算,要将每个字符串的字符数进行累加。我们采用的方法是将所有参数放在string[]类型的变量中,通过遍历数组进行计算,然后将这个string[]类型的变量直接传给StringBuilder.AppendFormat(它支持的参数类型是object[])。

小结

写这篇博文不是为让你弃用string.Format,而是让你了解它所存在的限制,在某些性能要求极高的场景下,可以考虑到这个影响因素。

更新

针对这个问题,实现了两个扩展方法。

1. 针对单个格式字符串

复制代码
namespace System
{public static class StringExtension{public static string FormatWith(this string format, params object[] args){if (format == null || args == null){throw new ArgumentNullException((format == null) ? "format" : "args");}else{var capacity = format.Length + args.Where(a => a != null).Select(p => p.ToString()).Sum(p => p.Length);Console.WriteLine(capacity);var stringBuilder = new StringBuilder(capacity);stringBuilder.AppendFormat(format, args);return stringBuilder.ToString();}}        }
}
复制代码

调用示例:

"welcome to {0}! welcome to {1}!".FormatWith("www.cnblogs.com", "q.cnblogs.com");

2. 针对多个格式字符串

复制代码
namespace System
{public static class StringExtension{public static string FormatWith(this IEnumerable<string> formats, params object[] args){if (formats == null || args == null){throw new ArgumentNullException((formats == null) ? "formats" : "args");}else{var capacity = formats.Where(f => !string.IsNullOrEmpty(f)).Sum(f => f.Length) +args.Where(a => a != null).Select(p => p.ToString()).Sum(p => p.Length);var stringBuilder = new StringBuilder(capacity);foreach (var f in formats){if (!string.IsNullOrEmpty(f)){stringBuilder.AppendFormat(f, args);}}return stringBuilder.ToString();}}}
}
复制代码

调用示例:

new string[] { "welcome to {0}!", " welcome to {1}!" }.FormatWith("www.cnblogs.com", "q.cnblogs.com");

前面使用StringBuilder的代码改为调用扩展方法:

Post.Text = new string[]{
"<span id=\"comment_body_{0}\" class=\"blog_comment_body\">{1}</span><br/>",
" <a href=\"javascript:void(0);\" class=\"comment_vote\" οnclick=\"voteComment({0},'Digg')\">支持({2})</a>",
" <a href=\"javascript:void(0);\" class=\"comment_vote\" οnclick=\"voteComment({0},'Bury')\">反对({3})</a>"
}.FormatWith(comment.ID, comment.Body, comment.DiggCount, comment.BuryCount);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/294873.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

hdu 3480 斜率dp

思路&#xff1a;很普通的斜率dp #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define Maxn 10010 #define LL int using namespace std; LL dp[Maxn][2],num[Maxn]; int que[Maxn*10]; inline LL getleft(int x,int …

angular的html引入js,在AngularJS中的文件夹中加载JavaScript和CSS文件

AngularJS不支持你想要的东西,但是你可以看看Grunt或Gulp等构建工具,它们可以让你“构建”你的应用程序.在您的情况下,这些工具可以查找CSS文件并将它们连接成一个文件.这样,如果您添加新模块,则index.html不必更改.我个人使用GulpJS,因为它看起来要快得多.我发现配置更容易&am…

Android之android.graphics.drawable.Drawable.Callback回调接口

[java] view plaincopy /*如果你想实现一个扩展子Drawable的动画drawable&#xff0c;那么你可以通过setCallBack(android.graphics.drawable.Drawable.Callback)来把你实现的该接口注册到动画drawable *中。可以实现对动画的调度和执行 */ public static interface Call…

为什么说,每个人都应该多读些书?

全世界只有3.14 % 的人关注了爆炸吧知识知乎上有个高赞问题&#xff1a;有哪些值得长期坚持下去就能改变人生的好习惯&#xff1f;其中最高频的回答是读书。随着经历和阅历的增加&#xff0c;越来越多的人清醒的认识到&#xff1a;读书不再是学生时代的事&#xff0c;而是一生的…

.Net Crank性能测试入门

Crank 是微软新出的一个性能测试框架&#xff0c;集成了多种基准测试工具&#xff0c;如bombardier、wrk等。Crank通过统一的配置&#xff0c;可以转换成不同基准测试工具命令进行测试。可参考Bombardier Job实现。安装Crank运行如下两个命令分别安装Crank的cli(Controller)和A…

兄弟机cnc系统面板图解_FANUC软操作面板的应用介绍,真的太详细了

FANUC软操作面板介绍&#xff1a;FANUC软操作面板功能是CNC系统软件的一项功能&#xff0c;可以利用MDI键盘上的光标移动按键和轴移动方向按键 代替机床操作面板的按钮&#xff0c;结合显示器的显示&#xff0c;实现与操作面板同样的功能。在CNC系统安装到机床上之前进行调试试…

个人项目耗时对比记录表

PSP2.1Personal Software Process StagesTime(%)Planning计划 Estimate 估计这个任务需要多少时间 Development开发 Analysis 需求分析 Design Spec 生成设计文档 Design Review 设计复审&#xff08;和同事审核设计文档&#xff09; Coding Standard 代码规范&#xff08…

C# 中静态调用C++dll 和C# 中动态调用C++dll

在最近的项目中&#xff0c;牵涉到项目源代码保密问题&#xff0c;由于代码是C#写的&#xff0c;容易被反编译&#xff0c;因此决定抽取核心算法部分使用C编写&#xff0c;C到目前为止好像还不能被很好的反编译&#xff0c;当然如果你是反汇编高手的话&#xff0c;也许还是有可…

Unity中使用RequireComponent,没有添加上组件

using UnityEngine; using System.Collections;[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))] public class starTest : MonoBehaviour {private Mesh mesh;void Start(){}void Update(){} }代码是这么写的但是编译完后发现没增加组件&#xff0c; 为啥呢………

html写出日出,描写日出优美句子

太阳升起来了&#xff0c;伴着牧歌跳出了地平线&#xff0c;那万道霞光透过云隙照在一望无垠的大草原上&#xff0c;为这片宽广的原野铺上了一层金辉。描写日出的优美句子应该怎么写?描写日出的优美句子1、光芒四射&#xff0c;把万物都唤醒了&#xff1a;小草睁开了它那朦胧的…

Android之封装支付宝支付

在做Android支付的时候肯定会用到支付宝支付&#xff0c; 根据官方给出的demo做起来非常费劲&#xff0c;所以我们需要一次简单的封装。 封装的代码也很简单&#xff0c;就是将官网给的demo提取出一个类来方便使用。 /*** 支付宝支付* * author lenovo*/ public class Alipay {…

有生之年必看!千古第一奇书《山海经》到底是怎样的一本书?

▲点击查看提到《山海经》&#xff0c;大家应该都耳熟能详。作为一部富有神话色彩的千古奇书&#xff0c;它记载了各种脍炙人口的神话传说&#xff0c;像女娲造人、夸父逐日、羿射九日、精卫填海、大禹治水……都是从这里诞生的。除了神话传说&#xff0c;它内容之博大&#xf…

值得永久收藏的 C# 设计模式套路(三)

设计模式套路&#xff0c;完结篇。今天写写设计模式套路中的最后一部分&#xff1a;行为设计模式。这是这个系列的最后一篇。前两篇在&#xff1a;值得永久收藏的 C# 设计模式套路(一)值得永久收藏的 C# 设计模式套路(二)如果你没有看过前两篇&#xff0c;建议先去看看前两篇&a…

sv队列和动态数组的区别_Go 刷 LeetCode 系列:经典(7) 设计双端队列

设计实现双端队列。你的实现需要支持以下操作&#xff1a;MyCircularDeque(k)&#xff1a;构造函数,双端队列的大小为k。insertFront()&#xff1a;将一个元素添加到双端队列头部。如果操作成功返回 true。insertLast()&#xff1a;将一个元素添加到双端队列尾部。如果操作成功…

js 中声明变量 “提前”

1.变量定义提升:声明语句&#xff1a;(1)var声明语句&#xff1a;变量声明语句会被“提前”至脚本或者函数的顶部&#xff0c;但是初始化的操作则还在原来var语句的位置执行。例如&#xff0c;下面的例子&#xff0c;所示&#xff1a;function fun(){alert(x);var x666;alert(x…

运维自动化之使用PHP+MYSQL+SHELL打造私有监控系统(二)

现在开始介绍phpmysqlshell监控系统 1、目的此监控系统主要是通过phpmysqlshell的方式&#xff0c;通过shell脚本对各个机器的其各个服务进行监控&#xff0c;达到及时的了解其各个应用服务的状态&#xff08;如果宕掉与启动&#xff09;&#xff0c;在检测应用服务宕掉时&…

Android之事件总线EventBus详解

顾名思义&#xff0c;AndroidEventBus是一个Android平台的事件总线框架&#xff0c;它简化了Activity、Fragment、Service等组件之间的交互&#xff0c;很大程度上降低了它们之间的耦合&#xff0c;使我们的代码更加简洁&#xff0c;耦合性更低&#xff0c;提升了我们的代码质量…

湘乡江南计算机学校,湘乡职业中等专业学校2021年招生录取分数线

许多学生对自己的职业生涯并没有什么明确的规划,所以没有什么好结果,纯属正常现象。只要不断学习,才能不断收获。由此,本网老师为大家整理了湘乡职业中等专业学校2021年招生录取分数线的相关内容,后期若有变化,一切以官方发布为准。湘乡职业中等专业学校往年参考分数线年份地区…

实验4

#include<stdio.h> int main(void) {double r,h,v,n;printf("Enter r,h and n ");scanf("%lf%lf%lf",&r,&h,&n);if(r<0||h<0){printf("输入错误&#xff0c;重新输入");}else{vcylinder(r,h,n);printf("v%.3f\n&qu…

当女朋友问你会不会出轨的时候,该怎么回答?

1 大象为什么会害怕体型小的动物&#xff1f;&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼2 学会说话很重要&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼3 原来&#xff0c;他们的老爸是一串香肠&#xff1f;&#xff08;素材来源网络&#xff0c;侵…