Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Często kilka algorytmów lub implementacji może poprawnie rozwiązać to samo zadanie. Wybrany kod wyraża projekt. Deweloperzy i agenci kodowania, którzy odczytują kod, używają jego struktury, aby wywnioskować Twój zamysł: czego potrzebuje każda operacja, gdzie dana operacja należy i kiedy jest uruchamiana.
Wskazówka
Ten artykuł jest częścią sekcji Podstawy dla deweloperów, którzy znają podstawowe wyrażenia, instrukcje i metody języka C#. Jeśli dopiero zaczynasz programować, najpierw zacznij od samouczków Wprowadzenie .
Ta sekcja porównuje prawidłowe podejścia do algorytmów i wyjaśnia subtelne różnice w ich zachowaniu oraz sposób ich interpretowania przez czytelników. Połączysz znane elementy języka, takie jak typy, wyrażenia, instrukcje i metody, i wybierzesz formy, które oddają zamierzony projekt.
Porównaj zachowanie i intencję projektową
Przed wybraniem implementacji zastanów się, jak inny kod będzie jej używać. Ten kod jest kodem wywołującym: dostarcza dane wejściowe, wywołuje operację i używa wyników. Czy potrzebuje od razu pełnego wyniku, czy może przetwarzać wartości pojedynczo? Czy pomocnik jest częścią wyłącznie tej operacji, czy też inne operacje go używają? Te pytania pomagają porównać zarówno potrzebne zachowanie, jak i projekt komunikowany przez kod.
Krótka operacja wykonywana w tekście pokazuje czytelnikom kolejne kroki bezpośrednio w miejscu użycia. Na przykład Where(task => task.Priority > 3) sprawia, że reguła filtrowania jest natychmiast widoczna. Nazwany pomocnik, jak w Where(IsUrgent), podkreśla cel reguły i umożliwia czytelnikom oddzielną inspekcję szczegółów. Wybierz na podstawie tego, czy kroki lub ich cel najlepiej wyjaśniają otaczający kod.
Miejsce zadeklarowania pomocnika wskazuje jego zamierzony zakres. Funkcja lokalna wewnątrz ShowDashboard wskazuje, że funkcja pomocnicza należy do tej operacji i uniemożliwia innym członkom jej wywoływanie. Metoda prywatna w typie zawierającym sprawia, że metoda pomocnicza jest dostępna dla innych składowych typu, co sugeruje, że oczekujesz ponownego użycia w innym miejscu w typie. Obie mogą organizować to samo obliczenie, ale budzą u czytelników różne oczekiwania dotyczące jego roli.
Sposób dostarczania danych wejściowych pomocnika komunikuje jego zależności. Przekazanie limitu jako parametru, takiego jak IsUrgent(task, minimumPriority), sprawia, że to wejście jest widoczne w każdym wywołaniu. Funkcja lokalna lub lambda może zamiast tego przechwycić zmiennąminimumPriority, używając zmiennej z otaczającego zakresu bez odbierania jej jako argumentu. Przechwytywanie łączy pomocnika z kontekstem operacji nadrzędnej; jawne parametry ułatwiają identyfikację dostarczonych danych. Użyj static lokalnej funkcji lub wyrażenia lambda, jeśli chcesz zapewnić, że nie przechwytuje otaczającego stanu.
Sposób zwracania wyników wskazuje, kiedy wykonywana jest praca. Metoda, która tworzy i zwraca kolekcję, kończy pracę, zanim obiekt wywołujący otrzyma kolekcję. Iterator tworzy elementy, gdy obiekt wywołujący żąda ich, więc obiekt wywołujący może zatrzymać się przed utworzeniem wszystkich elementów. Oba mogą tworzyć te same wartości, ale osoby wywołujące muszą zrozumieć, czy otrzymują ukończone wyniki, czy sekwencję, której obliczenie nastąpi dopiero później.
Dla każdego wyboru najpierw zidentyfikuj zachowanie wymagane przez kod wywołujący. Następnie zastanów się, co inny deweloper lub agent kodowania wywnioskuje z nazwy, zakresu, danych wejściowych i wyniku. Wybierz formę, która sprawia, że te oczekiwania są zgodne z projektem, i wyjaśnij ważne zachowania, których nie ujawnia sam kształt kodu. Celem jest dokonanie celowego wyboru między prawidłowymi alternatywami, a nie zastosowaniem jednej formy wszędzie.
Przeczytaj o wyborach projektowych na przykładzie
Poniższy przykład ilustruje, jak kilka wyborów przekazuje różne elementy projektu. Wyświetla zadania, które są pilne i gotowe do rozpoczęcia. Element ProjectTask ma tytuł, priorytet i stan zablokowania zadania.
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);
Każdy wybór zapewnia przejrzystość innej części pracy:
- Przekazana do
Wherefunkcja lambda określa krótką regułę priorytetu dla operacji, która jej używa. - Funkcja lokalna
FormatTaskoznacza etap formatowania, który należy tylko doShowDashboard. - Iterator
ReadyTasksumożliwia kodowi wywołującemu korzystanie z gotowych zadań bez tworzenia kolekcji pośredniej. - Pętla
foreachsprawia, że ostatnia czynność — zapisywanie każdego wyniku — jest jawnie określona.
Dla porównania ReadyTasks mógłby zastąpić Where(static task => !task.IsBlocked), ponieważ ten test gotowości jest prostym filtrem. Oba formularze odraczają filtrowanie do momentu iteracji. Lambda umieszcza regułę w miejscu wywołania; ReadyTasks nadaje regule nazwę. Wybierz między nimi, w zależności od tego, które informacje ułatwiają czytelnikom zrozumienie wywołującego.
Poniższe artykuły wykorzystują takie porównania, aby wyjaśnić, kiedy każde podejście jest odpowiednie i co czytelnicy mogą wywnioskować z Twojego wyboru.