Sök…
Anmärkningar
Enheter på Runtime
Måttenheter används endast för statisk kontroll av kompilatorn och är inte tillgängliga under körning. De kan inte användas i reflektion eller i metoder som ToString
.
Till exempel ger C # en double
utan enheter för ett fält av typen float<m>
definierat av och exponerat från ett F # -bibliotek.
Säkerställa konsekventa enheter i beräkningar
Mätenheter är anteckningar av ytterligare typ som kan läggas till flottörer eller heltal. De kan användas för att vid sammanställningstiden verifiera att beräkningar använder enheter konsekvent.
Så här definierar du anteckningar:
[<Measure>] type m // meters
[<Measure>] type s // seconds
[<Measure>] type accel = m/s^2 // acceleration defined as meters per second squared
När den har definierats kan anteckningar användas för att verifiera att ett uttryck resulterar i den förväntade typen.
// Compile-time checking that this function will return meters, since (m/s^2) * (s^2) -> m
// Therefore we know units were used properly in the calculation.
let freeFallDistance (time:float<s>) : float<m> =
0.5 * 9.8<accel> * (time*time)
// It is also made explicit at the call site, so we know that the parameter passed should be in seconds
let dist:float<m> = freeFallDistance 3.0<s>
printfn "%f" dist
Omvandlingar mellan enheter
[<Measure>] type m // meters
[<Measure>] type cm // centimeters
// Conversion factor
let cmInM = 100<cm/m>
let distanceInM = 1<m>
let distanceInCM = distanceInM * cmInM // 100<cm>
// Conversion function
let cmToM (x : int<cm>) = x / 100<cm/m>
let mToCm (x : int<m>) = x * 100<cm/m>
cmToM 100<cm> // 1<m>
mToCm 1<m> // 100<cm>
Observera att F # -kompileraren inte vet att 1<m>
lika med 100<cm>
. Så långt det bryr sig är enheterna separata typer. Du kan skriva liknande funktioner för att konvertera från meter till kilogram, och kompilatorn bryr sig inte.
[<Measure>] type kg
// Valid code, invalid physics
let kgToM x = x / 100<kg/m>
Det är inte möjligt att definiera måttenheter som multiplar av andra enheter som
// Invalid code
[<Measure>] type m = 100<cm>
Att definiera enheter "per någonting", till exempel Hertz, mätfrekvens, helt enkelt "per sekund", är dock ganska enkelt.
// Valid code
[<Measure>] type s
[<Measure>] type Hz = /s
1 / 1<s> = 1 <Hz> // Evaluates to true
[<Measure>] type N = kg m/s // Newtons, measuring force. Note lack of multiplication sign.
// Usage
let mass = 1<kg>
let distance = 1<m>
let time = 1<s>
let force = mass * distance / time // Evaluates to 1<kg m/s>
force = 1<N> // Evaluates to true
Använda LanguagePrimitives för att bevara eller ställa in enheter
När en funktion inte bevarar enheter automatiskt på grund av operationer på lägre nivå, kan modulen LanguagePrimitives
användas för att ställa in enheter på primitiven som stöder dem:
/// This cast preserves units, while changing the underlying type
let inline castDoubleToSingle (x : float<'u>) : float32<'u> =
LanguagePrimitives.Float32WithMeasure (float32 x)
För att tilldela måttenheter till ett dubbelprecisions flytande punktvärde, multiplicerar du helt enkelt med en med korrekta enheter:
[<Measure>]
type USD
let toMoneyImprecise (amount : float) =
amount * 1.<USD>
För att tilldela måttenheter till ett enhetsfritt värde som inte är System.Dubbel, till exempel, kommer från ett bibliotek skrivet på ett annat språk, använd en konvertering:
open LanguagePrimitives
let toMoney amount =
amount |> DecimalWithMeasure<'u>
Här är funktionstyperna rapporterade av F # interactive:
val toMoney : amount:decimal -> decimal<'u>
val toMoneyImprecise : amount:float -> float<USD>
Parameterns måttenhet
Attributet [<Measure>]
kan användas på typparametrar för att deklara typer som är generiska med avseende på måttenheter:
type CylinderSize<[<Measure>] 'u> =
{ Radius : float<'u>
Height : float<'u> }
Testanvändning:
open Microsoft.FSharp.Data.UnitSystems.SI.UnitSymbols
/// This has type CylinderSize<m>.
let testCylinder =
{ Radius = 14.<m>
Height = 1.<m> }
Använd standardiserade enhetstyper för att bibehålla kompatibiliteten
Till exempel har typer för SI-enheter standardiserats i F # -biblioteket, i Microsoft.FSharp.Data.UnitSystems.SI
. Öppna lämpligt UnitNames
, UnitNames
eller UnitSymbols
, för att använda dem. Eller, om bara några SI-enheter krävs, kan de importeras med alias av typ:
/// Seconds, the SI unit of time. Type abbreviation for the Microsoft standardized type.
type [<Measure>] s = Microsoft.FSharp.Data.UnitSystems.SI.UnitSymbols.s
Vissa användare tenderar att göra följande, vilket inte bör göras när en definition redan finns tillgänglig:
/// Seconds, the SI unit of time
type [<Measure>] s // DO NOT DO THIS! THIS IS AN EXAMPLE TO EXPLAIN A PROBLEM.
Skillnaden blir uppenbar när man gränsar till annan kod som hänvisar till standard SI-typer. Kod som hänvisar till standardenheterna är kompatibel, medan kod som definierar sin egen typ är oförenlig med någon kod som inte använder den specifika definitionen.
Använd därför alltid standardtyperna för SI-enheter. Det spelar ingen roll om du hänvisar till UnitNames
eller UnitSymbols
, eftersom motsvarande namn inom dessa två hänvisar till samma typ:
open Microsoft.FSharp.Data.UnitSystems.SI
/// This is valid, since both versions refer to the same authoritative type.
let validSubtraction = 1.<UnitSymbols.s> - 0.5<UnitNames.second>