BCL中String.Join的实现
在开发中,有时候会遇到需要把一个List对象中的某个字段用一个分隔符拼成一个字符串的情况。比如在SQL语句的in条件中,我们通常需要把List<int>这样的对象转换为“1,2,3”这样的字符串,然后作为in的语句传进去。所以自然而然,可以通过循环的方式来拼着个字符串,于是可以写一个下面这样的通用方法:
private static string GetStringFromList<T>(char seperator, IEnumerable<T> values)
{
if (seperator == null)
return string.Empty;
if (values == null || values.Count() == 0)
throw new ArgumentNullException("values");
String result;
StringBuilder strBuilder;
strBuilder = new StringBuilder();
foreach (T str in values)
{
strBuilder.Append(str.ToString());
strBuilder.Append(seperator);
}
result = strBuilder.ToString().TrimEnd(seperator);
return result;
}
方法其实很简单,首先创建一个StringBuilder,然后再往里面Append数据,最后把最后多余的最后一个分隔符去除。
后来发现BCL中string类型提供了现成的string.Join方法,该方法的功能和上面的方法相同。于是很好奇,想看看BCL中是如何实现这么一个简单的功能的,由于BCL的大部分代码已经开源,您可以使用Reflector这个工具查看,我之前就是使用这个工具,但是最近看到了微软的Reference Source 这个网站,可以在线查看源代码,比如string类的实现如下,您可以看到诸如string的GetHashCode是如何实现的等等, 这里我们回到我们想要查看的Join方法上来,其实现如下:
[ComVisible(false)]
public static String Join<T>(String separator, IEnumerable<T> values)
{
if (values == null)
throw new ArgumentNullException("values");
Contract.Ensures(Contract.Result<String>() != null);
Contract.EndContractBlock();
if (separator == null)
separator = String.Empty;
using (IEnumerator<T> en = values.GetEnumerator())
{
if (!en.MoveNext())
return String.Empty;
StringBuilder result = StringBuilderCache.Acquire();
if (en.Current != null)
{
// handle the case that the enumeration has null entries
// and the case where their ToString() override is broken
string value = en.Current.ToString();
if (value != null)
result.Append(value);
}
while (en.MoveNext())
{
result.Append(separator);
if (en.Current != null)
{
// handle the case that the enumeration has null entries
// and the case where their ToString() override is broken
string value = en.Current.ToString();
if (value != null)
result.Append(value);
}
}
return StringBuilderCache.GetStringAndRelease(result);
}
}
代码是不是很简单。对比之前手动实现的方法,发现自己写的代码看起来很挫,这个就是差距,String的Join方法中我们可以看到一下几个地方值得注意:
- 在方法的开始处,使用了Contract 这个类来进行验证协助代码的编写,这个在之前的文章中有所介绍,这里使用了后置条件判断,表示方法的返回值需要是string类型,并且不为空;还有就是在方法开始处做必要的参数合法性验证;在方法中及时判断,及时返回。
- 在实现中,使用了枚举器,C#中的foreach语句其实就是这种枚举器的语法糖,所以这里没有什么好说的,值得一提的是在while循环中的判断语句while(en.MoveNext) 很好的避免了我们方法中在字符串末尾添加多余的字符串,最后还要调用TrimEnd的这种无谓的内存开销。这其实也是do{…}while(..),和while(…){…}这两种循环体的差异体现。
- 实现中,没有直接new直接分配StringBuilder,在返回字符串时也没有直接使用ToString方法,而是使用了StringBuilderCache这个类,这个在之前翻译的.NET程序的性能要领和优化建议 这篇文章中有所介绍。
这个类一看就是对StringBuilder的缓存,因为对于一些小的字符串,创建StringBuilder也是一笔开销。StringBuilder的实现如下:
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Class: StringBuilderCache
**
** Purpose: provide a cached reusable instance of stringbuilder
** per thread it's an optimisation that reduces the
** number of instances constructed and collected.
**
** Acquire - is used to get a string builder to use of a
** particular size. It can be called any number of
** times, if a stringbuilder is in the cache then
** it will be returned and the cache emptied.
** subsequent calls will return a new stringbuilder.
**
** A StringBuilder instance is cached in
** Thread Local Storage and so there is one per thread
**
** Release - Place the specified builder in the cache if it is
** not too big.
** The stringbuilder should not be used after it has
** been released.
** Unbalanced Releases are perfectly acceptable. It
** will merely cause the runtime to create a new
** stringbuilder next time Acquire is called.
**
** GetStringAndRelease
** - ToString() the stringbuilder, Release it to the
** cache and return the resulting string
**
===========================================================*/
using System.Threading;
namespace System.Text
{
internal static class StringBuilderCache
{
// The value 360 was chosen in discussion with performance experts as a compromise between using
// as litle memory (per thread) as possible and still covering a large part of short-lived
// StringBuilder creations on the startup path of VS designers.
private const int MAX_BUILDER_SIZE = 360;
[ThreadStatic]
private static StringBuilder CachedInstance;
public static StringBuilder Acquire(int capacity = StringBuilder.DefaultCapacity)
{
if(capacity <= MAX_BUILDER_SIZE)
{
StringBuilder sb = StringBuilderCache.CachedInstance;
if (sb != null)
{
// Avoid stringbuilder block fragmentation by getting a new StringBuilder
// when the requested size is larger than the current capacity
if(capacity <= sb.Capacity)
{
StringBuilderCache.CachedInstance = null;
sb.Clear();
return sb;
}
}
}
return new StringBuilder(capacity);
}
public static void Release(StringBuilder sb)
{
if (sb.Capacity <= MAX_BUILDER_SIZE)
{
StringBuilderCache.CachedInstance = sb;
}
}
public static string GetStringAndRelease(StringBuilder sb)
{
string result = sb.ToString();
Release(sb);
return result;
}
}
}
这里面对StringBuilder的创建和字符串获取进行了缓存。 代码的注释很清楚,这里就不多讲了。
.NET的源代码大部分都可以直接看了,以前可以使用Reflector进行查看,现在Reference Source 这个网站可以在线查看源代码以及详细的注释信息,看看代码对自己的提高还是挺有帮助的。
BCL中String.Join的实现的更多相关文章
- C# 中String.Join()方法
今天在工作中看到了组里一个大佬写的代码,感触颇多,同样实现一个需求,我写循环费了老大劲,代码又臭又长,大佬的代码简洁明了,三行搞定...不得不说,今天赚大了 简单总结一下今天赚到的知识 string里 ...
- Java8中String.join方法
List names=new ArrayList<String>(); names.add("1"); names.add("2"); names. ...
- String.Join的实现
String.Join的实现 在开发中,有时候会遇到需要把一个List对象中的某个字段用一个分隔符拼成一个字符串的情况.比如在SQL语句的in条件中,我们通常需要把List<int>这样的 ...
- 【转载】 C#使用string.Join快速用特定字符串串联起数组
在C#中有时候我们的数组元素需要通过一些特定的字符串串联起来,例如将整形Int数组通过逗号快速串联起来成为一个字符串,可以使用String.Join方法.或者一个字符串string类型数组所有元素快速 ...
- c# String.Join 和 Distinct 方法 去除字符串中重复字符
1.在写程序中经常操作字符串,需要去重,以前我的用方式利用List集合和 contains去重复数据代码如下: string test="123,123,32,125,68,9565,432 ...
- String.Join 和 Distinct 方法 去除字符串中重复字符
Qualys项目中写到将ServerIP以“,”分割后插入数据库并将重复的IP去除后发送到服务器进行Scan,于是我写了下面的一段用来剔除重复IP: //CR#1796870 modify by v- ...
- python3与python2中的string.join()函数
在python2中,string 模块中有一个join()函数,用于以特定的分隔符分隔源变量中的字符串,将其作为新的元素加入到一个列表中,例如: body=string.join(( "Fr ...
- String.join() --Java8中String类新增方法
序言 在看别人的代码时发现一个方法String.join(),因为之前没有见过所以比较好奇. 跟踪源码发现源码很给力,居然有用法示例,以下是源码: /** * Returns a new String ...
- Python中的join()函数的用法
函数:string.join() Python中有join()和os.path.join()两个函数,具体作用如下: join(): 连接字符串数组.将字符串.元组.列表中的元素以指定的字 ...
随机推荐
- TODO:GitHub创建组织的步骤
TODO:GitHub创建组织的步骤 使用GitHub进行团队合作,写这个步骤主要作用是为了OneTODO作为一个团队组织进行代码的分享,让更多人来参与. 使用帐号.密码登录GitHub 2.右上角加 ...
- java head space/ java.lang.OutOfMemoryError: Java heap space内存溢出
上一篇JMX/JConsole调试本地还可以在centos6.5 服务器上进行监控有个问题端口只开放22那么设置的9998端口 你怎么都连不上怎么监控?(如果大神知道还望指点,个人见解) 线上项目出现 ...
- bzoj3208--记忆化搜索
题目大意: 花花山峰峦起伏,峰顶常年被雪,Memphis打算帮花花山风景区的人员开发一个滑雪项目. 我们可以把风景区看作一个n*n的地图,每个点有它的初始高度,滑雪只能从高处往低处滑[严格大于] ...
- Jqprint实现页面打印
好些项目需要实现页面打印,特别是一些后台管理类系统,下面介绍一款轻量级的打印插件: 1.实现页面打印要引入jQuery和Jqprint.点击下载Jqprint插件 <script languag ...
- iOS 原生地图地理编码与反地理编码
当我们要在App实现功能:输入地名,编码为经纬度,实现导航功能. 那么,我需要用到原生地图中的地理编码功能,而在Core Location中主要包含了定位.地理编码(包括反编码)功能. 在文件中导入 ...
- iOS--->微信支付小结
iOS--->微信支付小结 说起支付,除了支付宝支付之外,微信支付也是我们三方支付中最重要的方式之一,承接上面总结的支付宝,接下来把微信支付也总结了一下 ***那么首先还是由公司去创建并申请使用 ...
- phpexcel读取输出操作
//读取 <?php header("Content-Type:text/html;charset=utf-8"); include 'Classes/PHPExcel.ph ...
- FineReport:任意时刻只允许在一个客户端登陆账号的插件
在使用FineReport报表系统中,处于账户安全考虑,有些企业希望同一账号在任意时刻智能在统一客户端登录.那么当A用户在C1客户端登陆后,该账号又在另外一个C2客户端登陆,服务器如何取判断呢? 开发 ...
- 第六代智能英特尔® 酷睿™ 处理器图形 API 开发人员指南
欢迎查看第六代智能英特尔® 酷睿™ 处理器图形 API 开发人员指南,该处理器可为开发人员和最终用户提供领先的 CPU 和图形性能增强.各种新特性和功能以及显著提高的性能. 本指南旨在帮助软件开发人员 ...
- Spring MVC数据校验
在web应用程序中,为了防止客户端传来的数据引发程序异常,常常需要对 数据进行验证.输入验证分为客户端验证与服务器端验证.客户端验证主要通过JavaScript脚本进行,而服务器端验证则主要通过Jav ...