Ich mag C # nicht, aber ich sammle gerne alle Muster und den Zucker, den sie von Version zu Version anbieten.
Am dritten Tag habe ich Bill Wagners Rede auf den NDC-Konferenzen gesehen , wo er gezeigt hat, dass Sie Code auf eine neue Art und Weise schreiben müssen (TM).
Er zeigt viele Beispiele für gutes Refactoring, der Code wird lesbarer, aber von diesem Moment an wurde mir klar, dass die Sprache einen vernünftigen Architekten braucht.
Zucker hilft nichts
Nehmen Sie einen schlecht geschriebenen Code, den ein Amateur auf sein Knie geschrieben hat. Diese Methode überprüft den Status der Klasseninstanz und gibt true zurück, wenn ok, und false, wenn nicht.
internal bool GetAvailability()
{
if (_runspace.RunspacePoolAvailability == RunspacePoolAvailability.Available) { return true;}
if (_runspace.RunspacePoolAvailability == RunspacePoolAvailability.Busy) { return true;}
return false;
}
Der Programmierer versuchte es, nicht einmal ein anderer in der Methode. Aber wir sind erfahren, lasst es uns umgestalten, das Wenn entfernen und es in einen Ternark verwandeln:
internal bool GetAvailability()
{
return _runspace.RunspacePoolAvailability == RunspacePoolAvailability.Available ||
_runspace.RunspacePoolAvailability == RunspacePoolAvailability.Busy;
}
Es ist viel besser geworden, 2 statt 5 Codezeilen, aber der Ternark kann in ein Muster umgewandelt werden:
internal bool GetAvailability()
{
return _runspace.RunspacePoolAvailability is RunspacePoolAvailability.Available or RunspacePoolAvailability.Busy;
}
Insgesamt haben wir eine schöne Codezeile hinterlassen. Alles! Das Refactoring ist abgeschlossen! (Nein)
internal void Invoke()
{
if (!GetAvailability()) return;
PowerShell _powershell = PowerShell.Create();
_powershell.RunspacePool = _runspace;
_powershell.Invoke()
}
Das Aufrufen von _powershell'a in einem unzugänglichen _runspace'e löst eine Ausnahme aus.
Alle Implementierungen sind gleich schlecht, da sie ein Problem lösen: Sie schützen den Code hinter sich, sodass keine Ausnahme ausgelöst wird, und das Aussehen ist bereits das zehnte.
Der Code war nicht aktuell, aber seine Bedeutung hat sich nicht geändert.
Weitere Ausnahmen!
Wenn ein Programm auf die Realität trifft, entstehen natürlich Ausnahmesituationen. Datei nicht gefunden, Datei mit falschem Format, falschem Inhalt, ohne Inhalt, Streamreader hat null gelesen oder an eine leere Zeichenfolge weitergegeben. Zwei Codezeilen sind geschrieben und beide sind kaputt, aber ich habe das Gespräch gesehen und meine Sicht bekommen.
„Aber machen Sie sich jetzt Sorgen, wenn Sie Ihre eigene Klasse oder Bibliothek erstellen, müssen Sie nicht über den Verteidigungscode nachdenken, der Compiler führt die Typprüfung für uns durch, und das Überprüfen auf Null war noch nie einfacher!
Wirf einfach alles auf den Benutzer der Bibliothek und schreibe den Code. Ausnahmen zu werfen und ein Programm zu erstellen ist jetzt prestigeträchtig geworden! Ich werfe und du fängst! "
Als ich den Vortrag von Bill Wagner - NDC Conferences 2020 verstand, war
ich von diesem Konzept und der Arbeit von .net im Allgemeinen so inspiriert, dass ich Ihnen die wahre Geschichte der Entwicklung der RunspacePool- und Powershell-Klassen von System erzählen werde .Management.Automation, auf die ich kürzlich gestoßen bin:
Raucher Nr. 1 macht Powershell
Um den Status zu verfolgen, erstellen wir zunächst ein boolesches Feld, das sich beim Aufruf der Dispose-Methode in true ändert.
Es ist im Allgemeinen unsicher, das Feld IsDisposed anzuzeigen, da Sie eine Nullreferenz abfangen können, wenn die CLR Müll sammelt.
class PowershellInNutshell() : IDisposable
{
private static bool IsDisposed = false;
private static RunspacePoolInTheNuttshell;
public static void Invoke()
{
if (IsDisposed) throw new ObjectDisposedException();
Console.WriteLine("I was invoked");
}
public void Dispose()
{
if (IsDisposed) throw new ObjectDisposedException("Invoke"," , , ");
IsDisposed = true;
Console.WriteLine("I was invoked");
GC.SuppressFinalize(this);
}
}
Wenn wir Dispose oder eine andere Methode erneut aufrufen, lösen wir eine Ausnahme aus und lassen den anderen Programmierer auch den Status der Instanz überwachen oder Ausnahmen mit seinem Code abfangen, aber dies sind nicht meine Probleme.
Raucher # 2 macht RunspacePooll
Hier erstellen wir auch das IsDisposed-Feld, diesmal jedoch mit einem öffentlichen Getter, damit die Person, die die Bibliothek verwendet, keinen weiteren Sicherheitscode schreiben muss.
class RunspacePoolInTheNuttshell() : IDisposable
{
public static bool IsDisposed = false;
public void Dispose()
{
if (IsDisposed) return;
IsDisposed = true;
GC.SuppressFinalize(this);
Console.WriteLine("I was invoked");
}
}
Wenn Dispose aufgerufen wurde, kehren Sie zurück und fertig. Wenn Sie erneut auf das Feld zugreifen, erhält es natürlich nullref, da das Objekt bereits aus dem Speicher entfernt wird. Dies ist jedoch mein Problem.
Ein gesunder Mensch nutzt die Bibliothek:
Hier ist eine Ausnahme, hier ist keine Ausnahme, hier wickeln wir den Fisch ein. Beide Klassen kommen im selben Paket und haben ein unterschiedliches Verhalten. Klassen lösen aus verschiedenen Gründen dieselbe Art von Ausnahme aus.
- Falsches Passwort? InvalidRunspacePoolStateException!
- Keine Verbindung? InvalidRunspacePoolStateException!
Es stellt sich heraus, dass Sie an einer Stelle ObjectDisposedException, an einer anderen NullReferenceException in der dritten InvalidRunspacePoolStateException behandeln müssen und alles voller Überraschungen ist.
Ausnahme ist keine Lösung
Vor der Kommunion der heiligen Handlungen habe ich die Akte auf die alte Weise gelesen:
public static void Main()
{
string txt = @"c:\temp\test.txt";
if (File.Exists(txt)) return;
string readText = File.ReadAllText(txt);
Console.WriteLine(readText);
}
Aber nachdem ich mir das Video angesehen hatte, begann ich auf eine neue Art und Weise:
public static void Main()
{
string txt = @"c:\temp\test.txt";
try
{
string readText = File.ReadAllText(txt);
Console.WriteLine(readText);
}
catch (System.IO.FileNotFoundException)
{
Console.WriteLine("File was not found");
}
}
Oder ist es ein neuer Weg?
public static void Main()
{
string txt = @"c:\temp\test.txt";
if (!File.Exists(txt))
{
throw new NullReferenceException();
}
string readText = File.ReadAllText(txt);
Console.WriteLine(readText);
}
Wie genau ist es auf eine neue Art und Weise? Wo endet die Entwicklerverantwortung und wo beginnt die Benutzerverantwortung?
Im Allgemeinen ist die Idee klar, wenn Sie der Autor der Bibliothek sind, dann können Sie die Methode sofort aufrufen und falsche Daten an sie senden, Sie kennen den Themenbereich perfekt und beschreiben alle Fälle von falschem Verhalten, werfen Ausnahmen aus, die machen spüren und handhaben sie selbst.
internal class NewWay
{
public static string _a;
public static string _b;
public static string _c;
public static void NewWay(string a, string b, string c)
{
string _a = a ?? throw new NullReferenceException("a is null");
string _b = b ?? throw new NullReferenceException("b is null");
string _c = c ?? throw new NullReferenceException("c is null");
}
public void Print()
{
if (String.Compare(_a, _b) != 0)
{
throw new DataException("Some Other Ex");
}
Console.WriteLine($"{_a + _b + _c}");//
}
}
try
{
NewWay newway = new(stringThatCanBeNull, stringThatCanBeNull, stringThatCanBeNull);
newway.Print();
}
catch (NullReferenceException ex)
{
Console.WriteLine(" ");
}
catch (DataException ex)
{
Console.WriteLine(" ");
}
Die Genialsten haben bereits verstanden, wohin ich führe. Die Organisation der Fehlerkorrektur basierend auf Try-Catch-Blöcken führt nur zu einer tieferen Code-Verschachtelung.
Mit diesem Muster lehnen wir es auf jeden Fall ab, den Code auszuführen, aber höflicher.
Im Allgemeinen nichts Neues, vor 10 Jahren begannen die Leute zu vermuten, dass C # mit Mustern überladen war und von Jahr zu Jahr nicht abnahmen. Treffen Sie einen anderen, Ausnahmen zu werfen ist jetzt noch einfacher.
Und schließlich - die Betreiber
Bediener sollten nirgendwo etwas gießen.
Ein Beispiel von JS, das Sie wahrscheinlich kennen:
console.log('2'+'2'-'2');
// 20
Die JS-Designer waren der Ansicht, dass ein separater Additionsoperator und ein separater Verkettungsoperator nicht erforderlich sind. Daher ist es unsicher, in JS zu rechnen.
Die Ursache für diesen Fehler in JS ist die implizite Konvertierung des Zeichenfolgentyps in den Typ int mithilfe des Operators. So wird überschüssiger Zucker zum Käfer.
C # leidet auch unter implizitem Casting, wenn auch viel seltener. Nehmen wir zum Beispiel die Benutzereingabe, die nach dem Aktualisieren der Bibliothek wie zuvor einer Zeichenfolge anstelle von int zugeordnet wurde, sowie den Operator (+) und den mathematischen Operator und den Verkettungsoperator.
Das Ändern des Typs von int in string hat den Code nicht beschädigt, aber die Geschäftslogik gebrochen.
Also lasse ich es hier und du versuchst das Ergebnis der Ausführung zu erraten, ohne zu rennen.
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"{'2' + '2' - '2' }");
}
}