【源码分析】你必须知道的string.IsNullOrEmpty && string.IsNullOrWhiteSpace
写在前面
之前自信撸码时踩了一次小坑,代码如下:
private static void AppServer_NewMessageReceived(WebSocketSession session, string value)
{
if (string.IsNullOrEmpty(value))
{
return;
}
value = HttpUtility.UrlDecode(value);
SuperSocketTemp<string> model = JsonConvert.DeserializeObject<SuperSocketTemp<string>>(value);
//具体业务...
}
就是这段代码在测试环境抛错,说起来全是泪啊。这段代码的具体业务场景是Websocket即时通讯接收来自客户端的消息,消息以json字符串的形式传输。首先判断是否空字符串,如果不是,为了防止乱码进行Url解码,然后反序列化消息解析成需要的数据格式,最后执行具体的业务操作。
测试环境抛的错是万恶的“未将对象引用到对象的实例”,很简单就可以定位到问题的所在——反序列化失败了,只要在序列化之后执行具体业务逻辑之前加上非空判断就可以解决掉这个问题。这也怪自己思维还不够严密,没有养成防御性编码的习惯。
private static void AppServer_NewMessageReceived(WebSocketSession session, string value)
{
if (string.IsNullOrEmpty(value))
{
return;
}
value = HttpUtility.UrlDecode(value);
SuperSocketTemp<string> model = JsonConvert.DeserializeObject<SuperSocketTemp<string>>(value);
if(model==null)
{
return;
}
//具体业务...
}
通过日志分析反序列失败的原因,日志中记录的消息是空白的,但是代码中明明有string.IsNullOrEmpty(value)
的判断,为啥还会出现空的情况呢?仔细一看,原来是多个连续的空格,吐血。于是乎立马把string.IsNullOrEmpty(value)
改为string.IsNullOrWhiteSpace(value)
,当value是多个连续的空格时,直接返回,不会继续往下执行。
private static void AppServer_NewMessageReceived(WebSocketSession session, string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return;
}
value = HttpUtility.UrlDecode(value);
SuperSocketTemp<string> model = JsonConvert.DeserializeObject<SuperSocketTemp<string>>(value);
if(model==null)
{
return;
}
//具体业务...
}
我们都知道,string.IsNullOrEmpty方法是判断字符串是否为:null或者string.Empty;string.IsNullOrWhiteSpace方法是判断null或者所有空白字符,功能相当于string.IsNullOrEmpty和str.Trim().Length总和。那么具体方法内部是怎么实现的呢?我们可以通过ILSpy反编译窥探一番。
string.IsNullOrEmpty源码分析
// string
/// <summary>Indicates whether the specified string is null or an <see cref="F:System.String.Empty" /> string.</summary>
/// <param name="value">The string to test. </param>
/// <returns>true if the <paramref name="value" /> parameter is null or an empty string (""); otherwise, false.</returns>
[__DynamicallyInvokable]
public static bool IsNullOrEmpty(string value)
{
return value == null || value.Length == 0;
}
string.IsNullOrEmpty实现很简单,无非就是判断传入的字符串参数,当是null或者空字符串string.Empty就返回true;否则返回false。
string.IsNullOrWhiteSpace源码分析
// string
/// <summary>Indicates whether a specified string is null, empty, or consists only of white-space characters.</summary>
/// <param name="value">The string to test.</param>
/// <returns>true if the <paramref name="value" /> parameter is null or <see cref="F:System.String.Empty" />, or if <paramref name="value" /> consists exclusively of white-space characters. </returns>
[__DynamicallyInvokable]
public static bool IsNullOrWhiteSpace(string value)
{
if (value == null)
{
return true;
}
for (int i = 0; i < value.Length; i++)
{
if (!char.IsWhiteSpace(value[i]))
{
return false;
}
}
return true;
}
string.IsNullOrWhiteSpace的实现就稍微复杂一些,首先当传入的字符串参数为null时肯定返回true;如果不是就开始遍历字符串,取出字符执行char.IsWhiteSpace(value[i])方法,如果char.IsWhiteSpace(value[i])方法返回false,就终止遍历,返回fasle;否则返回true。所以char.IsWhiteSpace方法应该判断的是传入的字符是否为空字符,是空字符返回true,不是返回false。我们可以进入char.IsWhiteSpace方法看一下具体实现:
// char
/// <summary>Indicates whether the specified Unicode character is categorized as white space.</summary>
/// <param name="c">The Unicode character to evaluate. </param>
/// <returns>true if <paramref name="c" /> is white space; otherwise, false.</returns>
[__DynamicallyInvokable]
public static bool IsWhiteSpace(char c)
{
if (char.IsLatin1(c))
{
return char.IsWhiteSpaceLatin1(c);
}
return CharUnicodeInfo.IsWhiteSpace(c);
}
可以发现方法内部判断了char.IsLatin1(c),符合的话执行char.IsWhiteSpaceLatin1(c),看不明白,继续往下走。
// char
private static bool IsLatin1(char ch)
{
return ch <= 'ÿ';
}
'ÿ'是什么鬼?看不懂。但是char.IsWhiteSpace方法调用了CharUnicodeInfo.IsWhiteSpace(c)方法,那应该是和Unicode有关。而且到了char字符的级别上,更加可以肯定和Unicode编码有关。从Unicode字符列表搜索'ÿ',果然搜到了。
我们可以发现'ÿ'是拉丁字母辅助的最后一个字符,再往后的的字符基本不会出现,所以在大多数情况下ch <= 'ÿ'
可以满足的。当满足ch <= 'ÿ'
时执行下面的方法:
// char
private static bool IsWhiteSpaceLatin1(char c)
{
return c == ' ' || (c >= '\t' && c <= '\r') || c == '\u00a0' || c == '\u0085';
}
IsWhiteSpaceLatin1(char c)负责判断字符c是否是空白字符。
① c == ' '
很好理解,判断c是不是空格字符。对应下图:
② c >= '\t' && c <= '\r'
对照Unicode字符列表就可以理解。下图红框圈出的5个字符都认定为空白字符。
③ c == '\u00a0'
如下图被认定为空白字符。
④ c == '\u0085'
如下图被认定为空白字符。
满足①②③④其中任意一个,便会被判定为空白字符。
那么假设char.IsLatin1(c)返回false呢?此时执行CharUnicodeInfo.IsWhiteSpace(c)。
// System.Globalization.CharUnicodeInfo
internal static bool IsWhiteSpace(char c)
{
switch (CharUnicodeInfo.GetUnicodeCategory(c))
{
case UnicodeCategory.SpaceSeparator:
case UnicodeCategory.LineSeparator:
case UnicodeCategory.ParagraphSeparator:
return true;
default:
return false;
}
}
CharUnicodeInfo.GetUnicodeCategory(c)会返回一个UnicodeCategory枚举类型。
// System.Globalization.CharUnicodeInfo
/// <summary>Gets the Unicode category of the specified character.</summary>
/// <param name="ch">The Unicode character for which to get the Unicode category. </param>
/// <returns>A <see cref="T:System.Globalization.UnicodeCategory" /> value indicating the category of the specified character.</returns>
[__DynamicallyInvokable]
public static UnicodeCategory GetUnicodeCategory(char ch)
{
return CharUnicodeInfo.InternalGetUnicodeCategory((int)ch);
}
CharUnicodeInfo是一个静态类,根据MSDN说明,Unicode标准定义了许多Unicode字符类别。例如,一个字符可能被分类为大写字母,小写字母,小数位数字,字母数字,段落分隔符,数学符号或货币符号。所述UnicodeCategory枚举定义了可能的字符的类别。
使用CharUnicodeInfo类来获取特定字符的UnicodeCategory值。该CharUnicodeInfo类定义了返回下面的Unicode字符值的方法:
- 字符或代理对所属的特定类别。返回的值是UnicodeCategory枚举的成员。
- 数字值。仅适用于数字字符,包括分数,下标,上标,罗马数字,货币分子,圈出的数字和脚本特定的数字。
- 数字值。适用于可与其他数字字符组合的数字字符,以表示编号系统中的整数。
- 十进制数字值。仅适用于表示小数点(基10)系统中的十进制数字的字符。十进制数字可以是十个数字之一,从零到九。这些字符是UnicodeCategory的成员DecimalDigitNumber类别。
当GetUnicodeCategory方法返回的枚举值是UnicodeCategory.SpaceSeparator、UnicodeCategory.LineSeparator、UnicodeCategory.ParagraphSeparator其中任意之一,则判定为空白字符,返回true。
总结
踩坑不要紧,要紧的是要知道为什么会有这个坑。
软件80%的bug都拜“未将对象引用到对象的实例”所赐,要养成防御性编码的好习惯。
本文为博主学习感悟总结,水平有限,如果不当,欢迎指正。
如果您认为还不错,不妨点击一下下方的【推荐】按钮,谢谢支持。
转载与引用请注明出处。
【源码分析】你必须知道的string.IsNullOrEmpty && string.IsNullOrWhiteSpace的更多相关文章
- 面试必会之HashMap源码分析
相关文章 面试必会之ArrayList源码分析 面试必会之LinkedList源码分析 简介 HashMap最早出现在JDK1.2中,底层基于散列算法实现.HashMap 允许 null 键和 nul ...
- 深入理解.net - 4.你必须知道的String
为什么要单独写string,主要是它太常用了,同时又太特殊了,特殊到CLR对它的处理都和其它对象不一样.简直可以称为VIP用户啊.本文并不是一篇介绍如何使用string的文章,而是旨在阐述string ...
- 别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】
目录 1.什么是类的加载(类初始化) 2.类的生命周期 3.接口的加载过程 4.解开开篇的面试题 5.理解首次主动使用 6.类加载器 7.关于命名空间 8.JVM类加载机制 9.双亲委派模型 10.C ...
- Mybatis 源码分析--crud
增加源码分析-insert() --------------------------------------------------------------------- public int ins ...
- Spark源码分析 – BlockManager
参考, Spark源码分析之-Storage模块 对于storage, 为何Spark需要storage模块?为了cache RDD Spark的特点就是可以将RDD cache在memory或dis ...
- [源码]String StringBuffer StringBudlider(2)StringBuffer StringBuilder源码分析
纵骑横飞 章仕烜 昨天比较忙 今天把StringBuffer StringBulider的源码分析 献上 在讲 StringBuffer StringBuilder 之前 ,我们先看一下 ...
- String、StringBuffer、StringBuilder源码分析
利用反编译具体看看"+"的过程 1 public class Test 2 { 3 public static void main(String[] args) 4 { 5 int ...
- JDK源码分析(一)—— String
dir 参考文档 JDK源码分析(1)之 String 相关
- Java BAT大型公司面试必考技能视频-1.HashMap源码分析与实现
视频通过以下四个方面介绍了HASHMAP的内容 一. 什么是HashMap Hash散列将一个任意的长度通过某种算法(Hash函数算法)转换成一个固定的值. MAP:地图 x,y 存储 总结:通过HA ...
随机推荐
- TI Davinci DM6446开发攻略——UBL移植
UBL的程序设计,相对UBOOT.KERNEL.ROOTFS.设备驱动.DSP开发来说,还是比较简单.我们先从DAVINCI的启动说起,了解UBL在DAVIN系统中的位置和作用.对于固件程序烧写在N ...
- FFMPEG在windows平台编译的详细过程,包括环境安装
下面开始: 由于FFMpeg是基于Linux开发的开源项目,源代码和Windows下最常见的Visual Studio提供的C/C++编译器不兼容,因此它不能使用MSVC++编译.要想使用FFMpeg ...
- Android WebView 缓存机制和模式详解
当我们加载Html时候,会在我们data/应用package下生成database与cache两个文件夹: 我们请求的Url记录是保存在webviewCache.db里,而url的内容是保存在webv ...
- configure: error: xml2-config not found. Please check your libxml2 installation
安装php时的报错 checking libxml2 install dir... nochecking for xml2-config path... configure: error: xml2- ...
- 项目部署到Tomcat报错
1.今天晚上,我想把dojo项目部署到Tomcat中,结果发现部署不了,Tomcat报错.而且,这个错误白天时也碰到了. 错误具体详细如下: Publishing failed with multip ...
- Meet Python
关于python 入门书<Head First Python>的一些读书笔记,用以备忘. 下载安装Python 下载地址: https://www.python.org/downloads ...
- linux2.6硬盘扇区直接读写程序
下面的程序可以在linux2.6内核直接读写硬盘的指定扇区,也是根据网上一个朋友的做法做了修改的: 有两个不是很明白的地方就是:1.bd_claim函数的使用,这个是个递归函数,像是匹配内存指针和设备 ...
- windows下键盘常用快捷键整理
以下快捷键均在win7环境下测试有效: 声明:本博文由多篇博文经实测整理而出. win键相关的快捷键多用于桌面场景,如开起资源管理器.切换任务窗口.最大化最小化窗口等等. 场景一: 1. 任何情况下想 ...
- 芝麻HTTP:设置Selenium+Chrome代理
微博登录限制了错误次数···加上Cookie大批账号被封需要从Cookie池中 剔除被封的账号··· 需要使用代理··· 无赖百度了大半天都是特么的啥玩意儿???结果换成了 Google手到擒来 分分 ...
- iOS - XMPP Openfire 服务器的搭建
前言 提前下载好相关软件,且安装目录最好安装在全英文路径下.如果路径有中文名,那么可能会出现一些莫名其妙的问题. 提前准备好的软件: jdk-8u91-macosx-x64.dmg mysql-5.7 ...