F # 9: Geben Sie Option ein

Wenn C # das Konzept Null für Referenztypen und Nullabe für Strukturen hat. Dies kann eine der folgenden 2 Formen annehmen (zur Demonstration verwende ich hier den Typ int, aber der Typ kann eine beliebige Struktur sein).

  • Nullable
  • int?

Haftungsausschluss
   Nullabe  Nullable<T> ,    - ,    


Sie sind beide gleichwertig.

Die Nullable-Klasse bietet verschiedene Hilfseigenschaften und -methoden, die das Arbeiten mit Nulltypen und -strukturen erleichtern. Dies sind die folgenden:

  • Hasvalue
  • Wert
  • GetValueOrDefault ()
  • GetValueOrDefault (T)

Es gibt etwas anderes als F # in Form eines Optionstyps , bei dem es sich um einen F # -freundlicheren Typ handelt, der sich lieber nicht mit Null befasst, sondern sich mit Dingen im Sinne von "Kann Typwert enthalten" oder "Kann nicht haben" befasst Wert. " Es klingt wie Nullable, aber am Ende ist es F # -Typ, so dass Sie erwarten können, dass es normal in F # -Dingen verwendet wird.

Eine weitere erwähnenswerte Sache beim Option F # -Typ ist, dass er für jeden Typ verwendet werden kann, nicht nur für Strukturen. Dies unterscheidet sich von .NET Nullable, das nur mit Strukturen verwendet werden kann.

Der Wert None wird verwendet, wenn kein Wert vorhanden ist. Andernfalls weist der Ausdruck Some (...) einen Wert zu. Die Werte Some und None können beim Abgleichen mit einem Muster verwendet werden. Ein Beispiel dafür finden Sie in diesem Beitrag.

Wie Nullable verfügt der Typ F # Option über mehrere Hilfseigenschaften / -methoden, die in der folgenden Tabelle aufgeführt sind.

Keine
'T Option
Eine statische Eigenschaft, mit der Sie einen Parameterwert mit dem Wert None erstellen können.
Isnone
Bool
Gibt true zurück, wenn der Parameter None ist.
Issome
Bool
Gibt true zurück, wenn der Parameter einen anderen Wert als None hat.
Einige
'T Option
Ein statisches Element, das einen Parameter erstellt, dessen Wert nicht None ist.
Wert
'T.
Gibt einen Basiswert zurück oder löst eine NullReferenceException aus, wenn der Wert None ist.

Optionen erstellen


Nachdem wir nun wissen, was Optionstypen sind, wie erstellen wir sie. Schauen wir uns einige Beispiele an. Beachten Sie, dass ich in diesem Beispiel die Hilfsmethoden IsSome / IsNone verwendet habe. Persönlich glaube ich, dass der Vergleich mit einer Stichprobe der beste Weg ist, da Sie so alle Fälle vergleichen können, einschließlich der Option Nein.

Tatsächlich werde ich Ihnen zeigen, wie einfach es ist, bei der Arbeit mit Optionstypen etwas falsch zu machen, wenn Sie sich für die Verwendung von Hilfsmethoden entscheiden. Schauen wir uns jedoch zunächst ein Beispiel für den richtigen Fall an.

 let someInt = Some(43) let someString = Some("cat") let printTheOption (opt :Option<'a>) = printfn "Actual Option=%A" opt printfn "IsNone=%b, IsSome=%b Value=%A\r\n\r\n" opt.IsNone opt.IsSome opt.Value printfn "let someInt = Some(43)" printfn "let someString = Some(\"cat\")" printfn "printTheOption (someInt)" printTheOption someInt printfn "printTheOption (someString)" printTheOption someString 

Bild

Was aber, wenn wir es mit diesem Code erneut versuchen, wobei wir None für den Optionswert haben, den wir an die Funktion printTheOption übergeben:

 let demoNone = None let printTheOption (opt :Option<'a>) = printfn "Actual Option=%A" opt printfn "IsNone=%b, IsSome=%b Value=%A\r\n\r\n" opt.IsNone opt.IsSome opt.Value printfn "let demoNone = None" printfn "printTheOption demoNone" printTheOption demoNone 

Bild

Wie Sie sehen, haben wir hier ein Problem. Das Problem ist, dass wir versucht haben, den Optionswert mithilfe der Hilfseigenschaft Option.Value abzurufen. In diesem Fall ist es None, sodass wir eine NullReferenceException erhalten haben. In der obigen Tabelle wird gezeigt, dass bei Verwendung von Hilfseigenschaften und -methoden eine Ausnahme auftreten kann. Nun, Sie könnten die IsNone-Methode verwenden und den Wert immer damit überprüfen, wenn Sie nur eine schöne saubere Musterübereinstimmung verwenden könnten.

Wenn Sie dies nicht akzeptieren können, fragen Sie sich, wie oft Sie den Nullwert bei Verwendung von C # überprüfen mussten. Dies führte sogar dazu, dass Benutzer funktionale Konstrukte wie "Vielleicht Null Monad" in regulären .NET-Code einbauten.

Nachdem wir die Gefahr der Verwendung von Hilfsmethoden / -eigenschaften erkannt haben, wenden wir uns nun der Frage zu, wie wir diese Ausnahmen vermeiden können:

 et printTheOption (opt :Option<'a>) = match opt with | Some a -> printfn "opt is Some, and has value %A" a | None -> printfn "opt is None" let demoNone = None let someInt = Some 1 let someString = Some "crazy dude in the monkey house" printTheOption demoNone printTheOption someInt printTheOption someString 

Meine persönliche Meinung ist, dass es besser lesbar ist als Code, der überall mit IsSome / IsNone übersät ist. Dies ist natürlich jedermanns Sache, aber die Tatsache, dass wir hier alle Grundlagen dieser einfachen Funktion behandelt haben, kann nicht ignoriert werden.

Bild

Option gegen Null


Wir haben also über Option in F # im Vergleich zu Nullabe gesprochen und wissen, dass der Optionstyp mit jedem Typ verwendet werden kann, während Nullable nur mit Strukturen verwendet werden kann. Aber was ist mit Optionstypen im Vergleich zu regulären Referenztypen in .NET. Ein großer Vorteil von Option ist, dass Sie bei Verwendung des Referenztyps in .NET mit einer Zeigerreferenz zu tun haben, die als solche auf null gesetzt werden kann. Der Typ des Objekts bleibt jedoch derselbe wie deklariert. Dieser kann einen gültigen Verweis auf das Objekt im Heap (Heap) enthalten oder null sein.

Es wäre normal, so zu schreiben:

 string s1 = "cats"; int s1Length = s1.Length; string s2 = null; int s2Length = s2.Length; 

Dies wird erfolgreich kompiliert. Wenn wir dies ausführen, erhalten wir jedoch eine NullReferenceException, für die wir gezwungen sind, auszusteigen, um den gesamten Code vor dem möglichen Vorhandensein von Null zu schützen. Dies wird ziemlich schnell langweilig, selbst wenn Sie eine nette kleine Schutzklasse haben, die den Wert überprüft und damit umgeht / eine aussagekräftigere Ausnahme auslöst.

In diesem Screenshot wird LinqPad verwendet. Wenn Sie LinqPad noch nicht gesehen haben, ist dies möglicherweise etwas ungewöhnlich. Glauben Sie mir, Sie erhalten in einer anderen IDE immer noch das gleiche Ergebnis.

Bild

Nun wollen wir sehen, wie äquivalenter Code in F # aussehen wird

 let s1 = "Cats" let s1Length = s1.Length; let s2 = None let s2Length = s2.Length; //excplicily string typed None let s3 = Option.None let s3Length = s3.Length; 

Bei diesem Code wird sofort ein Kompilierungsfehler angezeigt, da s3 als ein anderer Typ betrachtet wird, der nicht über die Eigenschaft "Länge" verfügt.

Optionsvergleich


Optionstypen werden als gleich angesehen, sie sind vom gleichen Typ und die darin enthaltenen Typen sind gleich, was den Gleichheitsregeln des gehaltenen Typs entspricht.

So etwas kann also zu einem sofortigen Fehler bei der Kompilierung in F # führen

 let o1 = Some 1 let o2 = Some "Cats" printfn "o1=o2 : %b" (o1 = o2) 

Bild

Dies funktioniert wie erwartet, da die Typen gleich sind

 let o1 = Some "Cats" let o2 = Some "Cats" let o3 = Some "cats" printfn "o1=o2 : %b" (o1 = o2) printfn "o2=o3 : %b" (o2 = o3) 

Bild

Source: https://habr.com/ru/post/de470918/


All Articles