testing如果string是一个GUID没有抛出exception?

我想尝试将string转换为Guid,但我不想依靠捕捉exception(

  • 出于性能的原因 – 例外是昂贵的
  • 出于可用性原因 – debugging器popup
  • 出于devise的原因 – 预期并不例外

换句话说这个代码:

public static Boolean TryStrToGuid(String s, out Guid value) { try { value = new Guid(s); return true; } catch (FormatException) { value = Guid.Empty; return false; } } 

是不适合的。

我会尝试使用正则expression式,但由于GUID可以是括号包装,括号包装,没有包装,使得它很难。

此外,我认为某些Guid值是无效的(?)


更新1

ChristianK有一个好主意,只抓住FormatException ,而不是全部。 更改了问题的代码示例以包含build议。


更新2

为什么要担心抛出exception? 我真的很期待无效的GUID吗?

答案是肯定的 。 这就是为什么我使用TryStrToGuid – 我期待不好的数据。

示例1 可以通过将GUID附加到文件夹名称来指定名称空间扩展名 。 我可能会parsing文件夹名称,检查最后的文本后看看 是一个GUID。

 c:\Program Files c:\Program Files.old c:\Users c:\Users.old c:\UserManager.{CE7F5AA5-6832-43FE-BAE1-80D14CD8F666} c:\Windows c:\Windows.old 

示例2我可能正在运行大量使用的Web服务器,希望检查一些已发布的数据的有效性。 我不希望无效数据占用比所需要高2-3个数量级的资源。

示例3我可能正在parsing用户input的searchexpression式。

在这里输入图像描述

如果他们inputGUID,我想专门处理它们(如专门search该对象,或在响应文本中突出显示和格式化该特定search项)。


更新3 – 性能基准

testing转换10,000个好的GUID,和10,000个坏的GUID。

 Catch FormatException: 10,000 good: 63,668 ticks 10,000 bad: 6,435,609 ticks Regex Pre-Screen with try-catch: 10,000 good: 637,633 ticks 10,000 bad: 717,894 ticks COM Interop CLSIDFromString 10,000 good: 126,120 ticks 10,000 bad: 23,134 ticks 

我不应该为一个问题辩护。

性能基准

 Catch exception: 10,000 good: 63,668 ticks 10,000 bad: 6,435,609 ticks Regex Pre-Screen: 10,000 good: 637,633 ticks 10,000 bad: 717,894 ticks COM Interop CLSIDFromString 10,000 good: 126,120 ticks 10,000 bad: 23,134 ticks 

COM Intertop(最快)答案:

 /// <summary> /// Attempts to convert a string to a guid. /// </summary> /// <param name="s">The string to try to convert</param> /// <param name="value">Upon return will contain the Guid</param> /// <returns>Returns true if successful, otherwise false</returns> public static Boolean TryStrToGuid(String s, out Guid value) { //ClsidFromString returns the empty guid for null strings if ((s == null) || (s == "")) { value = Guid.Empty; return false; } int hresult = PInvoke.ObjBase.CLSIDFromString(s, out value); if (hresult >= 0) { return true; } else { value = Guid.Empty; return false; } } namespace PInvoke { class ObjBase { /// <summary> /// This function converts a string generated by the StringFromCLSID function back into the original class identifier. /// </summary> /// <param name="sz">String that represents the class identifier</param> /// <param name="clsid">On return will contain the class identifier</param> /// <returns> /// Positive or zero if class identifier was obtained successfully /// Negative if the call failed /// </returns> [DllImport("ole32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = true)] public static extern int CLSIDFromString(string sz, out Guid clsid); } } 

底线:如果您需要检查一个string是否是一个GUID,并且您关心性能,请使用COM Interop。

如果您需要将string表示中的guid转换为Guid,请使用

 new Guid(someString); 

一旦.net 4.0可用,您可以使用Guid.TryParse()

你不会喜欢这个,但是你认为捕捉exception的速度会变慢吗?

有多less失败的尝试来parsing一个GUID你期待与成功的比较?

我的build议是使用你刚刚创build的function和configuration你的代码。 如果你发现这个function是一个真正的热点, 然后修复它,但不是之前。

在.NET 4.0中,您可以编写如下代码:

 public static bool IsValidGuid(string str) { Guid guid; return Guid.TryParse(str, out guid); } 

我至less会重写它为:

 try { value = new Guid(s); return true; } catch (FormatException) { value = Guid.Empty; return false; } 

你不想在SEHException,ThreadAbortException或其他致命的或不相关的东西上说“无效的GUID”。

更新 :从.NET 4.0开始,Guid提供了一组新的方法:

  • Guid.TryParse
  • Guid.TryParseExact

真的,这些应该被使用(如果仅仅是因为它们并不是在内部使用try-catch来“天真地”实现的话)。

互操作比捕捉exception要慢:

在快乐的道路上,有10,000个Guids:

 Exception: 26ms Interop: 1,201ms 

在不快乐的路上:

 Exception: 1,150ms Interop: 1,201ms 

它更一致,但也一贯较慢。 对我来说,你最好把你的debugging器configuration成只处理未处理的exception。

那么,这是你将需要的正则expression式

 ^[A-Fa-f0-9]{32}$|^({|\\()?[A-Fa-f0-9]{8}-([A-Fa-f0-9]{4}-){3}[A-Fa-f0-9]{12}(}|\\))?$|^({)?[0xA-Fa-f0-9]{3,10}(, {0,1}[0xA-Fa-f0-9]{3,6}){2}, {0,1}({)([0xA-Fa-f0-9]{3,4}, {0,1}){7}[0xA-Fa-f0-9]{3,4}(}})$ 

但是,这只是初学者。 您还必须validation诸如date/时间等各个部分是否在可接受的范围内。 我无法想象这比你已经概述的try / catch方法更快。 希望你没有收到许多无效的GUID来保证这种检查!

出于可用性原因 – debugging器popup

如果你想要使用try / catch方法,你可以添加[System.Diagnostics.DebuggerHidden]属性,以确保debugging器不会中断,即使你已经设置了断点。

尽pipe使用错误确实是比较昂贵的,但是大多数人认为他们的GUID中的大部分都是计算机生成的,所以TRY-CATCH并不是太昂贵,因为它只会在CATCH上产生成本。 你可以用两个简单的testing来certificate这一点(用户公开的,没有密码)。

干得好:

 using System.Text.RegularExpressions; /// <summary> /// Validate that a string is a valid GUID /// </summary> /// <param name="GUIDCheck"></param> /// <returns></returns> private bool IsValidGUID(string GUIDCheck) { if (!string.IsNullOrEmpty(GUIDCheck)) { return new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$").IsMatch(GUIDCheck); } return false; } 

我有类似的情况,我注意到,几乎从来没有无效的string长36个字符。 所以基于这个事实,我改变了一下你的代码以获得更好的性能,同时保持简单。

 public static Boolean TryStrToGuid(String s, out Guid value) { // this is before the overhead of setting up the try/catch block. if(value == null || value.Length != 36) { value = Guid.Empty; return false; } try { value = new Guid(s); return true; } catch (FormatException) { value = Guid.Empty; return false; } } 

据我所知,在mscrolib中没有Guid.TryParse之类的东西。 根据参考资料来源,Guidtypes具有超复杂的构造函数,它检查各种guid格式并试图parsing它们。 即使通过反思,你也可以调用帮助器方法。 我认为你必须search第三方Guidparsing器,或者写你自己的。

运行可能的GUID,通过RegEx或一些自定义代码进行健全性检查,以确保strig至less看起来像一个GUID,只包含有效字符(也许它似乎适合整体格式)。 如果它没有通过健全检查返回一个错误 – 这可能会淘汰绝大多数无效的string。

然后像上面那样转换string,仍然捕捉通过完整性检查的less数无效string的exception。

Jon Skeet做了一些类似于parsingInts的分析(TryParse在框架之前): 检查一个string是否可以转换为Int32

然而,正如AnthonyW琼斯表示,你可能不应该担心这一点。

  bool IsProbablyGuid(string s) { int hexchars = 0; foreach(character c in string s) { if(IsValidHexChar(c)) hexchars++; } return hexchars==32; } 

我不知道这个答案,但是为了什么是值得的,你是对的,不想在这里使用try / catch块。 需要大量的计算才能超过捕获exception的成本,而try / catch不适用于正常的程序stream!

当然,如果你不需要在代码中捕捉很多例外, 那也不是什么大不了的事情 。

  • 得到reflection器
  • copy'n'paste Guid's .ctor(String)
  • 用“return false”replace“throw new …”的每一个发生。

Guid的ctor几乎是一个编译的正则expression式,这样你就可以得到完全一样的行为,没有exception的开销。

  1. 这是否构成逆向工程? 我认为这样做,因此可能是非法的。
  2. 如果GUIDforms发生变化,将会中断。

更酷的解决scheme是通过dynamicreplace“抛出新的”来dynamic地调用一个方法。

我投了由Jon或类似的解决scheme(IsProbablyGuid)上面张贴的GuidTryParse链接。 我将写一个像我的转换库一样。

我认为这个问题必须如此复杂是完全蹩脚的。 如果Guid可以为null,那么“is”或“as”关键字就可以了。 但是由于某种原因,即使SQL Server可以,.NET也不行。 为什么? Guid.Empty的价值是什么? 这只是.NETdevise所创造的一个愚蠢的问题,当一种语言的惯例自己join时,它真的让我感到毛骨悚然。 到目前为止,性能最好的答案是使用COM Interop,因为框架不能很好地处理它。 “这个string可以是GUID吗?” 应该是一个容易回答的问题。

依靠抛出的exception是好的,直到应用程序上网。 在那个时候,我只是设置了一个拒绝服务攻击。 即使我没有受到“攻击”,我知道有些雅虎正在与URL链接,也许我的营销部门会发送一个畸形的链接,然后我的应用程序将遭受相当沉重的性能打击,可能会带来因为我没有写我的代码来处理一个不应该发生的问题,但是我们都知道会发生。

这在“exception”上模糊了一点 – 但是底线,即使问题不频繁,如果在短时间内发生足够的时间,应用程序崩溃,从所有的捕获服务,然后我认为抛出一个exception不好的forms。

TheRage3K

如果TypeOf ctype(myvar,Object)是Guid那么…..

 Private Function IsGuidWithOptionalBraces(ByRef strValue As String) As Boolean If String.IsNullOrEmpty(strValue) Then Return False End If Return System.Text.RegularExpressions.Regex.IsMatch(strValue, "^[\{]?[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}[\}]?$", System.Text.RegularExpressions.RegexOptions.IgnoreCase) End Function Private Function IsGuidWithoutBraces(ByRef strValue As String) As Boolean If String.IsNullOrEmpty(strValue) Then Return False End If Return System.Text.RegularExpressions.Regex.IsMatch(strValue, "^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$", System.Text.RegularExpressions.RegexOptions.IgnoreCase) End Function Private Function IsGuidWithBraces(ByRef strValue As String) As Boolean If String.IsNullOrEmpty(strValue) Then Return False End If Return System.Text.RegularExpressions.Regex.IsMatch(strValue, "^\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\}$", System.Text.RegularExpressions.RegexOptions.IgnoreCase) End Function 

用C#中的扩展方法

 public static bool IsGUID(this string text) { return Guid.TryParse(text, out Guid guid); }