[C#] 56. 코딩 규약


Study/C#  2021. 10. 21. 17:28

안녕하세요. 명월입니다.


이 글은 C#의 코딩 규약에 대한 글입니다.


먼저 코딩 규약에 대해서 간략하게 설명하면, 코딩 규약은 우리가 프로그램을 작성할 때 지켜야하는 규칙이라고 할 수 있습니다. 프로그램 성능과 효율성과는 관련은 없고 여러 명에서 프로그램을 만들 때 소스를 알아보기 쉽게 정해 놓은 것입니다.

간단하게 예를 들면, 함수 명명법을 보면 「동사+ 명사」 형태로 보통 「GetData」 식으로 작성을 합니다. 그러나 DataGet 이런 식으로 함수명을 만든다 해도 동작이 안되는 것은 아닙니다. 아니 「abcdefghijklm」 이라고 함수 명을 지어도 동작하는 데는 문제없습니다.

그러나 「GetData」이라고 함수명을 작성하면 함수의 소스를 보지 않더라도 이 함수는 「어떤 데이터를 가져오는 처리구나」 라고 예상할 수 있지만 이상하게 함수명을 만든다면 함수명 만으로 어떤 동작을 하는 지 알 수가 없습니다.


그래서 MS에서는 C# 코딩을 할 때, 함수 명명법, 소스 내 띄어쓰기 등의 규칙을 정해 놓고 사용하기를 권장하고 있습니다. 이것을 코딩 규약이라고 합니다.

링크 - MS 코딩 표준

명명 규칙

정규화된 클래스의 이름이 한 줄에 길게 표시 되지 않게 합니다. 즉, 포함되어 있는 모든 namespace를 설정할 필요는 없습니다.

// 잘못된 명명 방법
System.Diagnostics.PerformanceCounterCategory category = new System.Diagnostics.PerformanceCounterCategory(); 
// 지시문은 var로 대체하여 최대한 간결하게 사용하고 선언문의 네임스페이스는 using을 사용하여 한줄의 코딩 표기를 최대한 줄인다.
using System.Diagnostics;
var category = new PerformanceCounterCategory();

레이 아웃 규칙

좋은 레이 아웃 규칙은 코딩의 구문을 강조하고 읽기 쉽게 작성하는 것입니다.

1. 기본 코드 편집기 설정 (4 자 들여 쓰기, 공백으로 저장된 탭)을 사용합니다.

   단축 키로는 Ctrl + K + D입니다.

   링크 - https://docs.microsoft.com/en-us/visualstudio/ide/reference/options-text-editor-csharp-formatting?view=vs-2019


2. 한 줄에 하나의 문장만 씁니다.

// 잘못된 코딩 방법
if( a > 0) for(int i = 0; i < 10; i++) Console.WriteLine(i);
// 올바른 코딩 방법
if (a > 0)
{
  for (int i = 0; i < 10; i++)
  {
    Console.WriteLine(i);
  } 
}


3. 연속적인 체인식 함수 선언이 자동으로 들여 쓰기되지 않으면 탭 간격을 한 칸 들여 씁니다.(4 칸)

   -> 특히 Linq 식의 경우는, .Where .Select의 연속된 처리식이 가능합니다만 라인을 바꿀 때는 꼭 들여쓰기를 통해 구분을 정합니다.

List.Where(x => x.IsCheck)
     .Select의(x => x.Data);


4. 메서드 정의와 프로퍼티 정의 사이에 적어도 하나의 공백 행을 추가합니다.

   -> 이게 무슨 말인지 진짜 고민을 많이 했습니다.

아마도 메서드 이름과 파라미터 사이에 공백을 넣으라는 뜻인것 같습니다.

public void SetData (int a)
{
}


5. 괄호를 사용하여 표현식에서 절을 명백하게 만듭니다.

   -> if 나 for의 경우 괄호없이 코딩(한줄 코딩)이 가능하나 절대 추천하지 않습니다. 왠만하면 괄호를 넣어서 확실하게 구분을 하는 편이 좋습니다.

코멘트 규칙

1. 코드 줄 끝에 주석을 넣지말고 별도의 줄에 주석을 넣습니다.

// 잘못된 표기법 
if ( a == 0 ) // a가 0일 경우
{
}  
// 올바른 표기법 
// a가 0일 경우
if ( a == 0 )
{
}

2. 대문자로 주석 텍스트를 시작합니다.(영어만 해당되는 사항입니다.)

3. 마침표가 있는 설명 텍스트를 끝냅니다.(영어만 해당되는 사항입니다.)


4. 주석 구분 기호 (//)와 주석 텍스트 사이에 하나의 공백을 넣습니다.

//잘못된 표기법
// 올바른 표기법

언어 규칙

1. 문자열 데이터 형식

  1-1. 가능한한 string interpolation(보간법)을 사용합니다.

int data = 10;
Console.WriteLine($"Hello world {data}");

link - [C#] String 보간법(interpolation)


  1-2. 많은 양의 String 객체를 합칠 경우에는 StringBuilder를 사용합니다.

var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
var manyPhrases = new StringBuilder();
for (var i = 0; i < 10000; i++)
{
  manyPhrases.Append(phrase);
}


2. 암시적 변수형 타입

  2-1. 변수 타입이 정확하거나 별도의 구분이 필요없을 경우는 var를 사용하세요.

link - [C#] 26. var 키워드와 dynamic 키워드

  2-2. 타입이 불분명한 경우, dynamic 타입과 object타입의 경우는 var을 사용하지 않습니다.

  2-3. 변수 이름에는 변수 형태(자료형)를 암시하는 명명을 하지 않습니다.

// 잘못된 경우
var inputInt = 10; 

  2-4. for와 foreach에서는 반드시 var 타입을 사용합니다.

for (var i=0 ; i<100 ; i++) 
{
}
foreach (var data in List)
{
}


3. Unsigned Data Type

   일반적으로 Unsigned Type은 사용하지 않습니다. 예로 int형으로 충분히 가능한 코딩을 unsigned int를 선언할 필요는 없습니다.

4. 배열

   최대한 간결하게 선언합니다.

string[] vowels1 = { "a", "e", "i", "o", "u" };
var vowels2 = new string[] { "a", "e", "i", "o", "u" };


5. 델리게이트

   최대한 간결하게 선언하고 클래스에 종속되지 않게 사용한다.(클래스의 종속 관계는 사양에 따라 달라질 수 있다.)

using System;

namespace Example
{
  // 델리게이트는 class의 외부에서도 생성가능
  public delegate void Del(string message);

  class Program
  {
    // 델리게이트 예제 함수
    public static void DelMethod(string str)
    {
      Console.WriteLine("DelMethod argument: {0}", str);
    }
    // 실행 함수
    static void Main(string[] args)
    {
      Del exampleDel2 = DelMethod;
      Del exampleDel1 = new Del(DelMethod);
    }
  }
}


6. 예외 처리

  6.1 예외 처리는 try~catch를 사용합니다..(다른 예외 처리도 있었나???)

  6.2 Dispose 메소드 호출 코드는 using를 사용합니다.

Font font1 = new Font("Arial", 10.0f);
try
{
  byte charset = font1.GdiCharSet;
}
finally
{
  if (font1 != null)
  {
    ((IDisposable)font1).Dispose();
  }
}
// try - finally 문을 using으로 대체 가능하다.
using (Font font2 = new Font("Arial", 10.0f))
{
  byte charset = font2.GdiCharSet;
}


7. &&와 || 연산자

  성능과 가독성을 높이고 싶으면 가능하면 &와 |대신에 &&와 ||를 사용합니다.

  (|와 ||는 용법이 다른게 아닌가? bit 연산의 경우는 |가 맞지만, 아마 true, false를 구분하려면 가능한 ||를 사용하라는 듯. 아니 용법이 전혀 다르다고...)

// 잘못된 표기
if(a == 1 & b == 1)
{
}
// 올바른 표기
if(a == 1 && b == 1)
{
}


8. 이벤트

  나중에 제거할 필요가 없는 event handler의 경우 람다식을 추천하고 있습니다.

  (분명히 람다식이 편한 것은 맞는데... 너무 람다식 위주로 가면 가독성이 떨어지는데...)

// 잘못된 사용법
public Form1()
{
  this.Click += new EventHandler(Form1_Click);
}
void Form1_Click(object sender, EventArgs e)
{
  MessageBox.Show(((MouseEventArgs)e).Location.ToString());
}
// 올바른 사용법
public Form2()
{
  this.Click += (s, e) =>
  {
    MessageBox.Show(((MouseEventArgs)e).Location.ToString());
  }; 
}


9. 정적 맴버

   반드시 클래스 명을 붙힙니다.

public class Test
{
  public static int Data;
  public void Print()
  {
    // 잘못된 표기법
    Data = 0;
    // 올바른 표기법
    Test.Data = 1;
  }
}


10. Linq식

  10-1. 변수명을 결과값이 유추되는 값을 명명합니다.

  10-2. select 결과에 익명 클래스를 사용할 경우 속성의 첫글자를 대문자를 사용합니다.

  10-3. 결과의 변수값이 모호할 경우 명확하게 바꿉니다.

  10-4. 결과의 변수 타입은 var를 사용합니다.

  10-5. 들여쓰기는 from에 맞춤니다.

    -> 개인적으로 Linq식은 위에 했던 이야기 반복된 이야기 하는 것같습니다.

그 밖에 코딩 명명 규칙

예전에 제가 코딩 규칙을 공부할 때와 현재 코딩 규칙이 많이 다른 듯합니다. 제가 기억나는 코딩 규칙을 추가로 작성합니다.


1. 클래스의 맴버 변수는 반드시 private로 선언합니다.

2. 맴버 변수의 첫 글자는 소문자이며 명사로만 구성합니다.

3. 프로퍼티의 첫 글자는 대문자입니다.

4. 함수는 「동사 + 명사」의 형태이고 첫 글자와 절 구분은 반드시 대문자입니다. 예 GetData, CreateBuildExcelSheet...

   참고로 자바의 경우는 「동사 + 명사」 의 형태는 같은데 첫 글자는 소문자, 절 구분은 대문자입니다. 다른 규약이니 참고하시면 됩니다. 예) getData, createBuildExcelSheet..

6. 괄호 앞은 반드시 개행이다.

   참고로 자바의 경우는 괄호 앞에 개행을 넣지 않는게 규칙입니다.

// C#의 경우는 
if( a == 1)
{
}
// Java의 경우는 
if ( a == 1) { 
}

7. 클래스는 되도록 데코레이터 패턴으로 작성한다. (interface에서 추상 클래스를 상속받고, 클래스를 상속 받는다.)

8. 클래스 명과 파일명은 일치하게 작성한다.

9. 네임 스페이스 명과 폴더명은 일치하게 작성한다.

10. goto문은 사용하지 않는다.

11. if - else 문보다는 if break, continue 혹은 if return문을 사용한다.

// 잘못된 사용법
for(var i=0; i<100; i++)
{
  if(i%2 == 0)
  {
    Console.WriteLine("짝수입니다.");
  }
  else
  {
    Console.WriteLine("홀수입니다.");
  }
}
// 올바른 사용법 
for(var i=0; i<100; i++)
{
  if(i%2 == 0)
  {
    Console.WriteLine("짝수입니다.");
    continue;
  }
  Console.WriteLine("홀수입니다.");
}

12. 가능하면 한 줄에 하나의 처리식만 작성한다.

String data = "";
// 잘못된 사용법
Console.WriteLine((String.IsNullOrEmpty(data) ? "null" : data)?.Length);
// 올바른 사용법
String buffer;
int length = 0;
if (String.IsNullOrEmpty(data))
{
  buffer = "null";
}
else
{
  buffer = data;
}
Console.WriteLine(buffer.Length);

그 밖의 명명 규칙의 경우는 제가 예전에 코딩 표준을 공부할 때 있었던 내용인데 지금은 없네요. 왜 없어졌는 지는 모르겠는데, 프로젝트 할 때에 필요한 규칙이니 알아두면 좋습니다.

위의 C#의 코딩 규약은 성능에는 영향의 거의 없습니다. 있는 경우도 있으나, 보통은 가독성을 올리기 위한 방법이 대부분입니다.

그리고 실무에서의 프로젝트는 혼자서 프로젝트를 진행하기 보다는 여러 명에서 작성하는 경우가 많으니 반드시 지키는 것이 서로 간의 불필요한 코드 논쟁을 줄이고 소스 저장도(Git)에도 불필요한 스탭이 생기지 않습니다.


물론 큰 프로젝트나 여러 기업이 붙어서 하는 경우에는 별도의 코드 규약이 있는 경우도 있는데 보통은 이 코딩 기본 규약을 좀 더 엄격히 설정한 경우가 많습니다. 사내 SE던가 학교에서 진행하는 조별 프로젝트나 개별 프로젝트에서는 이 규약을 무시하고 작성하는 경우가 많습니다.

그것이 잘못되었다는 건 아니지만 규약을 맞추지 못하면 그만큼 오픈 API등를 사용하는데 제약이 걸리는 경우도 있고, 작성한 라이브러리를 배포하기도 애매해 집니다. 나아가 가독성이 매우 떨어지는 경우가 발생하기 때문에 코드 리뷰등에도 오해나 문제가 발생할 수도 있겠네요.


이 코딩 규약이 정말 사소하고 쉽게 무시될 수 있는 부분이기도 하지만 프로젝트의 중요도에 따라 아주 중요한 문제가 될 수도 있습니다.


여기까지 C#의 코딩 규약에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.