자습서: 학습할 때 최상위 문을 사용하여 코드를 빌드하는 아이디어 탐색
이 자습서에서는 다음 작업을 수행하는 방법을 알아봅니다.
- 최상위 문 사용을 제어하는 규칙을 알아봅니다.
- 최상위 문을 사용하여 알고리즘을 탐색합니다.
- 탐색을 재사용 가능한 구성 요소로 리팩터링합니다.
필수 조건
C# 10 컴파일러를 포함하여 .NET 6을 실행하도록 컴퓨터를 설정해야 합니다. C# 10 컴파일러는 Visual Studio 2022 또는 .NET 6 SDK부터 사용할 수 있습니다.
이 자습서에서는 여러분이 Visual Studio 또는 .NET CLI를 비롯한 C# 및 .NET에 익숙하다고 가정합니다.
살펴보기 시작하기
최상위 문을 사용하면 프로그램의 진입점을 클래스의 정적 메서드에 배치하여 필요한 추가 공식 절차를 방지할 수 있습니다. 새 콘솔 애플리케이션의 일반적인 시작점은 다음 코드와 같습니다.
using System;
namespace Application
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
위 코드는 dotnet new console
명령을 실행하고 새 콘솔 애플리케이션을 만든 결과입니다. 11줄의 코드에 단 한 줄의 실행 코드가 포함됩니다. 새 최상위 문 기능을 사용하여 해당 프로그램을 단순화할 수 있습니다. 이렇게 하면 해당 프로그램에서 두 개 줄을 제외한 모든 줄을 제거할 수 있습니다.
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
Important
.NET 6 용 C# 템플릿은 ‘최상위 문’을 사용합니다. .NET 6으로 이미 업그레이드한 경우 애플리케이션이 이 문서의 코드와 일치하지 않을 수 있습니다. 자세한 내용은 최상위 문을 생성하는 새 C# 템플릿을 참조하세요.
.NET 6 SDK는 다음 SDK를 사용하는 프로젝트에 대한 암시적global using
지시문 집합도 추가합니다.
- Microsoft.NET.Sdk
- Microsoft.NET.Sdk.Web
- Microsoft.NET.Sdk.Worker
이러한 암시적 global using
지시문에는 해당 프로젝트 형식의 가장 일반적인 네임스페이스가 포함됩니다.
자세한 내용은 암시적 using 지시문에 대한 문서를 참조하세요.
이 기능은 새로운 아이디어 탐색을 시작하는 데 필요한 작업을 간소화합니다. 스크립팅 시나리오에서 또는 탐색하는 데 최상위 문을 사용할 수 있습니다. 적용되는 기본 사항을 확인한 후 코드의 리팩터링을 시작하고 빌드한 재사용 가능 구성 요소의 메서드, 클래스 또는 기타 어셈블리를 만들 수 있습니다. 최상위 문은 빠른 실험 및 초보자 자습서를 가능하게 합니다. 또한 실험에서 전체 프로그램까지 원활한 경로를 제공합니다.
최상위 문은 파일에 표시된 순서대로 실행됩니다. 최상위 문은 애플리케이션의 한 소스 파일에만 사용할 수 있습니다. 둘 이상의 파일에서 사용하는 경우 컴파일러에서 오류를 생성합니다.
매직 .NET 응답 머신 빌드
이 자습서에서는 임의 응답을 사용하여 “예” 또는 “아니요” 질문에 대답하는 콘솔 애플리케이션을 빌드해 보겠습니다. 기능을 단계별로 빌드합니다. 일반적인 프로그램의 구조에 필요한 공식 절차가 아닌 작업에 집중할 수 있습니다. 그런 다음, 기능에 만족하면 필요한 경우 애플리케이션을 리팩터링할 수 있습니다.
좋은 시작점은 질문을 다시 콘솔에 쓰는 것입니다. 먼저 다음 코드를 작성할 수 있습니다.
Console.WriteLine(args);
args
변수를 선언하지 않습니다. 최상위 문이 포함된 단일 소스 파일의 경우 컴파일러는 명령줄 인수를 의미하는 args
를 인식합니다. 인수 형식은 모든 C# 프로그램과 같이 string[]
입니다.
다음 dotnet run
명령을 실행하여 코드를 테스트할 수 있습니다.
dotnet run -- Should I use top level statements in all my programs?
명령줄에 있는 --
뒤의 인수는 프로그램에 전달됩니다. 콘솔에 인쇄된 항목인 args
변수 형식을 확인할 수 있습니다.
System.String[]
콘솔에 질문을 쓰려면 인수를 열거하고 공백으로 구분해야 합니다. WriteLine
호출을 다음 코드로 바꿉니다.
Console.WriteLine();
foreach(var s in args)
{
Console.Write(s);
Console.Write(' ');
}
Console.WriteLine();
이제 프로그램을 실행하면 질문을 인수 문자열로 올바르게 표시합니다.
임의 응답으로 응답
질문을 에코한 후에는 임의 응답을 생성하는 코드를 추가할 수 있습니다. 먼저 가능한 응답의 배열을 추가합니다.
string[] answers =
[
"It is certain.", "Reply hazy, try again.", "Don’t count on it.",
"It is decidedly so.", "Ask again later.", "My reply is no.",
"Without a doubt.", "Better not tell you now.", "My sources say no.",
"Yes – definitely.", "Cannot predict now.", "Outlook not so good.",
"You may rely on it.", "Concentrate and ask again.", "Very doubtful.",
"As I see it, yes.",
"Most likely.",
"Outlook good.",
"Yes.",
"Signs point to yes.",
];
이 배열에는 긍정 응답 10개, 커밋이 아닌 응답 5개, 부정 응답 5개가 포함되어 있습니다. 다음으로, 다음 코드를 추가하여 배열에서 임의 응답을 생성하고 표시합니다.
var index = new Random().Next(answers.Length - 1);
Console.WriteLine(answers[index]);
애플리케이션을 다시 실행하여 결과를 볼 수 있습니다. 다음 출력과 같은 정보가 표시됩니다.
dotnet run -- Should I use top level statements in all my programs?
Should I use top level statements in all my programs?
Better not tell you now.
이 코드는 질문에 응답하지만 기능을 하나 더 추가하겠습니다. 질문 앱에서 응답에 관한 생각을 시뮬레이트하려고 합니다. 이렇게 하려면 약간의 ASCII 애니메이션을 추가하고 작동 중에 일시 중지합니다. 질문을 에코하는 줄 뒤에 다음 코드를 추가합니다.
for (int i = 0; i < 20; i++)
{
Console.Write("| -");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("/ \\");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("- |");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("\\ /");
await Task.Delay(50);
Console.Write("\b\b\b");
}
Console.WriteLine();
또한 소스 파일의 맨 위에 using
문을 추가해야 합니다.
using System.Threading.Tasks;
using
문은 파일의 다른 문 앞에 있어야 합니다. 그렇지 않으면 컴파일러 오류가 발생합니다. 프로그램을 다시 실행하여 애니메이션을 확인할 수 있습니다. 이를 통해 더 나은 환경을 제공할 수 있습니다. 원하는 대로 지연 길이를 실험합니다.
앞의 코드는 공백으로 구분된 회전하는 선 세트를 만듭니다. await
키워드를 추가하면 컴파일러가 프로그램 진입점을 async
한정자가 있는 메서드로 생성하고 System.Threading.Tasks.Task를 반환하도록 지시합니다. 이 프로그램은 값을 반환하지 않으므로 프로그램 진입점이 Task
를 반환합니다. 프로그램이 정수 값을 반환하는 경우 최상위 문의 끝에 return 문을 추가합니다. return 문은 반환할 정수 값을 지정합니다. 최상위 문에 await
식이 포함된 경우 반환 형식은 System.Threading.Tasks.Task<TResult>이 됩니다.
미래를 위한 리팩터링
프로그램은 다음 코드와 같이 표시됩니다.
Console.WriteLine();
foreach(var s in args)
{
Console.Write(s);
Console.Write(' ');
}
Console.WriteLine();
for (int i = 0; i < 20; i++)
{
Console.Write("| -");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("/ \\");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("- |");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("\\ /");
await Task.Delay(50);
Console.Write("\b\b\b");
}
Console.WriteLine();
string[] answers =
[
"It is certain.", "Reply hazy, try again.", "Don't count on it.",
"It is decidedly so.", "Ask again later.", "My reply is no.",
"Without a doubt.", "Better not tell you now.", "My sources say no.",
"Yes – definitely.", "Cannot predict now.", "Outlook not so good.",
"You may rely on it.", "Concentrate and ask again.", "Very doubtful.",
"As I see it, yes.",
"Most likely.",
"Outlook good.",
"Yes.",
"Signs point to yes.",
];
var index = new Random().Next(answers.Length - 1);
Console.WriteLine(answers[index]);
앞의 코드는 적절합니다. 예상대로 작동합니다. 하지만 재사용할 수 없습니다. 이제 애플리케이션이 작동하고 있으므로 재사용 가능한 부분을 가져오겠습니다.
한 후보는 대기 중인 애니메이션을 표시하는 코드입니다. 해당 코드 조각은 메서드가 될 수 있습니다.
먼저 파일에서 로컬 함수를 만들 수 있습니다. 현재 애니메이션을 다음 코드로 바꿉니다.
await ShowConsoleAnimation();
static async Task ShowConsoleAnimation()
{
for (int i = 0; i < 20; i++)
{
Console.Write("| -");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("/ \\");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("- |");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("\\ /");
await Task.Delay(50);
Console.Write("\b\b\b");
}
Console.WriteLine();
}
앞의 코드는 main 메서드 내에 로컬 함수를 만듭니다. 그래도 재사용할 수 없습니다. 따라서 해당 코드를 클래스로 추출합니다. utilities.cs라는 새 파일을 만들고 다음 코드를 추가합니다.
namespace MyNamespace
{
public static class Utilities
{
public static async Task ShowConsoleAnimation()
{
for (int i = 0; i < 20; i++)
{
Console.Write("| -");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("/ \\");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("- |");
await Task.Delay(50);
Console.Write("\b\b\b");
Console.Write("\\ /");
await Task.Delay(50);
Console.Write("\b\b\b");
}
Console.WriteLine();
}
}
}
최상위 문이 있는 파일은 최상위 문 뒤에 파일 끝 부분에 네임스페이스 및 형식을 포함할 수도 있습니다. 그러나 이 자습서에서는 애니메이션 메서드를 별도의 파일에 배치하여 쉽게 다시 사용할 수 있도록 합니다.
마지막으로, 애니메이션 코드를 정리하여 일부 중복을 제거할 수 있습니다.
foreach (string s in animations)
{
Console.Write(s);
await Task.Delay(50);
Console.Write("\b\b\b");
}
이제 전체 애플리케이션이 있으며 나중에 사용할 수 있도록 재사용 가능한 부분을 리팩터링했습니다. 아래와 같이 주 프로그램의 최종 버전에 표시된 대로 최상위 문에서 새 유틸리티 메서드를 호출할 수 있습니다.
using MyNamespace;
Console.WriteLine();
foreach(var s in args)
{
Console.Write(s);
Console.Write(' ');
}
Console.WriteLine();
await Utilities.ShowConsoleAnimation();
string[] answers =
[
"It is certain.", "Reply hazy, try again.", "Don’t count on it.",
"It is decidedly so.", "Ask again later.", "My reply is no.",
"Without a doubt.", "Better not tell you now.", "My sources say no.",
"Yes – definitely.", "Cannot predict now.", "Outlook not so good.",
"You may rely on it.", "Concentrate and ask again.", "Very doubtful.",
"As I see it, yes.",
"Most likely.",
"Outlook good.",
"Yes.",
"Signs point to yes.",
];
var index = new Random().Next(answers.Length - 1);
Console.WriteLine(answers[index]);
앞의 예제는 Utilities.ShowConsoleAnimation
에 대한 호출을 추가하고 using
문을 추가합니다.
요약
최상위 문을 사용하면 새 알고리즘을 탐색하는 데 사용할 간단한 프로그램을 더 쉽게 만들 수 있습니다. 코드의 다른 조각을 시도하여 알고리즘을 실험할 수 있습니다. 작동 기능을 알아본 후에는 코드를 더 쉽게 유지 관리할 수 있도록 리팩터링할 수 있습니다.
최상위 문은 콘솔 애플리케이션을 기반으로 하는 프로그램을 단순화합니다. 여기에는 Azure Functions, GitHub Actions, 기타 작은 유틸리티가 포함됩니다. 자세한 내용은 최상위 문(C# 프로그래밍 가이드)을 참조하세요.
.NET