2015-04-08 10 views
13

Se si guarda un codice sorgente per il costruttore di Guid(string)in the .NET 4.5.2 source code è come segue:Perché il codice sorgente per il costruttore Guid contiene la riga "this = Guid.Empty"?

public Guid(String g) 
{ 
    if (g==null) { 
     throw new ArgumentNullException("g"); 
    } 
    Contract.EndContractBlock(); 
    this = Guid.Empty; 

    GuidResult result = new GuidResult(); 
    result.Init(GuidParseThrowStyle.All); 
    if (TryParseGuid(g, GuidStyles.Any, ref result)) { 
     this = result.parsedGuid; 
    } 
    else { 
     throw result.GetGuidParseException(); 
    } 
} 

La domanda è: qual è lo scopo della linea this = Guid.Empty;?

Da quello che posso vedere se string g può essere analizzato correttamente nel metodo TryParseGuid quindi verrà assegnato this. Se non può, allora verrà lanciata un'eccezione.

Supponiamo che hai scritto:

var guid = new Guid("invalidguid"); 

Questo potrebbe causare un'eccezione e il valore di guid sarebbero undefined Vorrei assumere. Quindi, perché la necessità di assegnare this a Guid.Empty?

+1

Valore vuoto predefinito. semplice come quella.Probabilmente è un po 'di supervisione perché non ha senso perché sta facendo un TryParse e assegnandolo in caso di successo altrimenti genera un'eccezione. Ma probabilmente ancora, quando lo guardi, anche se viene lanciata l'eccezione, il valore predefinito sarà Guid.Empty sulla linea # 6. Ora il consumatore può comunque rilevare un'eccezione e continuare a utilizzare questa particolare "istanza" della classe Guid ... ma il valore sarebbe vuoto per impostazione predefinita. –

+0

@HighCore - Non ho guardato il codice ATM, tuttavia è possibile assegnarlo a questo se, naturalmente, è del tipo corretto. –

+0

@Ahmedilyas [non proprio, no] (http://ideone.com/UKmYY2). –

risposta

-1

Guid.Empty assegna tutti gli zeri. Se non si desidera assegnare tutti gli zeri, è possibile utilizzare il metodo Guid.NewGuid(). Qui viene assegnato il valore predefinito.

+2

Non so come questo risponda alla domanda perché 'this = Guild.Empty' è tecnicamente superfluo - il valore predefinito di struct è già" tutti zeri "anche prima di quell'assegnazione. –

+0

@Shyju, non riesco a vedere come risponde alla domanda. E come si può utilizzare Guid.NewGuid() nel codice sorgente di Guid a meno che non si lavori in MS? –

+0

@ d_z, ho cercato di spiegare cosa significa quella linea. Non sono interessato a nessuna controversia. Ho solo fatto un passo avanti e ho spiegato, se domani la SM pensa, perché non possiamo assegnare un non-zero, dovranno generare qualche guida usando il metodo NewGuid, non è vero? – Shyju

7

Questa è più una questione di stile che altro - dal punto di vista funzionale è superflua e il compilatore può persino ottimizzare l'assegnazione a Guid.Empty nel codice generato.

La codifica difensiva raccomanda che la variabile debba sempre avere un valore iniziale esplicitamente assegnato. Perché? Perché riduce l'ambiguità, specialmente per coloro che non hanno familiarità con i dettagli di un determinato linguaggio/piattaforma di programmazione. Ad esempio si potrebbe chiedere:

  • Qual è il valore predefinito di una guida?
  • Dovrebbe essere un nuovo Guid?
  • Dovrebbe essere tutto a zero?
  • Dovrebbe essere un altro stato indeterminato?

In affermando:

Guid id = Guid.Empty; 

Tutte queste domande sono essenzialmente risposta leggendo il codice e senza dover ricorrere alla lettura della documentazione e/o la fonte. Inoltre, è esplicito che la variabile id inizia a essere vuota e indica al lettore che avrà il suo valore impostato successivamente nel codice.

+0

Stai dicendo che questa affermazione 'this = Guid.Empty;' nel costruttore rende più facile la comprensione del codice? Non per me. Per me solleva una domanda "come sarà usato quando viene sollevata un'eccezione". Per le variabili sono d'accordo che dovresti inizializzare, ma in questo caso è confuso. –

+1

Se è per le pratiche di codifica difensive, perché non è 'this = Guid.Empty;' posto in alto prima del controllo Null sulla stringa? Anche nessuno degli altri costruttori ha questo. Perché dovrebbe essere? – neodymium

+0

"La codifica difensiva raccomanda che la variabile debba sempre avere un valore iniziale esplicitamente assegnato." Questo non è esattamente difensivo perché disabilita la capacità del compilatore di avvisarti dell'uso di valori falsi. Questa pratica * aumenta * il rischio e aumenta l'ingombro. – usr

1

In sostanza hai risposto alla tua stessa domanda in una certa misura.

Se viene generato un errore e la riga this = Gui.Empty non è presente, potrebbe verificarsi un beign indefinito assegnato alla variabile. Quindi anche se hai un'eccezione, la variabile stessa non è definita.

Ci possono essere casi d'uso in cui solo quello è un caso negativo. Ad esempio se la variabile è globale o se è necessario utilizzarla anche se la generazione della GUI è fallita o se è necessario almeno controllare un valore, ..... Pertanto, anche se viene lanciata un'eccezione, è meglio avere un valore definito (in questo caso 0) piuttosto che un valore non definito che potrebbe portare a un comportamento erratico se qualcuno BISOGNA un valore anche se il costruttore non riesce per qualche motivo.

E questa è la ragione principale per cui esiste un tale costrutto (almeno spero che microsoft abbia QUELLA ragione come onestamente a parte che non riesco a pensare ad una ragione logica per farlo).

5

Vorrei indovinare che questo è un artefatto storico.

Nella versione 3.5, il GUID è costruito sul posto, con tutte le analisi del testo incluse nel metodo di costruzione stesso.

Direi che a un certo punto gli sviluppatori hanno deciso di refactoring tutto il codice di analisi fuori in metodi di supporto , a quel punto avrebbero potuto colpire un errore di compilazione perché si deve assegnare definitivamente this prima di chiamare esempio metodi.

La struttura helper GuidResult sembra essere stata introdotta in un altro punto nel tempo, il punto in cui questi metodi di parsing potrebbero poi diventare static e lavoro contro la GuidResult piuttosto che l'effettiva Guid attualmente in costruzione - a questo punto tutto diventa semplificato di nuovo e non è richiesto quel preciso incarico.


Questo è ciò che il riflettore decompila la versione 3.5 a:

public Guid(string g) 
{ 
    if (g == null) 
    { 
     throw new ArgumentNullException("g"); 
    } 
    int startIndex = 0; 
    int parsePos = 0; 
    try 
    { 
     int num2; 
     long num3; 
     if (g.IndexOf('-', 0) >= 0) 
     { 
      string str = g.Trim(); 
      if (str[0] == '{') 
      { 
       if ((str.Length != 0x26) || (str[0x25] != '}')) 
       { 
        throw new FormatException(Environment.GetResourceString("Format_GuidInvLen")); 
       } 
       startIndex = 1; 
      } 
      else if (str[0] == '(') 
      { 
       if ((str.Length != 0x26) || (str[0x25] != ')')) 
       { 
        throw new FormatException(Environment.GetResourceString("Format_GuidInvLen")); 
       } 
       startIndex = 1; 
      } 
      else if (str.Length != 0x24) 
      { 
       throw new FormatException(Environment.GetResourceString("Format_GuidInvLen")); 
      } 
      if (((str[8 + startIndex] != '-') || (str[13 + startIndex] != '-')) || ((str[0x12 + startIndex] != '-') || (str[0x17 + startIndex] != '-'))) 
      { 
       throw new FormatException(Environment.GetResourceString("Format_GuidDashes")); 
      } 
      parsePos = startIndex; 
      this._a = TryParse(str, ref parsePos, 8); 
      parsePos++; 
      this._b = (short) TryParse(str, ref parsePos, 4); 
      parsePos++; 
      this._c = (short) TryParse(str, ref parsePos, 4); 
      parsePos++; 
      num2 = TryParse(str, ref parsePos, 4); 
      parsePos++; 
      startIndex = parsePos; 
      num3 = ParseNumbers.StringToLong(str, 0x10, 0x2000, ref parsePos); 
      if ((parsePos - startIndex) != 12) 
      { 
       throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidInvLen"), new object[0])); 
      } 
      this._d = (byte) (num2 >> 8); 
      this._e = (byte) num2; 
      num2 = (int) (num3 >> 0x20); 
      this._f = (byte) (num2 >> 8); 
      this._g = (byte) num2; 
      num2 = (int) num3; 
      this._h = (byte) (num2 >> 0x18); 
      this._i = (byte) (num2 >> 0x10); 
      this._j = (byte) (num2 >> 8); 
      this._k = (byte) num2; 
     } 
     else if (g.IndexOf('{', 0) >= 0) 
     { 
      int num5 = 0; 
      int length = 0; 
      g = EatAllWhitespace(g); 
      if (g[0] != '{') 
      { 
       throw new FormatException(Environment.GetResourceString("Format_GuidBrace")); 
      } 
      if (!IsHexPrefix(g, 1)) 
      { 
       throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidHexPrefix"), new object[] { "{0xdddddddd, etc}" })); 
      } 
      num5 = 3; 
      length = g.IndexOf(',', num5) - num5; 
      if (length <= 0) 
      { 
       throw new FormatException(Environment.GetResourceString("Format_GuidComma")); 
      } 
      this._a = ParseNumbers.StringToInt(g.Substring(num5, length), 0x10, 0x1000); 
      if (!IsHexPrefix(g, (num5 + length) + 1)) 
      { 
       throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidHexPrefix"), new object[] { "{0xdddddddd, 0xdddd, etc}" })); 
      } 
      num5 = (num5 + length) + 3; 
      length = g.IndexOf(',', num5) - num5; 
      if (length <= 0) 
      { 
       throw new FormatException(Environment.GetResourceString("Format_GuidComma")); 
      } 
      this._b = (short) ParseNumbers.StringToInt(g.Substring(num5, length), 0x10, 0x1000); 
      if (!IsHexPrefix(g, (num5 + length) + 1)) 
      { 
       throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidHexPrefix"), new object[] { "{0xdddddddd, 0xdddd, 0xdddd, etc}" })); 
      } 
      num5 = (num5 + length) + 3; 
      length = g.IndexOf(',', num5) - num5; 
      if (length <= 0) 
      { 
       throw new FormatException(Environment.GetResourceString("Format_GuidComma")); 
      } 
      this._c = (short) ParseNumbers.StringToInt(g.Substring(num5, length), 0x10, 0x1000); 
      if ((g.Length <= ((num5 + length) + 1)) || (g[(num5 + length) + 1] != '{')) 
      { 
       throw new FormatException(Environment.GetResourceString("Format_GuidBrace")); 
      } 
      length++; 
      byte[] buffer = new byte[8]; 
      for (int i = 0; i < 8; i++) 
      { 
       if (!IsHexPrefix(g, (num5 + length) + 1)) 
       { 
        throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidHexPrefix"), new object[] { "{... { ... 0xdd, ...}}" })); 
       } 
       num5 = (num5 + length) + 3; 
       if (i < 7) 
       { 
        length = g.IndexOf(',', num5) - num5; 
        if (length <= 0) 
        { 
         throw new FormatException(Environment.GetResourceString("Format_GuidComma")); 
        } 
       } 
       else 
       { 
        length = g.IndexOf('}', num5) - num5; 
        if (length <= 0) 
        { 
         throw new FormatException(Environment.GetResourceString("Format_GuidBraceAfterLastNumber")); 
        } 
       } 
       uint num8 = (uint) Convert.ToInt32(g.Substring(num5, length), 0x10); 
       if (num8 > 0xff) 
       { 
        throw new FormatException(Environment.GetResourceString("Overflow_Byte")); 
       } 
       buffer[i] = (byte) num8; 
      } 
      this._d = buffer[0]; 
      this._e = buffer[1]; 
      this._f = buffer[2]; 
      this._g = buffer[3]; 
      this._h = buffer[4]; 
      this._i = buffer[5]; 
      this._j = buffer[6]; 
      this._k = buffer[7]; 
      if ((((num5 + length) + 1) >= g.Length) || (g[(num5 + length) + 1] != '}')) 
      { 
       throw new FormatException(Environment.GetResourceString("Format_GuidEndBrace")); 
      } 
      if (((num5 + length) + 1) != (g.Length - 1)) 
      { 
       throw new FormatException(Environment.GetResourceString("Format_ExtraJunkAtEnd")); 
      } 
     } 
     else 
     { 
      string s = g.Trim(); 
      if (s.Length != 0x20) 
      { 
       throw new FormatException(Environment.GetResourceString("Format_GuidInvLen")); 
      } 
      for (int j = 0; j < s.Length; j++) 
      { 
       char c = s[j]; 
       if ((c < '0') || (c > '9')) 
       { 
        char ch2 = char.ToUpper(c, CultureInfo.InvariantCulture); 
        if ((ch2 < 'A') || (ch2 > 'F')) 
        { 
         throw new FormatException(Environment.GetResourceString("Format_GuidInvalidChar")); 
        } 
       } 
      } 
      this._a = ParseNumbers.StringToInt(s.Substring(startIndex, 8), 0x10, 0x1000); 
      startIndex += 8; 
      this._b = (short) ParseNumbers.StringToInt(s.Substring(startIndex, 4), 0x10, 0x1000); 
      startIndex += 4; 
      this._c = (short) ParseNumbers.StringToInt(s.Substring(startIndex, 4), 0x10, 0x1000); 
      startIndex += 4; 
      num2 = (short) ParseNumbers.StringToInt(s.Substring(startIndex, 4), 0x10, 0x1000); 
      startIndex += 4; 
      parsePos = startIndex; 
      num3 = ParseNumbers.StringToLong(s, 0x10, startIndex, ref parsePos); 
      if ((parsePos - startIndex) != 12) 
      { 
       throw new FormatException(string.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("Format_GuidInvLen"), new object[0])); 
      } 
      this._d = (byte) (num2 >> 8); 
      this._e = (byte) num2; 
      num2 = (int) (num3 >> 0x20); 
      this._f = (byte) (num2 >> 8); 
      this._g = (byte) num2; 
      num2 = (int) num3; 
      this._h = (byte) (num2 >> 0x18); 
      this._i = (byte) (num2 >> 0x10); 
      this._j = (byte) (num2 >> 8); 
      this._k = (byte) num2; 
     } 
    } 
    catch (IndexOutOfRangeException) 
    { 
     throw new FormatException(Environment.GetResourceString("Format_GuidUnrecognized")); 
    } 
} 

Così, ovviamente, compreso tra 3,5 e 4.5.2, sono stati introdotti i metodi di supporto. Da lì, si suppone che i metodi helper siano stati introdotti per primi (come metodi di istanza, e quindi richiedendo assegnazioni definitive) e che successivamente sia stata introdotta la struttura helper (GuidResult).

Se copi tutto il codice 4.5.2 in un nuovo progetto, rimuovi la riga che chiedi e compili, tutto va ancora bene.


Probabilmente per sostenere l'introduzione di Parse/TryParse metodi su Guid che sembrano sono apparsi nel lasso di tempo di .NET 4.0.

0

Penso di avere una spiegazione soddisfacente di quella linea.

In un costruttore di parametri con struct è necessario assegnare tutti i campi, altrimenti si ottiene qualcosa come "Errore 1 campo" My.Guid._j "deve essere assegnato completamente prima che il controllo venga restituito al chiamante C: \ Work \ Projects \ Guid \ Guid \ Program.cs 153 16 Guid "

AFAIU, il motivo per cui è necessario farlo è che quando si chiama un costruttore di struct con parametri si sovrascrive il comportamento di inizializzazione della struttura CLR predefinito che azzera appena la memoria allocata invece di inizializzare ogni campo. (Potrei avere torto comunque)

Normalmente chiameresti un costruttore senza parametri con :this() da qualsiasi costruttore parametrizzato per inizializzare tutti i membri e che funzionerebbe anche qui. Ma ciò aggiunge una chiamata extra e allocazioni di memoria extra, quindi per l'ottimizzazione delle prestazioni hanno sostituito una chiamata al costruttore predefinito con assegnazione a un campo statico di sola lettura Guid.Empty.

Problemi correlati