C# 코딩 규칙
코딩 규칙은 다음과 같은 용도로 사용됩니다.
- 코드를 확인하는 사용자들이 레이아웃이 아닌 내용에 집중할 수 있도록 일관성 있게 표시되는 코드를 만듭니다.
- 코드를 확인하는 사용자들이 이전 경험을 토대로 한 가정을 통해 코드를 보다 빠르게 이해할 수 있도록 합니다.
- 코드를 보다 쉽게 복사, 변경 및 유지 관리할 수 있도록 합니다.
- C# 모범 사례를 제시합니다.
중요
이 문서의 지침은 Microsoft에서 샘플과 설명서를 개발하는 데 사용됩니다. .NET 런타임, C# 코딩 스타일 지침에서 채택되었습니다. 이를 사용하거나 필요에 맞게 조정할 수 있습니다. 주요 목표는 프로젝트, 팀, 조직 또는 회사 소스 코드 내에서 일관성과 가독성입니다.
명명 규칙
C# 코드를 작성할 때 고려할 몇 가지 명명 규칙이 있습니다.
다음 예제에서는 표시된 public 요소와 관련된 모든 지침이 외부 호출자에게 표시되도록 의도된 요소 및 protected internal 요소를 사용할 protected 때도 적용할 수 있습니다.
파스칼식 대/소문자
또는 이름을 지정할 classrecordstruct때 파스칼 대/소문자("PascalCasing")를 사용합니다.
public class DataService
{
}
public record PhysicalAddress(
string Street,
string City,
string StateOrProvince,
string ZipCode);
public struct ValueCoordinate
{
}
interface 이름을 지정할 때는 이름 앞에 I 접두사를 적용하고 파스칼식 대/소문자를 사용합니다. 이렇게 하면 해당 항목이 interface임을 소비자에게 분명히 나타냅니다.
public interface IWorkerQueue
{
}
필드, 속성, 이벤트, 메서드 및 로컬 함수와 같은 형식의 멤버 이름을 지정할 public 때 파스칼 대/소문자를 사용합니다.
public class ExampleEvents
{
// A public field, these should be used sparingly
public bool IsValid;
// An init-only property
public IWorkerQueue WorkerQueue { get; init; }
// An event
public event Action EventProcessing;
// Method
public void StartEventProcessing()
{
// Local function
static int CountQueueItems() => WorkerQueue.Count;
// ...
}
}
위치 레코드를 작성할 때는 레코드의 공용 속성인 매개 변수에 파스칼 대/소문자를 사용합니다.
public record PhysicalAddress(
string Street,
string City,
string StateOrProvince,
string ZipCode);
위치 레코드에 대한 자세한 내용은 속성 정의에 대한 위치 구문을 참조하세요.
카멜식 대/소문자
이름을 지정하거나 internal 필드를 지정할 private 때 낙타 대/소문자("camelCasing")를 사용하고 접두사로 _하십시오.
public class DataService
{
private IWorkerQueue _workerQueue;
}
팁
문 완성을 지원하는 IDE에서 이러한 명명 규칙을 따르는 C# 코드를 편집할 때 _을 입력하면 개체 범위 멤버가 모두 표시됩니다.
private 또는 internal인 static 필드를 사용할 때는 s_ 접두사를 사용하고 스레드 정적인 경우 t_을 사용합니다.
public class DataService
{
private static IWorkerQueue s_workerQueue;
[ThreadStatic]
private static TimeSpan t_timeSpan;
}
메서드 매개 변수를 작성할 때 카멜식 대/소문자를 사용합니다.
public T SomeMethod<T>(int someNumber, bool isValid)
{
}
C# 명명 규칙에 대한 자세한 내용은 C# Coding Style(C# 코딩 스타일)을 참조하세요.
추가 명명 규칙
using 지시문이 포함되지 않는 예제에서는 네임스페이스 한정을 사용합니다. 프로젝트에서 네임스페이스를 기본적으로 가져오는 경우에는 해당 네임스페이스의 이름을 정규화할 필요가 없습니다. 정규화된 이름은 한 줄에 표시하기가 너무 길면 다음 예제에 나와 있는 것처럼 점(.)으로 분할할 수 있습니다.
var currentPerformanceCounterCategory = new System.Diagnostics. PerformanceCounterCategory();다른 지침에 맞도록 조정하기 위해 Visual Studio 디자이너 도구를 사용하여 만든 개체 이름을 변경할 필요는 없습니다.
레이아웃 규칙
효율적인 레이아웃에서는 서식을 사용하여 코드 구조를 강조하고 코드를 보다 쉽게 읽을 수 있도록 생성합니다. Microsoft 예제 및 샘플은 다음 규칙을 따릅니다.
기본 코드 편집기 설정(스마트 들여쓰기, 4자 들여쓰기, 탭을 공백으로 저장)을 사용합니다. 자세한 내용은 옵션, 텍스트 편집기, C#, 서식을 참조하세요.
문을 한 줄에 하나씩만 작성합니다.
선언을 한 줄에 하나씩만 작성합니다.
연속 줄이 자동으로 들여쓰기되지 않으면 탭 정지 하나(공백 4개)만큼 들여씁니다.
메서드 정의와 속성 정의 간에는 빈 줄을 하나 이상 추가합니다.
다음 코드에 나와 있는 것처럼 괄호를 사용하여 식의 절을 명확하게 구분합니다.
if ((val1 > val2) && (val1 > val3)) { // Take appropriate action. }
주석 달기 규칙
코드 줄의 끝이 아닌 별도의 줄에 주석을 배치합니다.
주석 텍스트는 대문자로 시작합니다.
주석 텍스트 끝에는 마침표를 붙입니다.
다음 코드에 나와 있는 것처럼 주석 구분 기호(//)와 주석 텍스트 사이에 공백을 하나 삽입합니다.
// The following declaration creates a query. It does not run // the query.서식이 지정된 별표 블록으로 주석을 묶지 않습니다.
모든 공용 멤버가 해당 동작에 대한 적절한 설명을 제공하는 데 필요한 XML 주석이 있는지 확인합니다.
언어 지침
다음 섹션에서는 C# 팀이 코드 예제와 샘플을 준비할 때 따르는 방식에 대해 설명합니다.
String 데이터 형식
다음 코드에 나와 있는 것처럼 문자열 보간을 사용하여 짧은 문자열을 연결합니다.
string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";특히 많은 양의 텍스트를 사용할 때 문자열을 루프에 추가하려면 StringBuilder 개체를 사용합니다.
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala"; var manyPhrases = new StringBuilder(); for (var i = 0; i < 10000; i++) { manyPhrases.Append(phrase); } //Console.WriteLine("tra" + manyPhrases);
암시적 형식 지역 변수
할당 오른쪽에서 변수 형식이 명확하거나 정확한 형식이 중요하지 않으면 지역 변수에 대해 암시적 형식을 사용합니다.
var var1 = "This is clearly a string."; var var2 = 27;할당 오른쪽에서 변수 형식이 명확하지 않으면 var을 사용하지 않습니다. 메서드 이름에서 형식이 명확하다고 가정하지 않습니다. 변수 형식이
new연산자나 명시적 캐스트인 경우 명확한 것으로 간주됩니다.int var3 = Convert.ToInt32(Console.ReadLine()); int var4 = ExampleClass.ResultSoFar();변수 이름을 사용하여 변수 형식을 지정하지 않습니다. 이렇게 하면 형식이 올바르게 지정되지 않을 수 있습니다. 다음 예에서 변수 이름
inputInt는 오해의 소지가 있습니다. 문자열입니다.var inputInt = Console.ReadLine(); Console.WriteLine(inputInt);dynamic 대신
var를 사용하지 않습니다. 런타임 형식 유추를 원하는 경우dynamic을 사용합니다. 자세한 내용은 dynamic 형식 사용(C# 프로그래밍 가이드)을 참조하세요.암시적 입력을 사용하여 루프의 루프 변수
for형식을 결정합니다.다음 예제에서는
for문에서 암시적 형식을 사용합니다.var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala"; var manyPhrases = new StringBuilder(); for (var i = 0; i < 10000; i++) { manyPhrases.Append(phrase); } //Console.WriteLine("tra" + manyPhrases);암시적 입력을 사용하여 루프에서 루프 변수
foreach의 형식을 결정하지 마세요. 대부분의 경우 컬렉션의 요소 형식이 즉시 명확하지는 않습니다. 컬렉션의 이름은 요소의 형식을 유추하는 데만 의존해서는 안 됩니다.다음 예제에서는
foreach문에서 명시적 형식을 사용합니다.foreach (char ch in laugh) { if (ch == 'h') Console.Write("H"); else Console.Write(ch); } Console.WriteLine();참고
반복 가능한 컬렉션의 요소 형식을 실수로 변경하지 않도록 주의해야 합니다. 예를 들어
foreach문에서 System.Linq.IQueryable을 System.Collections.IEnumerable으로 전환하기 쉬운데 그러면 쿼리 실행이 변경됩니다.
부호 없는 데이터 형식
일반적으로는 부호 없는 형식 대신 int를 사용합니다. int는 C# 전체에서 일반적으로 사용되며, int를 사용하는 경우 다른 라이브러리와 보다 쉽게 상호 작용할 수 있습니다.
배열
선언 줄에서 배열을 초기화할 때는 간결한 구문을 사용합니다. 다음 예제에서는 string[] 대신 var을 사용할 수 없습니다.
string[] vowels1 = { "a", "e", "i", "o", "u" };
명시적 인스턴스화를 사용하는 경우 var을 사용할 수 있습니다.
var vowels2 = new string[] { "a", "e", "i", "o", "u" };
배열 크기를 지정하는 경우 한 번에 하나씩 요소를 초기화해야 합니다.
var vowels3 = new string[5];
vowels3[0] = "a";
vowels3[1] = "e";
// And so on.
대리자
대리자 형식을 정의하는 대신 Func<> 및 Action<>을 사용합니다. 클래스에서 대리자 메서드를 정의합니다.
public static Action<string> ActionExample1 = x => Console.WriteLine($"x is: {x}");
public static Action<string, string> ActionExample2 = (x, y) =>
Console.WriteLine($"x is: {x}, y is {y}");
public static Func<string, int> FuncExample1 = x => Convert.ToInt32(x);
public static Func<int, int, int> FuncExample2 = (x, y) => x + y;
Func<> 또는 Action<> 대리자로 정의된 시그니처를 사용하여 메서드를 호출합니다.
ActionExample1("string for x");
ActionExample2("string for x", "string for y");
Console.WriteLine($"The value is {FuncExample1("1")}");
Console.WriteLine($"The sum is {FuncExample2(1, 2)}");
대리자 형식의 인스턴스를 만드는 경우 간결한 구문을 사용합니다. 클래스에서 일치하는 시그니처가 있는 대리자 형식 및 메서드를 정의합니다.
public delegate void Del(string message);
public static void DelMethod(string str)
{
Console.WriteLine("DelMethod argument: {0}", str);
}
대리자 형식의 인스턴스를 만들고 호출합니다. 다음 선언에서는 압축된 구문을 보여 줍니다.
Del exampleDel2 = DelMethod;
exampleDel2("Hey");
다음 선언에서는 전체 구문을 사용합니다.
Del exampleDel1 = new Del(DelMethod);
exampleDel1("Hey");
예외 처리의 try-catch 및 using 문
대부분의 예외 처리에서는 try-catch 문을 사용합니다.
static string GetValueFromArray(string[] array, int index) { try { return array[index]; } catch (System.IndexOutOfRangeException ex) { Console.WriteLine("Index is out of range: {0}", index); throw; } }C# using 문을 사용하면 코드를 간소화할 수 있습니다.
finally블록의 코드가 Dispose 메서드 호출뿐인 try-finally 문이 있는 경우에는using문을 대신 사용합니다.다음 예제에서
try-finally문은finally블록의Dispose만 호출합니다.Font font1 = new Font("Arial", 10.0f); try { byte charset = font1.GdiCharSet; } finally { if (font1 != null) { ((IDisposable)font1).Dispose(); } }using문을 사용하여 같은 작업을 수행할 수 있습니다.using (Font font2 = new Font("Arial", 10.0f)) { byte charset2 = font2.GdiCharSet; }C# 8 이상 버전에서는 중괄호가 필요하지 않은 새
using구문을 사용합니다.using Font font3 = new Font("Arial", 10.0f); byte charset3 = font3.GdiCharSet;
&& 및 || 연산자
예외를 방지하고 불필요한 비교를 건너뛰어 성능을 개선하려면 비교를 수행할 때 다음 예제에 나와 있는 것처럼 & 대신 &&를, | 대신 ||를 사용합니다.
Console.Write("Enter a dividend: ");
int dividend = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter a divisor: ");
int divisor = Convert.ToInt32(Console.ReadLine());
if ((divisor != 0) && (dividend / divisor > 0))
{
Console.WriteLine("Quotient: {0}", dividend / divisor);
}
else
{
Console.WriteLine("Attempted division by 0 ends up here.");
}
제수가 0인 경우 if 문의 두 번째 절을 실행하면 런타임 오류가 발생합니다. 그러나 연산자는 && 첫 번째 식이 false이면 단락합니다. 즉, 두 번째 식을 계산하지 않습니다. 연산자는 & 둘 다 평가하므로 0이면 divisor 런타임 오류가 발생합니다.
new 연산자
다음 선언에 나와 있는 것처럼 간결한 형식의 개체 인스턴스화 중 하나를 사용합니다. 두 번째 예제는 C# 9부터 사용할 수 있는 구문을 보여 줍니다.
var instance1 = new ExampleClass();ExampleClass instance2 = new();앞의 선언은 다음 선언과 같습니다.
ExampleClass instance2 = new ExampleClass();다음 예제에 나와 있는 것처럼 개체 이니셜라이저를 사용하여 개체 만들기를 간소화합니다.
var instance3 = new ExampleClass { Name = "Desktop", ID = 37414, Location = "Redmond", Age = 2.3 };다음 예제에서는 앞의 예제와 같은 속성을 설정하지만, 이니셜라이저를 사용하지는 않습니다.
var instance4 = new ExampleClass(); instance4.Name = "Desktop"; instance4.ID = 37414; instance4.Location = "Redmond"; instance4.Age = 2.3;
이벤트 처리
나중에 제거할 필요가 없는 이벤트 처리기를 정의하려는 경우 람다 식을 사용합니다.
public Form2()
{
this.Click += (s, e) =>
{
MessageBox.Show(
((MouseEventArgs)e).Location.ToString());
};
}
람다 식은 다음과 같은 기존 정의를 줄여 줍니다.
public Form1()
{
this.Click += new EventHandler(Form1_Click);
}
void Form1_Click(object? sender, EventArgs e)
{
MessageBox.Show(((MouseEventArgs)e).Location.ToString());
}
정적 멤버
ClassName.StaticMember와 같이 클래스 이름을 사용하여 static 멤버를 호출합니다. 이렇게 하면 정적 액세스가 명확하게 표시되므로 코드를 보다 쉽게 읽을 수 있습니다. 파생 클래스 이름을 사용하여 기본 클래스에 정의된 정적 멤버를 정규화하지 않습니다. 이 코드는 컴파일되기는 하지만 가독성이 떨어지며 나중에 파생 클래스와 이름이 같은 정적 멤버를 추가하면 코드가 손상될 수도 있습니다.
LINQ 쿼리
쿼리 변수에 의미 있는 이름을 사용합니다. 다음 예제에서는 Seattle 거주 고객에 대해
seattleCustomers를 사용합니다.var seattleCustomers = from customer in customers where customer.City == "Seattle" select customer.Name;별칭을 사용하여 익명 형식의 속성 이름 대/소문자를 올바르게 표시합니다(파스칼식 대/소문자 사용).
var localDistributors = from customer in customers join distributor in distributors on customer.City equals distributor.City select new { Customer = customer, Distributor = distributor };결과의 속성 이름이 모호하면 속성 이름을 바꿉니다. 예를 들어 쿼리에서 고객 이름과 배포자 ID를 반환하는 경우 결과에서 이러한 정보를
Name및ID로 유지하는 대신Name은 고객의 이름이고ID는 배포자의 ID임을 명확하게 나타내도록 이름을 바꿉니다.var localDistributors2 = from customer in customers join distributor in distributors on customer.City equals distributor.City select new { CustomerName = customer.Name, DistributorID = distributor.ID };쿼리 변수 및 범위 변수의 선언에서 암시적 형식을 사용합니다.
var seattleCustomers = from customer in customers where customer.City == "Seattle" select customer.Name;이전 예제와 같이 절 아래에
from쿼리 절을 맞춥니다.다른 쿼리 절 앞에 절을 사용하여
where이후 쿼리 절이 축소되고 필터링된 데이터 집합에서 작동하도록 합니다.var seattleCustomers2 = from customer in customers where customer.City == "Seattle" orderby customer.Name select customer;절 대신 여러
from절을join사용하여 내부 컬렉션에 액세스합니다. 예를 들어Student개체 컬렉션이 각각 테스트 점수 컬렉션을 포함하는 경우 다음 쿼리를 실행하면 90점보다 높은 각 점수와 해당 점수를 받은 학생의 성이 반환됩니다.var scoreQuery = from student in students from score in student.Scores! where score > 90 select new { Last = student.LastName, score };
보안
보안 코딩 지침의 지침을 따르세요.