Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Ofta kan flera algoritmer eller implementeringar lösa samma uppgift korrekt. Den kod du väljer förmedlar ett designval. Utvecklare och kodningsagenter som läser koden använder kodens struktur för att härleda din avsikt: vad varje åtgärd behöver, var den hör hemma och när den körs.
Tip
Den här artikeln är en del av avsnittet Grunderna för utvecklare som känner till grundläggande C#-uttryck, satser och metoder. Om du inte har använt programmering tidigare börjar du med självstudierna Komma igång först.
Det här avsnittet jämför giltiga tillvägagångssätt för algoritmer och förklarar subtila skillnader i deras beteende och hur läsarna tolkar dem. Du kombinerar välbekanta språkelement, till exempel typer, uttryck, satser och metoder, och väljer former som förmedlar den design du vill använda.
Jämför beteende och designavsikt
Innan du väljer en implementering bör du överväga hur annan kod ska använda den. Koden är anroparen: den tillhandahåller indata, anropar åtgärden och använder resultatet. Behöver det ett färdigt resultat omedelbart eller kan det bearbeta värden en i taget? Är en hjälpfunktion bara för den här åtgärden eller kommer andra åtgärder också att använda den? De här frågorna hjälper dig att jämföra både det beteende du behöver och vilken design koden kommunicerar.
En kort inbäddad åtgärd visar läsarna stegen direkt där den används. Till exempel gör Where(task => task.Priority > 3) filtreringsregeln omedelbart synlig. En namngiven hjälpfunktion, som i Where(IsUrgent), betonar regelns syfte och låter läsarna granska dess detaljer separat. Välj baserat på om stegen eller deras syfte bäst förklarar den omgivande koden.
Var du deklarerar en helper visar vilket omfång den är avsedd att ha. En lokal funktion inuti ShowDashboard anger att hjälpfunktionen tillhör den åtgärden och hindrar andra medlemmar från att anropa den. En privat metod för den omslutande typen gör hjälpfunktionen tillgänglig för andra medlemmar, vilket tyder på att du förväntar dig återanvändning någon annanstans i typen. Båda kan strukturera samma beräkning, men de ger läsaren olika förväntningar på vilken roll den har.
Hur du anger en hjälpares indata kommunicerar dess beroenden. Att skicka en gräns som en parameter, till exempel IsUrgent(task, minimumPriority), gör den indata synlig vid varje anrop. En lokal funktion eller lambda kan i stället fångaminimumPriority med hjälp av variabeln från dess omgivande omfång utan att ta emot den som ett argument. Capture ansluter hjälparen till den innehållande åtgärdens kontext. Explicita parametrar gör de angivna data lättare att identifiera. Använd en static lokal funktion eller lambda när du vill framtvinga att den inte fångar omgivande tillstånd.
Sättet du returnerar resultat på visar när arbetet utförs. En metod som skapar och returnerar en samling slutför det arbetet innan anroparen tar emot samlingen. En iterator skapar element när anroparen begär dem, så anroparen kan stoppa innan alla element skapas. Båda kan generera samma värden, men anropare måste förstå om de får slutförda resultat eller en sekvens vars beräkning fortfarande ligger framför.
För varje val ska du först identifiera det beteende som anropande kod behöver. Tänk sedan på vad en annan utvecklare eller kodagent kommer att utläsa av namn, omfång, indata och resultat. Välj en form som gör att dessa förväntningar matchar din design och förklara viktigt beteende som kodens form inte avslöjar. Målet är att göra ett medvetet val mellan giltiga alternativ, inte att tillämpa en och samma form överallt.
Läs designval i ett exempel
I följande exempel visas hur flera alternativ kommunicerar olika delar av en design. Den visar uppgifter som är brådskande och redo att startas. A ProjectTask innehåller en aktivitets titel, prioritet och blockerade status.
var tasks = new[]
{
new ProjectTask("Confirm venue", Priority: 3, IsBlocked: false),
new ProjectTask("Print badges", Priority: 2, IsBlocked: true),
new ProjectTask("Test microphones", Priority: 2, IsBlocked: false),
};
ShowDashboard("Community meetup", tasks, minimumPriority: 2);
static void ShowDashboard(
string projectName,
IEnumerable<ProjectTask> tasks,
int minimumPriority)
{
var urgentTasks = tasks.Where(
task => task.Priority >= minimumPriority);
foreach (ProjectTask task in ReadyTasks(urgentTasks))
{
Console.WriteLine(FormatTask(task));
}
string FormatTask(ProjectTask task) =>
$"{projectName}: {task.Title}";
}
static IEnumerable<ProjectTask> ReadyTasks(
IEnumerable<ProjectTask> tasks)
{
foreach (ProjectTask task in tasks)
{
if (!task.IsBlocked)
{
yield return task;
}
}
}
record ProjectTask(string Title, int Priority, bool IsBlocked);
Varje val håller en annan del av arbetet tydlig:
- Lambda som skickas till
Whereplacerar en kort prioritetsregel bredvid den åtgärd som använder den. - Den lokala funktionen
FormatTasknamnger ett formateringssteg som bara tillhörShowDashboard. - Iteratorn
ReadyTaskslåter anropare bearbeta färdiga uppgifter utan att skapa en mellanliggande samling. - Slingan
foreachgör den sista åtgärden – att skriva varje resultat – explicit.
Som jämförelse Where(static task => !task.IsBlocked) kan ersätta ReadyTasks eftersom det här beredskapstestet är ett enkelt filter. Båda formulären skjuter upp filtret till enumereringen. Lambda placerar regeln på anropsplatsen; ReadyTasks ger regeln ett namn. Välj mellan dem baserat på vilken information som hjälper läsarna att förstå den som ringer.
Artiklarna som följer använder jämförelser som dessa för att förklara när varje metod passar och vad läsare kan dra för slutsatser av ditt val.