📒 갈무리 - C# 코드 컨벤션
📌 코드 컨벤션이란?
- 마이크로소프트에서 권장하는 코딩 규칙을 통해 코드를 확인하는 사용자들이 내용에 집중할 수 있도록 일관성 있는 코드를 만들기 위한 코딩 규칙
📌 코드 컨벤션의 목표
- 코드를 확인하는 사용자들이 레이아웃이 아닌 내용에 집중할 수 있도록 일관성있게 표시되는 코드를 만듭니다.
- 코드를 확인하는 사용자들이 이전 경험을 토대로 한 가정을 통해 코드를 보다 빠르게 이해할 수 있도록 합니다.
- 코드를 보다 쉽게 복사, 변경 및 유지 관리할 수 있도록 합니다.
📌 명명 규칙
파스칼식 대/소문자
- class또는 struct의 이름을 지정할 때 파스칼 대/소문자를 사용
public class Convention
{
}
public struct Convention
{
}
- interface 이름을 지정할 때는 이름 앞에 I 접두사를 적용하고 파스칼식 대/소문자를 사용
public interface IConvetion
{
}
- 메서드 및 로컬 함수와 같은 형식의 멤버의 이름을 지정할 때 public 파스칼 대/소문자를 사용
public class Convetion
{
public void ExMethod()
{
static void public ExFunc()
{
}
}
}
카멜식 대/소문자
- 지역 변수와 함수의 매개 변수는 카멜 대/소문자를 사용
public void ExFunc(int exParemeter)
{
int numVal1;
int numVal2;
}
📌 레이아웃 규칙
- 코드를 한줄에 하나씩 작성
Console.WriteLine("한 줄에");
Console.WriteLine("하나씩");
- 선언을 한 줄에 하나씩 작성
int num1 = 1;
int num2 = 2;
- 기본 코드 편집기 설정(스마트 들여쓰기, 4자 들여쓰기, 탭을 공백으로 저장)을 사용
- 연속 줄이 자동으로 들여쓰기되지 않으면 탭 정지 하나(공백 4개)만큼 들여쓰기
static void Main(string[] args)
{
if (true)
{
Console.WriteLine("들여쓰기는 공백 4칸");
}
}
- 메서드 정의와 속성 정의 간에는 빈 줄을 하나 이상 추가
public Conventions Convention { get; set; }
public Conventions GetConvetino()
{
return this.Convention;
}
- 식이 여러 개일 경우 괄호를 사용하여 식의 절을 명확하게 구분
if ((val > val2) && (val1 > val3))
{
}
📌 주석 달기 규칙
- 코드 줄의 끝이 아닌 별도의 줄에 주석을 배치
public void ExFunc()
{
}
- 주석 텍스트는 대문자로 시작
public void ExFunc()
{
}
- 주석 텍스트 끝에는 마침표를 붙인다.
- 주석 구분 기호(//)와 주석 텍스트 사이에 공백을 하나 삽입한다.
- 서식이 지정된 별도 블록으로 주석을 묶지 않는다.
📌 문법 규칙
- 짧은 문자열을 연결할 때 문자열 보간을 사용한다.
string name = $"{nameList[n].LastName}, {nameList[n].FirstName}";
- 많은 양의 텍스트를 사용할 때 문자열을 루프에 추가하려면 StringBuilder 개체를 사용한다.
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
var manyPhrases = new StringBuilder();
for (var i = 0; i < 10000; i++)
{
manyPhrases.Append(phrase);
}
📌 암시적 형식 지역 변수
- 할당 오른쪽에서 변수 형식이 명확하거나 형식이 중요하지 않으면 지역 변수에 대해 암시적 형식을 사용한다.
var var1 = "문자열";
var var2 = 10;
- 할당 오른쪽에서 변수 형식이 명확하지 않으면 var을 사용하지 않는다. 메서드 이름에서 형식이 명확하다고 가정하지 않는다. 변수 형식이 new 연산자나명시적 캐스트인 경우 명환한 것으로 간주한다.
- 변수 이름을 사용하여 변수 형식을 지정하지 않는다. 이렇게 하면 형식이 올바르게 지정되지 않을 수 있다.
var inputInt = Console.ReadLine();
Console.WriteLine(inputInt);
- dynamic 대신 var을 사용하지 않는다. 런타임 형식 유추를 원하는 경우 dynamic을 사용한다.
- 암시적 형식을 사용하여 루프 변수 for의 형식을 결정한다.
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala"; var manyPhrases = new StringBuilder();
for (var i = 0; i < 10000; i++)
{
manyPhrases.Append(phrase);
}
- foreach 루프의 경우에는 암시적 형식을 사용하지 않는다.
foreach (char ch in laugh)
{
if (ch == 'h')
Console.Write("H");
else
Console.Write(ch);
}
Console.WriteLine();
📌 부호 없는 데이터 형식
- 일반적으로는 부호 없는 형식 대신 int를 사용한다. int는 C# 전체에서 일반적으로 사용되며, int를 사용하는 경우 다른 라이브러리와 보다 쉽게 상호 작용할 수 있다.
📌 배열
- 선언 줄에서 배열을 초기화할 때는 간결한 구문을 사용한다.
string[] vowels1 = { "a", "e", "i", "o", "u" };
- 명시적 인스턴스화를 사용하는 경우 var을 사용할 수 있다.
var vowels2 = new string[] { "a", "e", "i", "o", "u" };
📌 Delegate
- Delegate 형식을 정의하는 대신 Func<>, Action<>을 사용한다.
📌 예외 처리의 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 문을 대신 사용한다.
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;
}
📌 && 및 || 연산자
- 예외를 방지하고 불필요한 비교를 건너뛰어 성능을 개선하기 위해 비교를 수행할 때 & 대신 &&를, | 대신 ||를 사용한다.
Console.Write("배당금을 입력 : ");
int dividend = Convert.ToInt32(Console.ReadLine());
Console.Write("나누는 수 입력 : ");
int divisor = Convert.ToInt32(Console.ReadLine());
if ((divisor != 0) && (dividend / divisor > 0))
{
Console.WriteLine("Quotient: {0}", dividend / divisor);
}
else
{
Console.WriteLine("0으로 나누면 else");
}
- divisor가 0인 경우 if 문의 두 번째 절을 실행하면 런타임 오류가 발생하게 된다. 그러나 연산자는 && 첫 번째 식이 false일 때 일단락하기 때문에 두 번째 식을 계산하지 않게 된다. 연산자 &는 두 식 모두 계산하므로 divisor가 0일 때 런타임 오류가 발생하게 된다.
📌 new 연산자
- new 연산자는 보다 간결하게 사용한다.
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());
}
📌 코드 가독성 높이기
- 삼항 연산자를 사용하지 않는다.
- else 예약어를 쓰지 않는다. (힌트: if 조건절에서 값을 return 하는 방식으로 구현하면 else를 사용하지 않아도 된다. else를 쓰지 말라고 하니 switch/case로 구현하는 경우가 있는데 switch/case도 허용하지 않는다.)
- indent(인덴트, 들여쓰기) depth를 3이 넘지 않도록 구현한다. 2까지만 허용한다.
- 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다.함수(또는 메서드)가 한 가지 일만 잘하도록 구현한다.
- 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
- 메서드의 파라미터 개수는 최대 3개까지만 허용한다.
💡 TIP
마이크로소프트에서 권장하는 코딩 규칙이 존재하지만, 본인이 속한 팀이나 직장에서의 코딩 규칙이 있다면 그 규칙을 따르는 것이 좋다.