文字列を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
ChristianK は、すべてではなくFormatException
のみをキャッチすることをお勧めしました。質問のコードサンプルを変更して提案を追加しました。
更新2
スローされた例外を心配するのはなぜですか?本当に頻繁に無効なGUIDを期待していますか?
答えはyesです。それが、私がTryStrToGuidを使用している理由です-Iam悪いデータを期待しています。
例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
p.s。質問を正当化する必要はありません。
パフォーマンスベンチマーク
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インタートップ(最速)回答:
/// <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);
.net 4.0が使用可能になったら、 Guid.TryParse()
を使用できます。
あなたはこれを好まないでしょうが、例外をキャッチするのが遅くなると思うのはなぜですか?
成功した試行と比較して、GUID=
私のアドバイスは、作成した関数を使用して、コードをプロファイルすることです。この関数が本当にホットスポットであることがわかったらthen修正してください。
.NET 4.0では、次のように記述できます。
public static bool IsValidGuid(string str)
{
Guid guid;
return Guid.TryParse(str, out guid);
}
少なくとも次のように書き直します。
try
{
value = new Guid(s);
return true;
}
catch (FormatException)
{
value = Guid.Empty;
return false;
}
SEHException、ThreadAbortException、またはその他の致命的または関連のないものについては、「無効なGUID」と言いたくないでしょう。
Update:.NET 4.0以降、Guidで利用可能な新しいメソッドセットがあります。
実際には、それらを使用する必要があります(事実だけのために、内部でtry-catchを使用して「単純に」実装されていない場合)。
相互運用は、例外をキャッチするよりも遅くなります。
10,000のGuidを使用した幸福な道:
Exception: 26ms
Interop: 1,201ms
不幸な道のり:
Exception: 1,150ms
Interop: 1,201ms
一貫性はありますが、一貫して低速です。私は、未処理の例外でのみブレークするようにデバッガを構成する方が良いと思われます。
さて、ここに必要な正規表現があります...
^[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を受け取っていないことを願っています!
使いやすさの理由-デバッガがポップアップします
Try/catchアプローチを使用する場合は、[System.Diagnostics.DebuggerHidden]属性を追加して、スロー時にブレークするように設定していてもデバッガーがブレークしないようにすることができます。
エラーを使用するとコストが高くなるのはisですが、ほとんどの人は、GUIDの大部分がコンピューターで生成されるため、TRY-CATCH
は、CATCH
でのみコストを生成するため、あまり高価ではありません。 two (ユーザー公開、パスワードなし)の簡単なテストでこれを自分で証明できます。
どうぞ:
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;
}
私は同様の状況にあり、無効な文字列が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;
}
}
可能性のあるGUID=を実行しますが、正規表現または健全性チェックを行うカスタムコードを使用して、少なくともGUIDのように見え、有効な文字のみで構成される(そしておそらくそれは全体のフォーマットに適合するようです。)健全性チェックに合格しない場合、エラーを返します-それはおそらく無効な文字列の大部分を取り除くでしょう。
次に、上記のように文字列を変換し、健全性チェックを通過するいくつかの無効な文字列の例外をキャッチします。
Jon Skeetは、Intsを解析するために似たようなものを分析しました(TryParseがフレームワークに存在する前): 文字列をInt32に変換できるかどうかを確認する
ただし、 AnthonyWJones が示しているように、おそらくこれについて心配する必要はありません。
私の知る限り、mscrolibにはGuid.TryParseのようなものはありません。 Reference Sourceによると、Guidタイプには、すべての種類のGUID形式をチェックして解析しようとするメガコンプレックスコンストラクターがあります。リフレクションを介しても呼び出すことができるヘルパーメソッドはありません。サードパーティのGuidパーサーを検索するか、独自のパーサーを作成する必要があると思います。
bool IsProbablyGuid(string s)
{
int hexchars = 0;
foreach(character c in string s)
{
if(IsValidHexChar(c))
hexchars++;
}
return hexchars==32;
}
Guidのctorはほとんどコンパイル済みの正規表現です。そのため、例外のオーバーヘッドなしでまったく同じ動作を得ることができます。
さらにクールなソリューションは、「throw new」をその場で置き換えることにより、メソッドを動的に計測することです。
Jon または同様のソリューション(IsProbablyGuid)によって上に投稿されたGuidTryParseリンクに投票します。変換ライブラリ用のようなものを作成します。
この質問が非常に複雑でなければならないのは、まったく不自由だと思います。 「is」または「as」キーワードは、Guidがnullになる可能性がある場合に適切です。しかし、何らかの理由で、SQL Serverはそれで問題ありませんが、.NETはそうではありません。どうして? Guid.Emptyの価値は何ですか?これは、.NETの設計によって作成された単なるばかげた問題であり、言語の規則がそれ自体に踏み込むと、本当に私を悩ませます。フレームワークがそれを適切に処理しないため、これまでのところ最高のパフォーマンスの答えはCOM相互運用を使用していますか? 「この文字列をGUIDにできますか?」答えやすい質問である必要があります。
アプリがインターネットに接続されるまで、スローされる例外に依存することは問題ありません。その時点で、サービス拒否攻撃に備えました。 「攻撃」を受けなくても、yahooがURLを使用して猿になったり、マーケティング部門が不正なリンクを送信したりする可能性があることを知っています。 SHOULD N'Tが発生しない問題を処理するためのコードを記述しなかったため、サーバーを停止しますが、私たちは皆、起こることを知っています。
これは「例外」の行を少し曖昧にします-しかし、問題がまれである場合でも、短い時間内に十分な回数発生してアプリケーションがすべてのキャッチを処理してクラッシュする場合、最終的には例外をスローすると思います悪い形。
TheRage3K
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);
}
typeOf ctype(myvar、Object)Is Guid then .....