String.Join的实现
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 这个网站可以在线查看源代码以及详细的注释信息,看看代码对自己的提高还是挺有帮助的。
String.Join的实现的更多相关文章
- BCL中String.Join的实现
在开发中,有时候会遇到需要把一个List对象中的某个字段用一个分隔符拼成一个字符串的情况.比如在SQL语句的in条件中,我们通常需要把List<int>这样的对象转换为“1,2,3”这样的 ...
- c# String.Join 和 Distinct 方法 去除字符串中重复字符
1.在写程序中经常操作字符串,需要去重,以前我的用方式利用List集合和 contains去重复数据代码如下: string test="123,123,32,125,68,9565,432 ...
- String.Join的巧用
String.Join大大的方便了我们拼接字符串的处理. 1.普通用法:指定元素间的拼接符号 var ids = new List<int>(); for (int i = 0; i &l ...
- string.Join()的用法
List<string> list = new List<string>(); list.Add("I"); list.Add("Love&quo ...
- string.join加引号
columnsGen = string.Join(",", modelDictionary.Keys); valueGen = modelDictionary.Values.Agg ...
- String.Join 和 Distinct 方法 去除字符串中重复字符
Qualys项目中写到将ServerIP以“,”分割后插入数据库并将重复的IP去除后发送到服务器进行Scan,于是我写了下面的一段用来剔除重复IP: //CR#1796870 modify by v- ...
- String.join()方法的使用
String.join()方法是JDK1.8之后新增的一个静态方法,使用方式如下所示: String result = String.join("-","java&qu ...
- 教你50招提升ASP.NET性能(二十三):StringBuilder不适用于所有字符串连接的场景;String.Join可能是
(41)StringBuilder is NOT the answer for all string concatenation scenarios; String.Join could be 招数4 ...
- Using LINQ Group By and String.Join() / Aggregate() in Entity Framework 3.5
linq to sql 的时候,有时候需要用到 先group 然后来个 aggregate 串连一下值, 但会总会出错,说不识别 aggregate 或者 string.join 方法 搜遍网络 一 ...
- string.Join和string.Concat的区别
源自Difference between String.Join() vs String.Concat() With .NET 4.0, String.Join() uses StringBuilde ...
随机推荐
- [模拟] hdu 4452 Running Rabbits
意甲冠军: 两个人在一个人(1,1),一个人(N,N) 要人人搬家每秒的速度v.而一个s代表移动s左转方向秒 特别值得注意的是假设壁,反弹.改变方向 例如,在(1,1),采取的一个步骤,以左(1,0) ...
- 每天收获一点点------Hadoop Eclipse插件的使用
本文所用软件版本:myeclipe2014 hadoop1.2.1 1.安装Hadoop开发插件 下载hadoop-eclipse-plugin-1.2.1.jar,拷贝到myeclipse根目 ...
- Display Database Image using MS SQL Server 2008 Reporting Services
原文 Display Database Image using MS SQL Server 2008 Reporting Services With the new release of MS SQL ...
- Android变化如何破解几场金
我们在玩游戏的总会遇到一些东西需要购买,但是,我们可能要花钱,那么我们应该怎么办呢?这与游戏的插.我们在这里谈论的Android游戏,搜索互联网上的移动端游戏插件,您可能会发现一个叫段:八门神器.ap ...
- SQL Server的备份
原文:SQL Server的备份 0.参考文献 1.恢复模式 SQL Server 备份和还原操作发生在数据库的恢复模式的上下文中. 恢复模式旨在控制事务日志维护. “恢复模式”是一种数据库属性,它控 ...
- 快速解读GC日志(转)
本文是 Plumbr 发行的 Java垃圾收集手册 的部分内容.文中将介绍GC日志的输出格式, 以及如何解读GC日志, 从中提取有用的信息.我们通过 -XX:+UseSerialGC 选项,指定JVM ...
- Eclipse:引用一个项目作为库(图文教程)
前言:工程TestRoid要引用Volley项目作为一个库 过程,如下面: 一:选择导入Android工程 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc ...
- Cocos2d-X采用CCScrollView创建滚动视图
CCScrollView滚动视图可以让游戏有效果,并能够通过滚动视图切换游戏场景,滚动视图通常用来选择在游戏中的级别 实例1:使用CCScrollView创建一个简单的滚动视图 首先创建一个Scrol ...
- ABP分层架构
ABP分层架构 基于DDD的现代ASP.NET开发框架--ABP系列之3.ABP分层架构 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ABP ...
- Oracle免费的便捷Web应用开发框架
Oracle免费的便捷Web应用开发框架 APEX 总体来说,APEX是我见过最便捷最高效的开发框架,用起来比PHP还舒服.上手简单,学习成本极低,曾经有个做行政的小女生,在我指导下两天就可以开发出简 ...