Ich habe ein bizarres Verhalten in meinem Code festgestellt, als ich während der Codeüberprüfung versehentlich eine Zeile in einer Funktion auskommentierte. Es war sehr schwer zu reproduzieren, aber ich werde hier ein ähnliches Beispiel zeigen.
Ich habe diese Testklasse:
public class Test
{
public void GetOut(out EmailAddress email)
{
try
{
Foo(email);
}
catch
{
}
}
public void Foo(EmailAddress email)
{
}
}
Es gibt keine Zuordnung zu E-Mail in der, GetOut
die normalerweise einen Fehler auslösen würde:
Der out-Parameter 'email' muss zugewiesen werden, bevor die Steuerung die aktuelle Methode verlässt
Befindet sich EmailAddress jedoch in einer Struktur in einer separaten Assembly, wird kein Fehler erstellt und alles wird ordnungsgemäß kompiliert.
public struct EmailAddress
{
#region Constructors
public EmailAddress(string email)
: this(email, string.Empty)
{
}
public EmailAddress(string email, string name)
{
this.Email = email;
this.Name = name;
}
#endregion
#region Properties
public string Email { get; private set; }
public string Name { get; private set; }
#endregion
}
Warum erzwingt der Compiler nicht, dass E-Mail zugewiesen werden muss? Warum wird dieser Code kompiliert, wenn die Struktur in einer separaten Assembly erstellt wird, aber nicht kompiliert, wenn die Struktur in der vorhandenen Assembly definiert ist?
struct Dog{}
alles in Ordnung.Antworten:
TLDR: Dies ist ein bekannter Fehler von langer Dauer. Ich habe 2010 zum ersten Mal darüber geschrieben:
https://blogs.msdn.microsoft.com/ericlippert/2010/01/18/a-definite-assignment-anomaly/
Es ist harmlos und Sie können es ignorieren und sich selbst gratulieren, dass Sie einen etwas obskuren Fehler gefunden haben.
Oh, in gewisser Weise. Es hat nur eine falsche Vorstellung davon, welche Bedingung impliziert, dass die Variable definitiv zugewiesen ist, wie wir sehen werden.
Das ist der Kern des Fehlers. Der Fehler ist eine Folge der Überschneidung, wie der C # -Compiler bestimmte Zuweisungsprüfungen für Strukturen durchführt und wie der Compiler Metadaten aus Bibliotheken lädt.
Bedenken Sie:
OK, an diesem Punkt, was wissen wir?
f
ist ein Alias für eine Variable vom TypFoo
, daher wurde der Speicher bereits zugewiesen und befindet sich definitiv zumindest in dem Zustand, in dem er aus dem Speicherzuweiser stammt. Wenn der Aufrufer einen Wert in die Variable eingefügt hat, ist dieser Wert vorhanden.Was brauchen wir? Wir fordern, dass
f
diese definitiv an jedem Punkt zugewiesen wird, an dem die KontrolleM
normal abläuft. Sie würden also etwas erwarten wie:welche setzt
f.x
undf.y
auf ihre Standardwerte. Aber was ist damit?Das sollte auch gut gehen. Aber, und hier ist der Kicker, warum müssen wir die Standardwerte nur zuweisen, um sie einen Moment später wegzublasen? Der definitive Zuweisungsprüfer von C # prüft, ob jedes Feld zugewiesen ist! Das ist legal:
Und warum sollte das nicht legal sein? Es ist ein Werttyp.
f
ist eine Variable und enthält bereits einen gültigen Wert vom Typ.Foo
Setzen wir also einfach die Felder, und wir sind fertig, oder?Recht. Also, was ist der Fehler?
Der Fehler, den Sie entdeckt haben, ist: Aus Kostengründen lädt der C # -Compiler die Metadaten für private Felder von Strukturen, die sich in referenzierten Bibliotheken befinden, nicht . Diese Metadaten können sehr groß sein und den Compiler für sehr wenig Gewinn verlangsamen, um jedes Mal alles in den Speicher zu laden.
Und jetzt sollten Sie in der Lage sein, die Ursache des gefundenen Fehlers abzuleiten. Wenn der Compiler prüft, ob der out-Parameter definitiv zugewiesen ist, vergleicht er die Anzahl der bekannten Felder mit der Anzahl der Felder, die definitiv initialisiert wurden, und in Ihrem Fall kennt er nur die null öffentlichen Felder, da die Metadaten der privaten Felder nicht geladen wurden . Der Compiler kommt zu dem Schluss: "Null Felder erforderlich, Null Felder initialisiert, wir sind gut."
Wie ich bereits sagte, gibt es diesen Fehler seit mehr als einem Jahrzehnt und Leute wie Sie entdecken ihn gelegentlich wieder und melden ihn. Es ist harmlos und es ist unwahrscheinlich, dass es repariert wird, da das Reparieren fast keinen Nutzen bringt, aber hohe Leistungskosten verursacht.
Und natürlich wird der Fehler nicht für private Felder von Strukturen angezeigt, die sich in Ihrem Projekt im Quellcode befinden, da der Compiler offensichtlich bereits Informationen über die privaten Felder zur Hand hat.
quelle
System.TimeSpan
stattdessen a verwenden, treten folgende Fehler auf:error CS0269: Use of unassigned out parameter 'email'
underror CS0177: The out parameter 'email' must be assigned to before control leaves the current method
. Es gibt nur ein nicht statisches Feld vonTimeSpan
nämlich_ticks
. Es istinternal
zu seiner Montage mscorlib. Ist diese Baugruppe etwas Besonderes? GleichesSystem.DateTime
gilt für und sein Feld istprivate
Obwohl es wie ein Fehler aussieht, macht es doch Sinn.
Der 'fehlende Fehler' wird nur bei Verwendung einer Klassenbibliothek angezeigt. Und eine Klassenbibliothek wurde möglicherweise in einer anderen .net-Sprache geschrieben, z. B. VB.Net. Die 'definitive Zuweisungsverfolgung' ist eine Funktion von C #, nicht des Frameworks.
Im Großen und Ganzen denke ich nicht, dass es ein Fehler ist, aber ich weiß nichts über eine maßgebliche Aussage dafür.
quelle
default(T)
) gesetzt wird. Es liegt also kein Verstoß gegen die Speichersicherheit oder ähnliches vor.