185

文字列をGUIDに変換したいのですが、例外のキャッチに依存したくありません(

  • パフォーマンス上の理由から-例外は高額です
  • 使いやすさの理由から-デバッガーがポップアップします
  • 設計上の理由から-期待されるものは例外ではありません

言い換えれば、コード:

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

適切ではない。

RegExを使用してみますが、GUIDは括弧でラップしたり、ブレースでラップしたり、ラップしなかったりする可能性があるため、難しくなります。

さらに、特定のGUID値が無効だと思いました(?)


アップデート1

ChristianKFormatExceptionは、すべてではなく、のみをキャッチすることをお勧めしました。質問のコードサンプルを変更して、提案を含めました。


アップデート2

スローされた例外について心配するのはなぜですか?無効なGUIDが頻繁に発生することを本当に期待していますか?

答えはイエスです。それが私がTryStrToGuidを使用している理由です-私悪いデータを期待しています。

例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

例2頻繁に使用されるWebサーバーを実行していて、ポストバックされたデータの有効性を確認したい場合があります。無効なデータが必要以上に2〜3桁高いリソースを拘束することは望ましくありません。

例3ユーザーが入力した検索式を解析している可能性があります。

ここに画像の説明を入力してください

それらがGUIDを入力した場合、それらを特別に処理したいと思います(たとえば、そのオブジェクトを具体的に検索したり、応答テキストでその特定の検索語を強調表示してフォーマットしたりします)。


アップデート3-パフォーマンスベンチマーク

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

ps質問を正当化する必要はありません。

4

19 に答える 19

109

パフォーマンス ベンチマーク

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

結論: 文字列が GUID かどうかを確認する必要があり、パフォーマンスを重視する場合は、COM 相互運用機能を使用してください。

文字列表現の GUID を GUID に変換する必要がある場合は、次を使用します。

new Guid(someString);
于 2008-11-13T19:00:03.017 に答える
91

.net 4.0 が利用可能になったら、 を使用できますGuid.TryParse()

于 2009-12-31T04:58:34.490 に答える
67

これは気に入らないでしょうが、例外のキャッチが遅くなると思う理由は何ですか?

成功したものと比較して、GUIDの解析に失敗した試行の数を予想していますか?

私のアドバイスは、作成したばかりの関数を使用して、コードのプロファイルを作成することです。この関数が本当にホットスポットであることがわかった場合は修正しますが、以前は修正しませんでした。

于 2008-09-19T19:41:21.003 に答える
42

.NET 4.0 では、次のように記述できます。

public static bool IsValidGuid(string str)
{
    Guid guid;
    return Guid.TryParse(str, out guid);
}
于 2011-06-30T10:18:11.950 に答える
22

少なくとも次のように書き直します。

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

SEHException、ThreadAbortException、またはその他の致命的または関連のないもので「無効な GUID」と言いたくありません。

更新: .NET 4.0 以降では、Guid で使用できる新しいメソッドのセットがあります。

実際には、それらを使用する必要があります (内部で try-catch を使用して「単純に」実装されていないという事実のみが理由である場合)。

于 2008-09-26T04:49:46.363 に答える
14

相互運用は、例外をキャッチするよりも遅くなります。

10,000 人のガイドがいる幸せな道のり:

Exception:    26ms
Interop:   1,201ms

不幸な道で:

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

より一貫性がありますが、一貫して遅くなります。未処理の例外でのみ中断するようにデバッガーを構成する方がよいようです。

于 2008-11-14T17:02:10.183 に答える
10

さて、ここにあなたが必要とする正規表現があります...

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

しかし、それは初心者向けです。また、日付/時刻などのさまざまな部分が許容範囲内であることを確認する必要があります。これが、既に概説した try/catch メソッドよりも高速であるとは想像できません。この種のチェックを正当化するほど多くの無効な GUID を受け取っていないことを願っています!

于 2008-09-19T19:46:19.870 に答える
6

使いやすさの理由から - デバッガーがポップアップします

try/catch アプローチを使用する場合は、 [System.Diagnostics.DebuggerHidden] 属性を追加して、スロー時にブレークするように設定した場合でもデバッガーがブレークしないようにすることができます。

于 2009-05-27T17:54:49.447 に答える
5

私も同様の状況にありましたが、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;
    }
}
于 2009-07-26T19:56:40.260 に答える
5

エラーを使用するとよりコストがかかるの事実ですが、ほとんどの人は、GUID の大部分がコンピューターで生成されるため、TRY-CATCHはコストがかかるだけなので、 はそれほど高価ではないと考えていCATCHます。これは、 2 つの簡単なテスト(ユーザー公開、パスワードなし) で証明できます。

どうぞ:

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;
  }
于 2008-09-19T19:52:20.213 に答える
2

正規表現または健全性チェックを実行するカスタムコードを介して潜在的なGUIDを実行し、ストライグが少なくともGUIDのように見え、有効な文字のみで構成されていることを確認します(おそらく、全体の形式に適合しているようです)。健全性チェックに合格しなかった場合は、エラーが返されます。これにより、無効な文字列の大部分が排除される可能性があります。

次に、上記のように文字列を変換しますが、健全性チェックを通過するいくつかの無効な文字列の例外を引き続きキャッチします。

Jon Skeetは、Intの解析に似たものの分析を行いました(TryParseがフレームワークに含まれる前): 文字列をInt32に変換できるかどうかを確認します

ただし、AnthonyWJonesが示したように、おそらくこれについて心配する必要はありません。

于 2008-09-19T19:41:07.760 に答える
2

私の知る限り、mscrolib には Guid.TryParse のようなものはありません。Reference Source によると、Guid 型には、あらゆる種類の guid 形式をチェックし、それらを解析しようとする非常に複雑なコンストラクターがあります。リフレクション経由であっても、呼び出すことができるヘルパー メソッドはありません。サードパーティの Guid パーサーを検索するか、独自のパーサーを作成する必要があると思います。

于 2008-09-19T19:48:06.293 に答える
1
  • リフレクターを入手
  • Guid の .ctor(String) をコピーアンドペースト
  • 「throw new ...」のすべての出現を「return false」に置き換えます。

Guid の ctor はほぼコンパイル済みの正規表現です。そのため、例外のオーバーヘッドなしでまったく同じ動作が得られます。

  1. これはリバース エンジニアリングを構成しますか? あると思いますし、違法かもしれません。
  2. GUID フォームが変更されると壊れます。

さらにクールな解決策は、「throw new」をオンザフライで置き換えることにより、メソッドを動的に計測することです。

于 2009-05-27T17:59:13.150 に答える
1
 bool IsProbablyGuid(string s)
    {
        int hexchars = 0;
        foreach(character c in string s)
        {
           if(IsValidHexChar(c)) 
               hexchars++;          
        }
        return hexchars==32;
    }
于 2008-09-19T19:50:27.770 に答える
0

TypeOf ctype(myvar,Object) が Guid の場合 .....

于 2009-05-23T14:20:55.867 に答える
0
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
于 2009-08-20T11:54:44.483 に答える