Týden 7
Přidání cen ve špičce
U konečné funkce chce mýtný úřad přidat časově citlivé ceny ve špičce. V ranních a večerních špičkách se placené poplatky zdvojnásobí. Toto pravidlo má vliv pouze na provoz v jednom směru: příchozí do města ráno a odchozí ve večerní špičce. V jiných časech během pracovního dne se placené poplatky zvyšují o 50 %. Pozdě v noci a brzy ráno je placená daň snížena o 25 %. O víkendu je to normální sazba bez ohledu na čas. K vyjádření tohoto problému můžete použít řadu if
příkazů a else
pomocí následujícího kódu:
public decimal PeakTimePremiumIfElse(DateTime timeOfToll, bool inbound)
{
if ((timeOfToll.DayOfWeek == DayOfWeek.Saturday) ||
(timeOfToll.DayOfWeek == DayOfWeek.Sunday))
{
return 1.0m;
}
else
{
int hour = timeOfToll.Hour;
if (hour < 6)
{
return 0.75m;
}
else if (hour < 10)
{
if (inbound)
{
return 2.0m;
}
else
{
return 1.0m;
}
}
else if (hour < 16)
{
return 1.5m;
}
else if (hour < 20)
{
if (inbound)
{
return 1.0m;
}
else
{
return 2.0m;
}
}
else // Overnight
{
return 0.75m;
}
}
}
Předchozí kód funguje správně, ale není čitelný. Abyste mohli o kódu uvažovat, musíte projít všechny vstupní případy a vnořené if
příkazy. Místo toho pro tuto funkci použijete porovnávání vzorů, ale integrujete ji s jinými technikami. Mohli byste vytvořit jeden výraz shody se vzorem, který by zohlednil všechny kombinace směru, dne v týdnu a času. Výsledkem by byl složitý výraz. Bylo by těžké ho číst a těžko pochopit. To ztěžuje zajištění správnosti. Místo toho zkombinujte tyto metody a vytvořte řazenou kolekci hodnot, která výstižně popisuje všechny tyto stavy. Pak použijte porovnávání vzorů k výpočtu multiplikátoru pro placenou linku. Řazená kolekce členů obsahuje tři samostatné podmínky:
- Den je buď pracovní den, nebo víkend.
- Časové pásmo, kdy se vybírá placená platba.
- Směr je do města nebo mimo město
Následující tabulka uvádí kombinace vstupních hodnot a násobitele cen ve špičce:
timeOfToll.DayOfWeek switch
{
DayOfWeek.Monday => true,
DayOfWeek.Tuesday => true,
DayOfWeek.Wednesday => true,
DayOfWeek.Thursday => true,
DayOfWeek.Friday => true,
DayOfWeek.Saturday => false,
DayOfWeek.Sunday => false
};
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">private static bool IsWeekDay(DateTime timeOfToll) =>
timeOfToll.DayOfWeek switch
{
DayOfWeek.Monday => true,
DayOfWeek.Tuesday => true,
DayOfWeek.Wednesday => true,
DayOfWeek.Thursday => true,
DayOfWeek.Friday => true,
DayOfWeek.Saturday => false,
DayOfWeek.Sunday => false
};
Tato metoda je správná, ale je opakovaná. Můžete ho zjednodušit, jak je znázorněno v následujícím kódu:
timeOfToll.DayOfWeek switch
{
DayOfWeek.Saturday => false,
DayOfWeek.Sunday => false,
_ => true
};
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">private static bool IsWeekDay(DateTime timeOfToll) =>
timeOfToll.DayOfWeek switch
{
DayOfWeek.Saturday => false,
DayOfWeek.Sunday => false,
_ => true
};
Dále přidejte podobnou funkci, která čas kategorizuje do bloků:
timeOfToll.Hour switch
{
< 6 or > 19 => TimeBand.Overnight,
< 10 => TimeBand.MorningRush,
< 16 => TimeBand.Daytime,
_ => TimeBand.EveningRush,
};
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">private enum TimeBand
{
MorningRush,
Daytime,
EveningRush,
Overnight
}private static TimeBand GetTimeBand(DateTime timeOfToll) =>
timeOfToll.Hour switch
{
< 6 or > 19 => TimeBand.Overnight,
< 10 => TimeBand.MorningRush,
< 16 => TimeBand.Daytime,
_ => TimeBand.EveningRush,
};
Přidáte privátní enum
pro převod každého časového rozsahu na diskrétní hodnotu. GetTimeBand
Pak metoda používá relační vzory a konjunktivní or
vzory, které jsou přidané v jazyce C# 9.0. Relační vzor umožňuje otestovat číselnou hodnotu pomocí <
, >
, <=
nebo >=
. Vzor or
testuje, jestli výraz odpovídá jednomu nebo více vzorům. Můžete také použít and
vzor, který zajistí, aby výraz odpovídal dvěma odlišným not
vzorům, a vzor, který otestuje, že výraz neodpovídá vzoru.
Po vytvoření těchto metod můžete k výpočtu cenové úrovně Premium použít jiný switch
výraz se vzorem řazené kolekce členů . Mohli byste vytvořit výraz se switch
všemi 16 rameny:
(IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
{
(true, TimeBand.MorningRush, true) => 2.00m,
(true, TimeBand.MorningRush, false) => 1.00m,
(true, TimeBand.Daytime, true) => 1.50m,
(true, TimeBand.Daytime, false) => 1.50m,
(true, TimeBand.EveningRush, true) => 1.00m,
(true, TimeBand.EveningRush, false) => 2.00m,
(true, TimeBand.Overnight, true) => 0.75m,
(true, TimeBand.Overnight, false) => 0.75m,
(false, TimeBand.MorningRush, true) => 1.00m,
(false, TimeBand.MorningRush, false) => 1.00m,
(false, TimeBand.Daytime, true) => 1.00m,
(false, TimeBand.Daytime, false) => 1.00m,
(false, TimeBand.EveningRush, true) => 1.00m,
(false, TimeBand.EveningRush, false) => 1.00m,
(false, TimeBand.Overnight, true) => 1.00m,
(false, TimeBand.Overnight, false) => 1.00m,
};
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public decimal PeakTimePremiumFull(DateTime timeOfToll, bool inbound) =>
(IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
{
(true, TimeBand.MorningRush, true) => 2.00m,
(true, TimeBand.MorningRush, false) => 1.00m,
(true, TimeBand.Daytime, true) => 1.50m,
(true, TimeBand.Daytime, false) => 1.50m,
(true, TimeBand.EveningRush, true) => 1.00m,
(true, TimeBand.EveningRush, false) => 2.00m,
(true, TimeBand.Overnight, true) => 0.75m,
(true, TimeBand.Overnight, false) => 0.75m,
(false, TimeBand.MorningRush, true) => 1.00m,
(false, TimeBand.MorningRush, false) => 1.00m,
(false, TimeBand.Daytime, true) => 1.00m,
(false, TimeBand.Daytime, false) => 1.00m,
(false, TimeBand.EveningRush, true) => 1.00m,
(false, TimeBand.EveningRush, false) => 1.00m,
(false, TimeBand.Overnight, true) => 1.00m,
(false, TimeBand.Overnight, false) => 1.00m,
};
Výše uvedený kód funguje, ale dá se ho zjednodušit. Všech osm kombinací pro víkend má stejné mýto. Všech osm můžete nahradit následujícím řádkem:
1.0m,
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">(false, _, _) => 1.0m,
Příchozí i odchozí provoz mají stejný násobitel během dne v týdnu i v nočních hodinách. Tato čtyři spínací ramena mohou být nahrazena následujícími dvěma řádky:
0.75m,
(true, TimeBand.Daytime, _) => 1.5m,
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">(true, TimeBand.Overnight, _) => 0.75m,
(true, TimeBand.Daytime, _) => 1.5m,
Kód by měl vypadat jako následující kód po těchto dvou změnách:
(IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
{
(true, TimeBand.MorningRush, true) => 2.00m,
(true, TimeBand.MorningRush, false) => 1.00m,
(true, TimeBand.Daytime, _) => 1.50m,
(true, TimeBand.EveningRush, true) => 1.00m,
(true, TimeBand.EveningRush, false) => 2.00m,
(true, TimeBand.Overnight, _) => 0.75m,
(false, _, _) => 1.00m,
};
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public decimal PeakTimePremium(DateTime timeOfToll, bool inbound) =>
(IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
{
(true, TimeBand.MorningRush, true) => 2.00m,
(true, TimeBand.MorningRush, false) => 1.00m,
(true, TimeBand.Daytime, _) => 1.50m,
(true, TimeBand.EveningRush, true) => 1.00m,
(true, TimeBand.EveningRush, false) => 2.00m,
(true, TimeBand.Overnight, _) => 0.75m,
(false, _, _) => 1.00m,
};
Nakonec můžete odebrat dvě špičky, které platí běžnou cenu. Jakmile tyto paže vyjmete, můžete nahradit false
zahozenou (_
) v posledním rameni spínače. Budete mít následující dokončenou metodu:
(IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
{
(true, TimeBand.Overnight, _) => 0.75m,
(true, TimeBand.Daytime, _) => 1.5m,
(true, TimeBand.MorningRush, true) => 2.0m,
(true, TimeBand.EveningRush, false) => 2.0m,
_ => 1.0m,
};
" style="box-sizing: inherit; outline-color: inherit; font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 1em; direction: ltr; border: 0px; padding: 0px; line-height: 1.3571; display: block; position: relative;">public decimal PeakTimePremium(DateTime timeOfToll, bool inbound) =>
(IsWeekDay(timeOfToll), GetTimeBand(timeOfToll), inbound) switch
{
(true, TimeBand.Overnight, _) => 0.75m,
(true, TimeBand.Daytime, _) => 1.5m,
(true, TimeBand.MorningRush, true) => 2.0m,
(true, TimeBand.EveningRush, false) => 2.0m,
_ => 1.0m,
};
Tento příklad zvýrazňuje jednu z výhod porovnávání vzorů: větve vzorů se vyhodnocují v pořadí. Pokud je přeskupíte tak, aby starší větev zpracovávala některý z pozdějších případů, kompilátor vás upozorní na nedostupný kód. Tato jazyková pravidla usnadnila předchozí zjednodušení s jistotou, že se kód nezměnil.
Porovnávání vzorů dělá některé typy kódu čitelnější a nabízí alternativu k objektům orientovaným technikám, když nemůžete přidat kód do tříd. Cloud způsobuje, že data a funkce se od sebe oddělují. Tvar dat a operace s ním nejsou nutně popsány společně. V tomto kurzu jste využili existující data úplně jinak než jejich původní funkce. Porovnávání vzorů vám umožnilo psát funkce, které tyto typy přetěžují, i když jste je nemohli rozšířit.
Hotový kód si můžete stáhnout z úložiště githubu dotnet/samples . Prozkoumejte vlastní vzory a přidejte tuto techniku do běžných aktivit kódování. Když se tyto techniky naučíte, získáte další způsob, jak přistupovat k problémům a vytvářet nové funkce.
undefinedExistuje 16 různých kombinací těchto tří proměnných. Kombinací některých podmínek zjednodušíte konečný výraz switch.