C# 在不抛出异常的情况下测试字符串是否为 guid?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/104850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 14:10:21  来源:igfitidea点击:

Test if string is a guid without throwing exceptions?

提问by Ian Boyd

I want to try to convert a string to a Guid, but I don't want to rely on catching exceptions (

我想尝试将字符串转换为 Guid,但我不想依赖捕获异常 (

  • for performance reasons - exceptions are expensive
  • for usability reasons - the debugger pops up
  • for design reasons - the expected is not exceptional
  • 出于性能原因 - 异常是昂贵的
  • 出于可用性原因 - 调试器弹出
  • 出于设计原因 - 预期并不例外

In other words the code:

换句话说,代码:

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

is not suitable.

不合适。

I would try using RegEx, but since the guid can be parenthesis wrapped, brace wrapped, none wrapped, makes it hard.

我会尝试使用 RegEx,但由于 guid 可以用括号包裹、大括号包裹、不包裹,所以很难。

Additionally, I thought certain Guid values are invalid(?)

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



Update 1

更新 1

ChristianKhad a good idea to catch only FormatException, rather than all. Changed the question's code sample to include suggestion.

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



Update 2

更新 2

Why worry about thrown exceptions? Am I really expecting invalid GUIDs all that often?

为什么要担心抛出的异常?我真的经常期待无效的 GUID 吗?

The answer is yes. That is why I am using TryStrToGuid - I amexpecting bad data.

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

Example 1Namespace extensions can be specified by appending a GUID to a folder name. I might be parsing folder names, checking to see if the text after the final .is a GUID.

示例 1可以通过将 GUID 附加到文件夹名称来指定命名空间扩展。我可能正在解析文件夹名称,检查最终. 是一个 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

Example 2I might be running a heavily used web-server wants to check the validity of some posted back data. I don't want invalid data tying up resources 2-3 orders of magnitude higher than it needs to be.

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

Example 3I might be parsing a search expression entered by a user.

示例 3我可能正在解析用户输入的搜索表达式。

enter image description here

在此处输入图片说明

If they enter GUID's I want to process them specially (such as specifically searching for that object, or highlight and format that specific search term in the response text.)

如果他们输入 GUID,我想专门处理它们(例如专门搜索该对象,或在响应文本中突出显示和格式化该特定搜索词。)



Update 3 - Performance benchmarks

更新 3 - 性能基准

Test converting 10,000 good Guids, and 10,000 bad Guids.

测试转换 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

p.s. I shouldn't have to justify a question.

ps我不应该证明一个问题。

采纳答案by Ian Boyd

Performance Benchmarks

性能基准

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 (Fastest) Answer:

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);
    }
}


Bottom line: If you need to check if a string is a guid, and you care about performance, use COM Interop.

底线:如果您需要检查字符串是否为 guid,并且您关心性能,请使用 COM Interop。

If you need to convert a guid in String representation to a Guid, use

如果您需要将字符串表示形式的 guid 转换为 Guid,请使用

new Guid(someString);

回答by Michael Burr

Run the potential GUID though a RegEx or some custom code that does a sanity check to ensure the strig at least looks like a GUID and consists only of valid characters (and maybe that it seems to fit the overall format). If it doesn't pass the sanity check return an error - that'll probably weed out the vast majority of invalid strings.

通过 RegEx 或一些执行健全性检查的自定义代码运行潜在的 GUID,以确保 srig 至少看起来像一个 GUID 并且只包含有效字符(也许它似乎适合整体格式)。如果它没有通过健全性检查,则返回错误 - 这可能会清除绝大多数无效字符串。

Then convert the string as you have above, still catching the exception for the few invalid strings that get through the sanity check.

然后像上面那样转换字符串,仍然捕获少数通过健全性检查的无效字符串的异常。

Jon Skeet did an analysis for something similar for parsing Ints (before TryParse was in the Framework): Checking if a string can be converted to Int32

Jon Skeet 对解析 Ints 的类似内容进行了分析(在 TryParse 在框架中之前): 检查字符串是否可以转换为 Int32

However, as AnthonyWJonesindicated you probably shouldn't be worrying about this.

但是,正如AnthonyWJones指出的那样,您可能不必担心这一点。

回答by AnthonyWJones

You're not going to like this but what makes you think that catching the exception is going to be slower?

您不会喜欢这样,但是是什么让您认为捕获异常会变慢?

How many failed attempts to parse a GUID are you expecting in comparison with successful ones?

与成功的尝试相比,您期望解析 GUID 的尝试失败多少次?

My advice is use the function you've just created and profile your code. If you find that this function is truely a hotspot thenfix it but not before.

我的建议是使用您刚刚创建的函数并分析您的代码。如果您发现该功能是忠实地的热点,然后解决它,但不是之前。

回答by pdavis

Well, here is the regex you will need...

好吧,这是您需要的正则表达式...

^[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}(}})$

But that is just for starters. You will also have to verify that the various parts such as the date/time are within acceptable ranges. I can't imagine this being any faster than the try/catch method that you have already outlined. Hopefully you aren't receiving that many invalid GUIDs to warrant this type of check!

但这只是初学者。您还必须验证日期/时间等各个部分是否在可接受的范围内。我无法想象这比您已经概述的 try/catch 方法更快。希望您没有收到那么多无效的 GUID 来保证进行此类检查!

回答by Ilya Ryzhenkov

As far as I know, there is no something like Guid.TryParse in mscrolib. According to Reference Source, Guid type has mega-complex constructor which checks all kinds of guid formats and tries to parse them. There is no helper method you can call, even via reflection. I think you have to search for 3rd party Guid parsers, or write your own.

据我所知,mscrolib 中没有类似 Guid.TryParse 的东西。根据参考来源,Guid 类型具有超级复杂的构造函数,它检查各种 guid 格式并尝试解析它们。即使通过反射,也没有您可以调用的辅助方法。我认为您必须搜索 3rd 方 Guid 解析器,或者自己编写。

回答by rupello

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

回答by Josef

While it istrue that using errors is more expensive, most people believe that a majority of their GUIDs are going to be computer generated so a TRY-CATCHisn't too expensive since it only generates cost on the CATCH. You can prove this to yourself with a simple test of the two(user public, no password).

虽然这事实,使用错误比较贵,大多数人认为,他们大多数的GUID将是计算机生成所以TRY-CATCH,因为它仅在产生费用不算太贵CATCH。您可以通过对两者的简单测试来证明这一点(用户 public,无密码)。

Here you go:

干得好:

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;
  }

回答by Christian.K

I would at least rewrite it as:

我至少会把它改写为:

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

You don't want to say "invalid GUID" on SEHException, ThreadAbortException or other fatal or non-related stuff.

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

Update: Starting with .NET 4.0, there is a new set of methods available for Guid:

更新:从 .NET 4.0 开始,有一组新的 Guid 方法可用:

Really, those should be used (if only for the fact, that they are not "naively" implemented using try-catch internally).

真的,应该使用那些(如果只是因为它们不是在内部使用 try-catch “天真地”实现的事实)。

回答by Mark Brackett

Interop is slower than just catching the exception:

互操作比仅仅捕获异常要慢:

In the happy path, with 10,000 Guids:

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

Exception:    26ms
Interop:   1,201ms

In the unhappy path:

在不幸的道路上:

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

It's more consistent, but it's also consistently slower. Seems to me you'd be better off configuring your debugger to only break on unhandled exceptions.

它更加一致,但也始终较慢。在我看来,您最好将调试器配置为仅在未处理的异常时中断。

回答by Mark Brackett

if TypeOf ctype(myvar,Object) Is Guid then .....

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