在开发中,有时候会遇到需要把一个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 这个网站可以在线查看源代码以及详细的注释信息,看看代码对自己的提高还是挺有帮助的。

BCL中String.Join的实现的更多相关文章

  1. C# 中String.Join()方法

    今天在工作中看到了组里一个大佬写的代码,感触颇多,同样实现一个需求,我写循环费了老大劲,代码又臭又长,大佬的代码简洁明了,三行搞定...不得不说,今天赚大了 简单总结一下今天赚到的知识 string里 ...

  2. Java8中String.join方法

    List names=new ArrayList<String>(); names.add("1"); names.add("2"); names. ...

  3. String.Join的实现

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

  4. 【转载】 C#使用string.Join快速用特定字符串串联起数组

    在C#中有时候我们的数组元素需要通过一些特定的字符串串联起来,例如将整形Int数组通过逗号快速串联起来成为一个字符串,可以使用String.Join方法.或者一个字符串string类型数组所有元素快速 ...

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

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

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

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

  7. python3与python2中的string.join()函数

    在python2中,string 模块中有一个join()函数,用于以特定的分隔符分隔源变量中的字符串,将其作为新的元素加入到一个列表中,例如: body=string.join(( "Fr ...

  8. String.join() --Java8中String类新增方法

    序言 在看别人的代码时发现一个方法String.join(),因为之前没有见过所以比较好奇. 跟踪源码发现源码很给力,居然有用法示例,以下是源码: /** * Returns a new String ...

  9. Python中的join()函数的用法

    函数:string.join() Python中有join()和os.path.join()两个函数,具体作用如下:    join():    连接字符串数组.将字符串.元组.列表中的元素以指定的字 ...

随机推荐

  1. 为C# as 类型转换及Assembly.LoadFrom埋坑!

    背景: 不久前,我发布了一个调试工具:发布:.NET开发人员必备的可视化调试工具(你值的拥有) 效果是这样的: 之后,有小部分用户反映,工具用不了(没反应或有异常)~~~ 然后,建议小部分用户换个电脑 ...

  2. .NET Core系列 : 2 、project.json 这葫芦里卖的什么药

    .NET Core系列 : 1..NET Core 环境搭建和命令行CLI入门 介绍了.NET Core环境,本文介绍.NET Core中最重要的一个配置文件project.json的相关内容.我们可 ...

  3. ExtJS 4.2 组件介绍

    目录 1. 介绍 1.1 说明 1.2 组件分类 1.3 组件名称 1.4 组件结构 2. 组件的创建方式 2.1 Ext.create()创建 2.2 xtype创建 1. 介绍 1.1 说明 Ex ...

  4. input[tyle="file"]样式修改及上传文件名显示

    默认的上传样式我们总觉得不太好看,根据需求总想改成和上下结构统一的风格…… 实现方法和思路: 1.在input元素外加a超链接标签 2.给a标签设置按钮样式 3.设置input[type='file' ...

  5. 前端学HTTP之日志记录

    前面的话 几乎所有的服务器和代理都会记录下它们所处理的HTTP事务摘要.这么做出于一系列的原因:跟踪使用情况.安全性.计费.错误检测等等.本文将谥介绍日志记录 记录内容 大多数情况下,日志的记录出于两 ...

  6. Hawk 4.6 并行化

    并行化 Hawk支持单机并行化,也就是使用多线程获取数据.它可以控制目前所有任务的数量,为了不给网站造成过大的压力,仅当任务池中的任务数量小于一定值后,才会插入新的任务. 你可以在数据清洗的 执行面板 ...

  7. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  8. Asp.Net 操作XML文件的增删改查 利用GridView

    不废话,直接上如何利用Asp.NET操作XML文件,并对其属性进行修改,刚开始的时候,是打算使用JS来控制生成XML文件的,但是最后却是无法创建文件,读取文件则没有使用了 index.aspx 文件 ...

  9. 打破陈规抓痛点,H3 BPM10.0挑战不可能

    高效益意味着相似的运营活动比竞争对手做得更好,而战略定位则意味着企业在运营活动中有区别于竞争对手的实施方式,即差异化竞争.在新经济体下,面对社会的变革.市场的竞争环境.不断攀升的成本压力,几乎没有企业 ...

  10. 一不小心写了个WEB服务器

    开场 Web服务器是啥玩意? 是那个托管了我的网站的机器么? No,虽然那个也是服务器,但是我们今天要说的Web服务器主要是指像IIS这样一类的,用于处理request并返回response的工具,没 ...