Kiểm tra nếu chuỗi là một hướng dẫn mà không ném ngoại lệ?


180

Tôi muốn thử chuyển đổi một chuỗi thành Hướng dẫn, nhưng tôi không muốn dựa vào việc bắt ngoại lệ (

  • vì lý do hiệu suất - ngoại lệ đắt
  • vì lý do khả năng sử dụng - trình gỡ lỗi bật lên
  • vì lý do thiết kế - dự kiến ​​không phải là ngoại lệ

Nói cách khác, mã:

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

không phù hợp

Tôi sẽ thử sử dụng RegEx, nhưng vì hướng dẫn có thể được gói ngoặc đơn, bọc nẹp, không được bọc, làm cho nó khó khăn.

Ngoài ra, tôi nghĩ rằng các giá trị Hướng dẫn nhất định không hợp lệ (?)


Cập nhật 1

ChristianK có một ý tưởng tốt để chỉ bắt FormatException, hơn là tất cả. Đã thay đổi mẫu mã của câu hỏi để bao gồm gợi ý.


Cập nhật 2

Tại sao lo lắng về ngoại lệ ném? Tôi có thực sự mong đợi GUID không hợp lệ thường xuyên không?

Câu trả lời là . Đó là lý do tại sao tôi đang sử dụng TryStrToGuid - Tôi đang mong đợi dữ liệu xấu.

Ví dụ 1 Phần mở rộng không gian tên có thể được chỉ định bằng cách thêm GUID vào tên thư mục . Tôi có thể phân tích tên thư mục, kiểm tra xem văn bản sau khi kết thúc . là một HƯỚNG DẪN.

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

Ví dụ 2 Tôi có thể đang chạy một máy chủ web được sử dụng nhiều muốn kiểm tra tính hợp lệ của một số dữ liệu được đăng lại. Tôi không muốn dữ liệu không hợp lệ buộc tài nguyên cao hơn 2-3 bậc so với mức cần thiết.

Ví dụ 3 Tôi có thể phân tích một biểu thức tìm kiếm được nhập bởi người dùng.

nhập mô tả hình ảnh ở đây

Nếu họ nhập GUID, tôi muốn xử lý chúng đặc biệt (chẳng hạn như tìm kiếm cụ thể đối tượng đó hoặc tô sáng và định dạng cụm từ tìm kiếm cụ thể trong văn bản phản hồi.)


Cập nhật 3 - Điểm chuẩn hiệu suất

Kiểm tra chuyển đổi 10.000 Hướng dẫn tốt và 10.000 Hướng dẫn xấu.

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 tôi không cần phải biện minh cho một câu hỏi.


7
Tại sao trên thế giới đây là một wiki cộng đồng?
Jeff

36
Bạn đúng; bạn không cần phải biện minh cho một câu hỏi. Tuy nhiên, tôi đọc lời biện minh với sự thích thú (vì nó rất giống với lý do tại sao tôi ở đây đọc bài này). Vì vậy, cảm ơn cho sự biện minh tuyệt vời.
bw

2
@Jeff có khả năng vì OP đã chỉnh sửa nó hơn 10 lần - xem meta trên wiki cộng đồng
Marijn

3
Hãy tiếp tục tìm kiếm trên trang này để biết các giải pháp với Guid.TryPude hoặc Guid.TryPudeExact. Với .NET 4.0 +, giải pháp trên không phải là thanh lịch nhất
dplante

1
@dplante Khi tôi đặt câu hỏi ban đầu vào năm 2008, không có 4.0. Đó là lý do tại sao câu hỏi, và câu trả lời được chấp nhận, là cách họ đang có.
Ian Boyd

Câu trả lời:


107

Điểm chuẩn hiệu suất

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 (nhanh nhất) Trả lời:

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

Dòng dưới cùng: Nếu bạn cần kiểm tra xem một chuỗi có phải là hướng dẫn hay không và bạn quan tâm đến hiệu suất, hãy sử dụng COM Interop.

Nếu bạn cần chuyển đổi một hướng dẫn trong biểu diễn Chuỗi thành Hướng dẫn, hãy sử dụng

new Guid(someString);

8
Bạn đã chạy chúng với trình gỡ lỗi bật hay tắt? Hiệu suất của ném ngoại lệ được cải thiện nhiều lần mà không cần gắn trình gỡ lỗi.
Daniel T.

cảm ơn bạn. Tôi đã định tự đặt câu hỏi này. Vui mừng tôi tìm thấy câu trả lời của bạn.
David

Tôi đã tạo một tệp mới có tên PInvoke.cs với đoạn mã không gian tên PInvoke từ phía trên, nhưng tôi không thể làm cho mã hoạt động được. Khi tôi gỡ lỗi, tôi thấy rằng kết quả của CLSIDFromString là LUÔN LUÔN. Tôi đã thử thay đổi đường dây gọi thành: int hresult = PInvoke.ObjBase.CLSIDFromString (Guid.NewGuid (). ToString (), out value); nhưng nó vẫn luôn tiêu cực. Tôi đang làm gì sai?
JALLRED


65

Bạn sẽ không thích điều này nhưng điều gì khiến bạn nghĩ rằng việc bắt ngoại lệ sẽ chậm hơn?

Có bao nhiêu lần thất bại để phân tích một GUID mà bạn mong đợi so với những người thành công?

Lời khuyên của tôi là sử dụng chức năng bạn vừa tạo và hồ sơ mã của bạn. Nếu bạn thấy rằng chức năng này thực sự là một điểm nóng thì hãy sửa nó nhưng không phải trước đó.


2
Câu trả lời tốt, tối ưu hóa sớm là gốc rễ của mọi tội lỗi.
Kev

33
Đó là hình thức kém để dựa vào các ngoại lệ không phải là ngoại lệ. Đó là một thói quen xấu mà tôi không muốn ai mắc phải. Và tôi đặc biệt không muốn làm điều đó trong một thói quen thư viện nơi mọi người sẽ tin tưởng rằng nó hoạt động tốt.
Ian Boyd

Ẩn danh, câu hỏi ban đầu của bạn nêu hiệu suất là lý do bạn muốn tránh ngoại lệ. Nếu không phải vậy thì có lẽ bạn nên điều chỉnh câu hỏi của mình.
AnthonyWJones

6
Ngoại lệ nên được sử dụng trong ý nghĩa của các trường hợp EXCEPTIONNAL: không được nhà phát triển quản lý. Tôi là đối thủ của Microsoft, 'tất cả các ngoại lệ' trong cách quản lý lỗi. Quy tắc lập trình phòng thủ. Vui lòng các nhà phát triển khung của Microsoft, xem xét thêm 'TryPude' vào lớp Hướng dẫn.
Mose

14
để đáp lại nhận xét của riêng tôi => Guid.TryPude đã được thêm vào khung 4.0 --- msdn.microsoft.com/en-us/l Library / trộm --- thxs MS cho phản ứng nhanh như vậy;)
Mose

39

Trong .NET 4.0 bạn có thể viết như sau:

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

3
Đây thực sự nên là một trong những câu trả lời hàng đầu.
Tom Lint

21

Tôi ít nhất sẽ viết lại nó như sau:

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

Bạn không muốn nói "GUID không hợp lệ" trên SEHException, ThreadAbortException hoặc các nội dung gây tử vong hoặc không liên quan khác.

Cập nhật : Bắt đầu với .NET 4.0, có một bộ phương thức mới có sẵn cho Guid:

Thực sự, những thứ đó nên được sử dụng (nếu chỉ vì thực tế, chúng không được "thực hiện" một cách ngây thơ bằng cách sử dụng thử bắt trong nội bộ).


13

Interop chậm hơn là chỉ bắt ngoại lệ:

Trong con đường hạnh phúc, với 10.000 Hướng dẫn:

Exception:    26ms
Interop:   1,201ms

Trong con đường bất hạnh:

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

Nó phù hợp hơn, nhưng nó cũng liên tục chậm hơn. Dường như với tôi, bạn nên cấu hình trình gỡ lỗi của mình để chỉ phá vỡ các ngoại lệ chưa được xử lý.


"Trình gỡ lỗi của bạn chỉ phá vỡ các ngoại lệ chưa được xử lý" Không phải là một tùy chọn.
Ian Boyd

1
@Ian Boyd - Nếu bạn đang sử dụng bất kỳ phiên bản VS nào (bao gồm cả Express), thì đó một tùy chọn. msdn.microsoft.com/en-us/l Library / 038tzxdw.aspx .
Mark Brackett

1
ý tôi là nó không phải là một lựa chọn khả thi Giống như, "Thất bại không phải là một lựa chọn." Đây một lựa chọn, nhưng tôi sẽ không sử dụng.
Ian Boyd

9

Chà, đây là regex bạn sẽ cần ...

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

Nhưng đó chỉ là cho người mới bắt đầu. Bạn cũng sẽ phải xác minh rằng các phần khác nhau như ngày / giờ nằm ​​trong phạm vi chấp nhận được. Tôi không thể tưởng tượng điều này nhanh hơn phương pháp thử / bắt mà bạn đã vạch ra. Hy vọng rằng bạn không nhận được nhiều GUID không hợp lệ để đảm bảo loại séc này!


Ừm, IIRC GUID được tạo từ dấu thời gian thường được coi là một ý tưởng tồi và loại khác (loại 4) hoàn toàn randome
BCS

5

vì lý do khả năng sử dụng - trình gỡ lỗi bật lên

Nếu bạn đang sử dụng phương pháp thử / bắt, bạn có thể thêm thuộc tính [System.Diagnostics.DebuggerHidden] để đảm bảo trình gỡ lỗi không bị hỏng ngay cả khi bạn đã đặt nó ở chế độ ném.


4

Trong khi nó đúng rằng việc sử dụng lỗi này là tốn kém hơn, hầu hết mọi người tin rằng đa số các GUID của họ sẽ được máy tính tạo nên một TRY-CATCHkhông phải là quá đắt vì nó chỉ tạo ra chi phí trên CATCH. Bạn có thể chứng minh điều này với chính mình bằng một thử nghiệm đơn giản của cả hai (người dùng công khai, không có mật khẩu).

Ở đây bạn đi:

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

4

Tôi đã có một tình huống tương tự và tôi nhận thấy rằng hầu như không bao giờ là chuỗi 36 ký tự không hợp lệ. Vì vậy, dựa trên thực tế này, tôi đã thay đổi mã của bạn một chút để có hiệu suất tốt hơn trong khi vẫn giữ cho nó đơn giản.

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

1
Guid chấp nhận nhiều hơn chỉ là dạng chuỗi nét đứt trong ctor của nó. GUID có thể có các dấu ngoặc nhọn bao quanh bằng dấu gạch ngang hoặc không có dấu gạch ngang hoặc dấu ngoặc nhọn. Mã này sẽ tạo ra các phủ định sai khi được sử dụng bởi các dạng chuỗi thay thế nhưng cũng hoàn toàn hợp lệ.
Chris Charabaruk

1
Để theo dõi, độ dài hợp lệ cho GUID dạng chuỗi lần lượt là 32, 36 và 38 - hex, dấu gạch ngang và dấu ngoặc kép có dấu gạch ngang.
Chris Charabaruk

1
@Chris, quan điểm của bạn là hợp lệ, nhưng ý tưởng về sự tỉnh táo của @JBrooks kiểm tra GUID tiềm năng trước khi tham gia thử / bắt có ý nghĩa, đặc biệt nếu đầu vào nghi ngờ là phổ biến. Có thể một cái gì đó như if (value == null || value. Chiều dài <30 || value.length> 40) {value = Guid.Empty; return false;}
bw

1
Thật vậy, điều đó sẽ tốt hơn, mặc dù tôi sẽ giữ phạm vi chặt chẽ hơn, 32., 38 chứ không phải 30..40.
Chris Charabaruk

2

Theo như tôi biết, không có thứ gì giống như Guid.TryPude trong mscrolib. Theo Nguồn tham khảo, loại Guid có hàm tạo phức tạp cực lớn kiểm tra tất cả các loại định dạng hướng dẫn và cố gắng phân tích chúng. Không có phương thức trợ giúp nào bạn có thể gọi, thậm chí thông qua sự phản chiếu. Tôi nghĩ rằng bạn phải tìm kiếm các trình phân tích cú pháp Guid của bên thứ 3 hoặc tự viết.


2

Chạy GUID tiềm năng mặc dù RegEx hoặc một số mã tùy chỉnh kiểm tra độ tỉnh táo để đảm bảo strig ít nhất trông giống như GUID và chỉ bao gồm các ký tự hợp lệ (và có thể phù hợp với định dạng chung). Nếu nó không vượt qua kiểm tra độ tỉnh táo sẽ trả về một lỗi - điều đó có thể sẽ loại bỏ phần lớn các chuỗi không hợp lệ.

Sau đó chuyển đổi chuỗi như bạn có ở trên, vẫn bắt ngoại lệ cho một vài chuỗi không hợp lệ có được thông qua kiểm tra độ tỉnh táo.

Jon Skeet đã thực hiện một phân tích cho một cái gì đó tương tự để phân tích Ints (trước khi TryPude nằm trong Framework): Kiểm tra xem một chuỗi có thể được chuyển đổi thành Int32 không

Tuy nhiên, như AnthonyWJones chỉ ra rằng có lẽ bạn không nên lo lắng về điều này.


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

"-" "{" "}" ("và") "không phải là ký tự hex hợp lệ, nhưng hợp lệ trong chuỗi hướng dẫn.
Preston Guillot

2
và mã này sẽ hoạt động hoàn hảo nếu chuỗi hướng dẫn đầu vào chứa các ký tự không phải hex đó
rupello

1
  • Nhận phản xạ
  • copy'n'paste Guid's .ctor (Chuỗi)
  • thay thế mọi trường hợp "ném mới ..." bằng "return false".

Guid's ctor gần như là một regex được biên dịch, theo cách đó bạn sẽ có được chính xác hành vi tương tự mà không có ngoại lệ.

  1. Liệu điều này tạo thành một kỹ thuật đảo ngược? Tôi nghĩ rằng nó làm, và như vậy có thể là bất hợp pháp.
  2. Sẽ phá vỡ nếu hình thức GUID thay đổi.

Ngay cả giải pháp làm mát sẽ là tự động thiết bị một phương pháp, bằng cách thay thế "ném mới" một cách nhanh chóng.


1
tôi đã cố gắng đánh cắp mã từ ctor, nhưng nó tham chiếu nhiều lớp riêng bên trong để thực hiện công việc hỗ trợ của nó. Hãy tin tôi, đó là lần thử đầu tiên của tôi.
Ian Boyd

1

Tôi bỏ phiếu cho liên kết GuidTryPude được đăng ở trên bởi Jon hoặc một giải pháp tương tự (IsProbinglyGuid). Tôi sẽ viết một cái giống như cho thư viện Chuyển đổi của tôi.

Tôi nghĩ rằng hoàn toàn khập khiễng rằng câu hỏi này phải quá phức tạp. Từ khóa "is" hoặc "as" sẽ ổn nếu NẾU Hướng dẫn có thể là null. Nhưng vì một số lý do, mặc dù SQL Server vẫn ổn với điều đó, .NET thì không. Tại sao? Giá trị của Guid.Empty là gì? Đây chỉ là một vấn đề ngớ ngẩn được tạo ra bởi thiết kế của .NET và nó thực sự làm tôi bực mình khi các quy ước của một ngôn ngữ tự bước đi. Câu trả lời hiệu quả nhất cho đến nay vẫn đang sử dụng COM Interop vì Framework không xử lý nó một cách duyên dáng? "Chuỗi này có thể là một GUID không?" nên là một câu hỏi dễ trả lời

Dựa vào ngoại lệ bị ném là OK, cho đến khi ứng dụng xuất hiện trên internet. Tại thời điểm đó, tôi chỉ cần thiết lập một cuộc tấn công từ chối dịch vụ. Ngay cả khi tôi không bị "tấn công", tôi biết một số yahoo sẽ sử dụng URL hoặc có thể bộ phận tiếp thị của tôi sẽ gửi một liên kết không đúng định dạng, và sau đó ứng dụng của tôi phải chịu một hiệu suất khá lớn mà COULD mang lại xuống máy chủ vì tôi đã không viết mã của mình để xử lý một vấn đề KHÔNG NÊN xảy ra, nhưng tất cả chúng ta đều biết S H HẠNH PHÚC.

Điều này làm mờ dòng một chút về "Ngoại lệ" - nhưng điểm mấu chốt, ngay cả khi sự cố không thường xuyên xảy ra, nếu nó có thể xảy ra đủ số lần trong một khoảng thời gian ngắn mà ứng dụng của bạn gặp sự cố phục vụ các sản phẩm khai thác từ đó, thì tôi nghĩ rằng việc ném ngoại lệ là hình thức xấu.

TheRage3K



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

0

Với phương thức mở rộng trong C #

public static bool IsGUID(this string text)
{
    return Guid.TryParse(text, out Guid guid);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.