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方法中我们可以看到一下几个地方值得注意:

  1. 在方法的开始处,使用了Contract 这个类来进行验证协助代码的编写,这个在之前的文章中有所介绍,这里使用了后置条件判断,表示方法的返回值需要是string类型,并且不为空;还有就是在方法开始处做必要的参数合法性验证;在方法中及时判断,及时返回。
  2. 在实现中,使用了枚举器,C#中的foreach语句其实就是这种枚举器的语法糖,所以这里没有什么好说的,值得一提的是在while循环中的判断语句while(en.MoveNext) 很好的避免了我们方法中在字符串末尾添加多余的字符串,最后还要调用TrimEnd的这种无谓的内存开销。这其实也是do{…}while(..),和while(…){…}这两种循环体的差异体现。
  3. 实现中,没有直接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的实现的更多相关文章

  1. BCL中String.Join的实现

    在开发中,有时候会遇到需要把一个List对象中的某个字段用一个分隔符拼成一个字符串的情况.比如在SQL语句的in条件中,我们通常需要把List<int>这样的对象转换为“1,2,3”这样的 ...

  2. c# String.Join 和 Distinct 方法 去除字符串中重复字符

    1.在写程序中经常操作字符串,需要去重,以前我的用方式利用List集合和 contains去重复数据代码如下: string test="123,123,32,125,68,9565,432 ...

  3. String.Join的巧用

    String.Join大大的方便了我们拼接字符串的处理. 1.普通用法:指定元素间的拼接符号 var ids = new List<int>(); for (int i = 0; i &l ...

  4. string.Join()的用法

    List<string> list = new List<string>(); list.Add("I"); list.Add("Love&quo ...

  5. string.join加引号

    columnsGen = string.Join(",", modelDictionary.Keys); valueGen = modelDictionary.Values.Agg ...

  6. String.Join 和 Distinct 方法 去除字符串中重复字符

    Qualys项目中写到将ServerIP以“,”分割后插入数据库并将重复的IP去除后发送到服务器进行Scan,于是我写了下面的一段用来剔除重复IP: //CR#1796870 modify by v- ...

  7. String.join()方法的使用

    String.join()方法是JDK1.8之后新增的一个静态方法,使用方式如下所示: String  result = String.join("-","java&qu ...

  8. 教你50招提升ASP.NET性能(二十三):StringBuilder不适用于所有字符串连接的场景;String.Join可能是

    (41)StringBuilder is NOT the answer for all string concatenation scenarios; String.Join could be 招数4 ...

  9. Using LINQ Group By and String.Join() / Aggregate() in Entity Framework 3.5

    linq to sql 的时候,有时候需要用到 先group  然后来个 aggregate 串连一下值, 但会总会出错,说不识别 aggregate 或者 string.join 方法 搜遍网络 一 ...

  10. string.Join和string.Concat的区别

    源自Difference between String.Join() vs String.Concat() With .NET 4.0, String.Join() uses StringBuilde ...

随机推荐

  1. TreeMap cannot be cast to java.lang.Comparable

    /** * Constructs a new, empty tree map, using the natural ordering of its * keys. All keys inserted ...

  2. ssh下常用操作汇总(good)

    1. 安装git,从程序目录打开 "Git Bash"  2. 键入命令:ssh-keygen -t rsa -C "email@email.com"   &q ...

  3. Android 布局学习之——Layout(布局)具体解释二(常见布局和布局參数)

     [Android布局学习系列]   1.Android 布局学习之--Layout(布局)具体解释一   2.Android 布局学习之--Layout(布局)具体解释二(常见布局和布局參数)   ...

  4. [Error]EOL while scanning string literal

    有一个经常性的工作项目.需要一天的一些表数据到外部接口,但最近总是异常.今天检查的原因. 第一本地和测试环境中测试程序是没有问题,有网络环境只会在日志中抛出一个异常.产生主要的例外是推定异常数据. , ...

  5. 如何为ios酷我音乐盒下载导出的音乐文件(使用Java程序设计)

    这个工具已经准备第二版,读者了解编程软件,可以直接使用,请阅读和使用这个场地 http://blog.csdn.net/jzj1993/article/details/44459983 本文所涉及内容 ...

  6. CSS3+HTML5特效8 - 顶部和右侧固定,左侧随窗口变化的布局

    原文:CSS3+HTML5特效8 - 顶部和右侧固定,左侧随窗口变化的布局 效果演示 实现原理 使用3个div(一个是顶部栏,一个是左侧栏,一个是右侧栏): 使用checkbox作为判断是否显示和隐藏 ...

  7. netty 3.9.2 UDP协议服务器和客户端DEMO

    说明:基于netty 3.9.2的udp协议实现的(如果你使用的版本是4.X或5.X,请参考其他方法):程序的逻辑结构是,客户端发送给服务端一串数据,服务器端返回给客户端“A”.在进行游戏开发时需要对 ...

  8. app后端设计(0)--总文件夹

    原文:http://blog.csdn.net/newjueqi/article/details/19003775 做了接近两年app相关的系统架构,api设计,先后在两个创业公司中工作,经历过手机网 ...

  9. http协议报头信息和主体鉴别

    http协议报头信息和主体是使用一个空行分开.这是什么空行?简单的说,那是,\r\n\r\n. 所以会server数据的回归\r\n\r\n结果分离,一个是标题信息.它是一个消息的文本. C#例如,下 ...

  10. 分布式基础学习(2)分布式计算系统(Map/Reduce)

    二. 分布式计算(Map/Reduce) 分 布式式计算,同样是一个宽泛的概念,在这里,它狭义的指代,按Google Map/Reduce框架所设计的分布式框架.在Hadoop中,分布式文件 系统,很 ...