Pergunta

Eu quero tentar converter uma string para um Guid, mas eu não quero depender de capturar exceções (

  • por razões de desempenho - exceções são caros
  • por razões de usabilidade - o depurador aparece
  • por razões de concepção - a espera não é excepcional

Em outras palavras, o código:

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

não é adequado.

Eu tentaria usando RegEx, mas desde que o guid pode ser parêntese embrulhado, cinta enrolada, nenhum envolvido, torna difícil.

Além disso, eu pensei que certos valores Guid são inválidos (?)


Update 1

Christiank tinha um boa idéia para pegar única FormatException, ao invés de todos. Mudou amostra de código da pergunta para incluir sugestão.


Update 2

Por que se preocupar com as exceções lançadas? Estou realmente esperando GUIDs inválidos tudo o que muitas vezes?

A resposta é Sim . É por isso que eu estou usando TryStrToGuid -. I am à espera de dados ruins

Exemplo 1 Namespace extensões pode ser especificado anexando um GUID para um nome de pasta . Eu poderia estar analisar nomes de pastas, verificando se o texto após o final . é um 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

Exemplo 2 eu poderia estar executando um web-servidor muito utilizado quer para verificar a validade de alguns dados retrospectivos enviado. Eu não quero dados inválidos amarrando recursos 2-3 ordens de magnitude maior do que ele precisa ser.

Exemplo 3 eu poderia estar analisar uma expressão de pesquisa inserido por um usuário.

enter descrição da imagem aqui

Se eles entram GUID é que eu quero processá-los especialmente (como procurando especificamente para esse objeto, ou destaque e formato que termo de pesquisa específico no texto de resposta.)


Update 3 - benchmarks de desempenho

Test converter 10.000 boas Guids e 10.000 maus Guids.

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. Eu não deveria ter de justificar uma pergunta.

Foi útil?

Solução

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) Resposta:

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

A linha inferior: Se você precisa verificar se uma string é um guid, e você se preocupa com o desempenho, use COM Interop.

Se você precisa converter um guid em representação String para um Guid, uso

new Guid(someString);

Outras dicas

Uma vez .NET 4.0 está disponível, você pode usar Guid.TryParse() .

Você não vai como este, mas o que faz você pensar que captura a exceção vai ser mais lento?

Quantas tentativas para analisar uma GUID que você está esperando em comparação com os bem-sucedidos? Falhou

O meu conselho é usar a função que você acabou de criar e perfil do seu código. Se você achar que esta função é verdadeiramente um hotspot seguida, corrigi-lo, mas não antes.

Em .NET 4.0 você pode escrever como a seguir:

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

Eu, pelo menos, reescrevê-la como:

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

Você não quer dizer "GUID inválido" na SEHException, ThreadAbortException ou outras coisas fatal ou não-relacionado.

Atualizar : Começando com .NET 4.0, há um novo conjunto de métodos disponíveis para Guid:

Realmente, aqueles deve ser usado (se apenas para o fato de que eles não são "ingenuamente" implementado usando try-catch internamente).

Interop é mais lento do que apenas captura a exceção:

No caminho feliz, com 10.000 Guids:

Exception:    26ms
Interop:   1,201ms

No caminho infeliz:

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

É mais consistente, mas também é consistentemente mais lento. Parece-me que seria melhor fora de configurar o seu depurador apenas quebrar em exceções não tratadas.

Bem, aqui é o regex você precisará ...

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

Mas isso é só para começar. Você também terá de verificar se as várias partes, tais como a data / hora estão dentro dos limites aceitáveis. Eu não posso imaginar este ser mais rápido do que o método try / catch que você já delineado. Espero que você não está recebendo que muitos GUIDs inválidos para justificar este tipo de verificação!

por razões de usabilidade - o depurador aparece

Se você estiver indo para a abordagem try / catch que você pode adicionar o atributo [System.Diagnostics.DebuggerHidden] para garantir que o depurador não quebrar mesmo se você configurá-lo para quebrar no lance.

Embora é true que o uso de erros é mais caro, a maioria das pessoas acredita que a maioria dos seus GUIDs vão ser geradas por computador de modo a TRY-CATCH não é muito caro, uma vez que só gera custos em o CATCH. Você pode provar isso a si mesmo com um teste simples do dois (público usuário, sem senha).

Aqui vai:

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

Eu tinha uma situação semelhante e notei que quase nunca foi a cadeia inválida 36 caracteres. Assim, com base neste fato, eu mudei o código um pouco para obter um melhor desempenho, enquanto ainda mantê-lo simples.

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

Tanto quanto eu sei, não há algo como Guid.TryParse em mscrolib. De acordo com a fonte de referência, tipo Guid tem construtor mega-complexos que verifica todos os tipos de formatos de GUID e tentativas para analisá-los. Não há método auxiliar você pode chamar, mesmo através de reflexão. Eu acho que você tem que procurar por 3rd party analisadores Guid, ou escreve sua própria.

Executar o GUID potencial embora um RegEx ou algum código personalizado que faz uma verificação de sanidade para garantir a strig, pelo menos, parece um GUID e consiste apenas de caracteres válidos (e talvez que parece caber no formato geral). Se ele não passar no teste de sanidade retornar um erro -. Que provavelmente vai eliminar a grande maioria das cordas inválidos

Em seguida, converter a cadeia como você tem acima, ainda captura a exceção para as poucas cordas inválidos que recebem através da verificação de sanidade.

Jon Skeet fez uma análise para algo semelhante para analisar Ints (antes TryParse estava no quadro): Verificando uma lata corda ser convertido para Int32

No entanto, como AnthonyWJones indicou que você provavelmente não deveria estar se preocupando com isso.

 bool IsProbablyGuid(string s)
    {
        int hexchars = 0;
        foreach(character c in string s)
        {
           if(IsValidHexChar(c)) 
               hexchars++;          
        }
        return hexchars==32;
    }
  • Get Refletor
  • copy'n'paste .ctor de Guid (String)
  • substituir cada ocorrência de "jogar novo ...", com "falsa retorno".

ctor de Guid é praticamente uma regex compilado, dessa forma você terá exatamente o mesmo comportamento, sem sobrecarga da exceção.

  1. Será que isso constitui uma engenharia reversa? Eu acho que ele faz, e como tal pode ser ilegal.
  2. vai quebrar se forma muda GUID.

solução mais frio Mesmo seria instrumento dinamicamente um método, substituindo "throw new" on the fly.

Eu voto para o link GuidTryParse postou acima por Jon ou uma solução similar (IsProbablyGuid). Eu estarei escrevendo um como aqueles para a minha biblioteca de conversão.

Eu acho que é totalmente coxo que esta questão tem de ser tão complicado. O "é" ou "como" palavra-chave seria muito bem se um Guid poderia ser nulo. Mas por alguma razão, embora o SQL Server é OK com isso, .NET não é. Por quê? Qual é o valor de Guid.Empty? Este é apenas um problema bobo criado pelo design do .NET, e isso realmente me incomoda quando as convenções de um passo linguagem em si. A resposta de melhor desempenho até agora tem vindo a utilizar interoperabilidade COM porque o quadro não lidar com isso graciosamente? "Pode esta seqüência de ser um GUID?" deve ser uma questão que é fácil de responder.

Baseando-se na exceção sendo lançada é OK, até que o aplicativo vai para a internet. Nesse ponto eu só me estabeleci para um ataque de negação de serviço. Mesmo se eu não receber o "atacado", eu sei que alguns yahoo vai macaco com o URL, ou talvez o meu departamento de marketing irá enviar um link mal formado, e depois a minha candidatura tem que sofrer um impacto no desempenho bastante robusto que poderia trazer desligar o servidor, porque eu não escrever o meu código para lidar com um problema que não deveria acontecer, mas todos sabemos vai acontecer.

Esta borra a linha um pouco sobre "exceção" - mas a linha de fundo, mesmo se o problema é pouco frequente, se isso pode acontecer bastante vezes em um curto período de tempo que o seu aplicativo falha manutenção das capturas de tudo, então eu acho que jogando uma exceção é má forma.

TheRage3K

Se TypeOf ctype (myvar, Object) é Guid então .....

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

Com um método de extensão em C #

public static bool IsGUID(this string text)
{
    return Guid.TryParse(text, out Guid guid);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top