[C#/dotnet] 일반적인 C# 코드 규칙

2024. 10. 23. 12:19·C#
728x90
728x90

참조

https://learn.microsoft.com/ko-kr/dotnet/csharp/fundamentals/coding-style/coding-conventions

 

 


언어 지침

  1) 제네릭 예외 처리를 지양한다.

    - 범용 예외 타입인 'Exception'을 포괄적으로 catch하지 말 것

       → 구체적인 예외 타입을 catch하여 예상 가능한 오류만 처리하기

  2) 사용자가 예외 상황을 이해할 수 있도록 의미 있는 오류 메시지를 제공한다.

  3) 컬렉션 조작에 LINQ 쿼리와 메서드를 사용하여 코드 가독성을 높인다.

    - LINQ (Language Integrated Query)

       : C#에서 컬렉션, 배열, 리스트 등 데이터를 쿼리하는 도구

         → DB에서 SQL로 데이터를 조회/조작하듯, C#에서는 LINQ로 데이터를 조회/조작할 수 있음

  4) I/O 바인딩된 작업에 async 및 await를 사용한 비동기 프로그래밍을 사용한다.

    - 입출력 (I/O) 바운드 작업 

       :  CPU가 아닌 외부 시스템과의 통신에 의존하는 작업

          ex) 파일 시스템 접근, 네트워크 통신, 데이터베이스 쿼리 등

       → 전통적인 방식으로 처리하면 해당 작업이 완료될 때까지 애플리케이션이 멈춰있게 됨

  5) 교착 상태(Deadlock)에 주의하고, 적절한 경우 Task.ConfigureAwait를 사용한다.

    - Task.ConfigureAwait(false)

       : 비동기 작업이 완료된 후, 작업의 후속 처리(continuation)가 현재의 컨텍스트로 돌아가지 않도록 제어함

    - 주로 UI 스레드에서 비동기 작업을 할 때, 불필요한 교착 상태를 방지하거나 성능을 향상시키기 위해 사용함

       · 교착 상태

          : 비동기 작업을 수행할 때, await 이후 해당 스레드로 돌아오기를 기다리다가 교착 상태에 빠질 수 있음

          ex) UI 스레드에서 어떤 작업이 끝나길 기다리지만, 그 작업도 UI 스레드로 돌아가 후속 작업을 하려고 할 때

                양쪽이 서로 대기하는 상황이 발생할 수 있음

    - Task.ConfigureAwait(false)는 비동기 작업이 완료된 후 UI 스레드나 원래의 컨텍스트로 돌아가지 않고

       다른 스레드에서 후속 작업을 처리하도록 함

       · 이점 ① 교착 상태 방지

       · 이점 ② 성능 최적화

          : 후속 작업이 반드시 UI 스레드에서 실행될 필요가 없다면,

            Task.ConfigureAwait(false)을 통해 불필요하게 UI 스레드로 돌아가는 것을 방지함

            → UI 스레드 부하 Down / 애플리케이션 응답성 Up

       · 이점 ③ 컨텍스트 전환 비용 절약

  6) 데이터 형식에 런타임 형식 대신 언어 키워드를 사용한다.

    ex) System.String 대신 string / System.Int32 대신 int

  7) 부호 없는 정수형 대신 int를 사용한다.

    - unit 타입 (unsigned int)는 음수 값을 허용하지 않고 0과 양수만을 표현할 수 있음

  8) 개발자가 변수의 자료형을 명확히 유추할 수 있는 경우에만 var를 사용한다. (암시적 형식 지역 변수 선언)

    - C#은 기본적으로 변수를 선언할 때 앞에 변수의 자료형을 명시해주어야 함

      → 명시하지 않아도 개발자가 해당 변수의 자료형을 유추할 수 있는 경우에는 var 로 대체할 수 있음

    - 모든 상황에서 var를 쓰더라도 컴파일러는 변수의 자료형을 추론할 수 있기 때문에 컴파일 에러가 발생하지 않음

       → 그러나, 가독성을 위해 변수의 타입이 명확하지 않을 때에는 var 대신 자료형을 명시하는 것이 좋음

 


▶ 문자열 데이터에 대한 지침

  1) 문자열 보간법을 사용하여 짧은 문자열을 연결한다.

string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";

  2) 문자열을 다룰 때 많은 양의 텍스트를 반복적으로 추가할 때에는 System.Text.StringBuilder를 사용한다.

     - C#에서 string은 불변함 (Java와 동일)

        · 상수 풀 영역에 한 번 생성된 문자열은 변경되지 않음

        · 동일한 문자열을 사용할 경우, 메모리 공간을 새로 생성하지 않고 기존에 저장되어 있는 문자열을 재사용함

        · 문자열을 변경하려고 하면 새로운 문자열 객체가 생성됨

string str = "hello";
str = "world"; // 새로운 문자열 "world"를 가리킴, 이전 "hello"는 메모리에서 유지됨

     → 따라서, 문자열을 자주 변경해야 하는 경우 StringBuilder를 사용하는 것이 성능 면에서 효율적임

        · StringBuilder : 가변 객체

           - 문자열을 수정할 때 새로운 객체를 생성하지 않고, 내부 버퍼를 사용해 문자열을 변경함

           - 스레드 안전을 보장하지 않음

              : 여러 스레드가 동시에 같은 StringBuilder 객체를 수정할 경우 동기화를 보장하지 않음

                 → 다중 스레드 환경에서는 별도의 동기화 처리 필요 (lock 활용)

           - 병렬 처리나 다중 스레드를 사용하지 않는 상황에서는 빠른 성능을 제공하며, 효율적임

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

     ※ Java와의 차이점

        · Java는 스레드 안전을 보장하는 대신 처리 속도가 느린 StringBuffer와,

          스레드 안전을 보장하지 않지만 처리 속도가 빠른 StringBuilder가 존재함

 

 


▶ 배열에 대한 지침

  1) 아래와 같은 간결한 구문으로 배열을 초기화할 때에는 var를 사용할 수 없다.

string[] vowels1 = { "a", "e", "i", "o", "u" }; // var를 사용할 수 없음

  2) 아래와 같이 명시적 인스턴스화를 사용할 때에는 var를 사용할 수 있다.

var vowels2 = new string[] { "a", "e", "i", "o", "u" }; // var를 사용할 수 있음

 


▶ 대리자(Delegate)에 대한 지침

  - TODO : Delegate 형식에 대해 공부한 후 추가

 

 


▶ 예외 처리에 대한 지침

  1) 대부분의 예외 처리에서는 try-catch 문을 사용한다.

  2) using 문을 사용하여 코드를 간소화한다.

     - 일반적으로 리소스 (파일, DB 연결, 네트워크 소켓 등)는 사용 후에 반드시 해제해야 함

        → using 문을 사용하면 이러한 리소스들을 자동으로 정리할 수 있음

     - using

        : IDisposable 인터페이스를 구현하는 객체에 대해 자동으로 Dispose 메서드를 호출하여 리소스를 해제함

 

# using 문 없이 리소스를 관리하는 방식 (try-finally)

FileStream fileStream = null;
try
{
    fileStream = new FileStream("example.txt", FileMode.Open);
    // 파일 작업 수행
}
finally
{
    if (fileStream != null)
    {
        fileStream.Dispose(); // 리소스 해제
    }
}

#  using 문을 사용하여 리소스를 관리하는 방식

using (FileStream fileStream = new FileStream("example.txt", FileMode.Open))
{
    // 파일 작업 수행
} // 여기서 fileStream이 자동으로 Dispose

 


▶ new 연산자에 대한 지침

  1) 간결한 형식의 인스턴스화를 사용한다.

// 방식 1)
var firstExample = new ExampleClass();

// 방식 2)
ExampleClass instance2 = new();

  2) 개체 이니셜라이저(Object Initializer)를 활용하여 인스턴스화한다.

var thirdExample = new ExampleClass 
{ 
    Name = "Desktop", 
    ID = 37414,
    Location = "Redmond", 
    Age = 2.3 
};

 


▶ 이벤트 처리기에 대한 지침

  1) 나중에 제거할 필요가 없는 이벤트 처리기를 정의하려는 경우 람다 식을 사용하여 축약한다.

 

# 축약하지 않은 기존 정의

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());
        };
}

 


▶ LINQ 쿼리에 대한 지침

  1) 쿼리 변수에 의미 있는 이름을 사용한다.

  2) 별칭을 사용하여 '익명 형식'의 속성 이름 대/소문자를 올바르게 표시한다.

var localDistributors =
    from customer in customers
    join distributor in distributors on customer.City equals distributor.City
    select new { Customer = customer, Distributor = distributor }; // 익명 형식을 이용하여 객체로 반환

    - 익명 형식 : https://learn.microsoft.com/ko-kr/dotnet/csharp/fundamentals/types/anonymous-types

 

  3) 결과의 속성 이름이 모호하면 속성 이름을 바꾼다.

var localDistributors2 =
    from customer in customers
    join distributor in distributors on customer.City equals distributor.City
    select new { CustomerName = customer.Name, DistributorID = distributor.ID };

  4) where 절을 다른 쿼리 절 앞에 사용하여, 뒤에 있는 쿼리 절이 필터링된 데이터 집합에 대해 작동하게 한다.

var seattleCustomers2 = from customer in customers
                        where customer.City == "Seattle" // orderby보다 where을 앞에
                        orderby customer.Name
                        select customer;

  5) 하나의 join 절 대신 여러 개의 from 절을 사용함으로써 내부 컬렉션에 더 간단하게 접근할 수 있다.

var scoreQuery = from student in students
                 from score in student.Scores
                 where score > 90
                 select new { Last = student.LastName, score };

 


▶ 암시적 형식 지역 변수에 대한 지침

  1) 변수의 형식이 명확하면 지역 변수에 대해 암시적 형식(var)를 사용한다.

     a. new 연산자

Person person = new Person(); // 형식이 명확하므로 var 사용 가능
var person = new Person();

     b. 명시적 캐스트

var number = (int)3.14; // int로 명확하게 변환되므로 var 사용 가능

     c. 리터럴 값

var number = 10; // int 형식으로 명확함
var text = "Hello"; // string 형식으로 명확함

  2) 메서드/함수 호출의 반환 형식이 명확하지 않으면 var를 사용하지 않는다.

     - 메서드 이름을 통해 형식을 명확히 알 수 있다고 가정하지 말 것

var result = GetSomeData(); // GetSomeData()의 반환 형식이 불분명하면 var 사용하지 않는 것이 좋음
List<int> result = GetSomeData(); // 반환 형식이 명확함

  3) dynamic 대신 var를 사용하지 않는다.

     - dynamic

        · 런타임에 형식이 결정됨

           : 컴파일 시에는 형식 검사가 이뤄지지 않으며, 실제 형식은 프로그램이 실행될 때 결정됨

        · 컴파일 타임에 형식 검사 없이 다양한 형식의 값을 가질 수 있음

     - var

        · 컴파일 타임에 형식이 결정됨

           : 컴파일러는 할당된 값의 형식을 바탕으로 변수의 타입을 추론함

        · 한 번 결정된 형식은 변경할 수 없으며, 컴파일러는 강한 형식 검사를 수행함

     → 서로 다른 목적으로 사용되는 키워드이므로, 형식의 결정 시점에 따라 선택해서 사용해야 함

 

  4) for 루프의 루프 변수에 암시적 형식을 사용한다.

for (var i = 0; i < 10000; i++)
{
    // ...
}

  5) foreach 루프의 루프 변수에는 명시적 형식을 사용한다.

foreach (char ch in laugh)
{
    // ...
}

  6) LINQ 쿼리의 결과를 변수에 할당할 때는 암시적 형식을 사용한다.

var groupedStudents = from student in students
                      group student by student.Grade into gradeGroup
                      select gradeGroup;

 


▶ using 지시문에 대한 지침

  1) using 지시문은 네임스페이스 선언 외부에 배치한다.

using Azure;

namespace CoolStuff.AwesomeFeature
{
    public class Awesome
    {
        public void Stuff()
        {
            WaitUntil wait = WaitUntil.Completed;
            // ...
        }
    }
}

 

 

320x100
반응형
저작자표시 비영리 변경금지 (새창열림)

'C#' 카테고리의 다른 글

[C#/dotnet] 페이지 구조 - 마스터 페이지 & 콘텐츠 페이지  (1) 2024.10.24
[C#/dotnet] 페이지 수명 주기  (3) 2024.10.24
[C#/dotnet] 제네릭 (Generics)  (0) 2024.10.23
[C#/dotnet] LINQ (Language-Integrated Query)  (0) 2024.10.23
[C#/dotnet] 식별자 명명 규칙  (0) 2024.10.18
'C#' 카테고리의 다른 글
  • [C#/dotnet] 페이지 수명 주기
  • [C#/dotnet] 제네릭 (Generics)
  • [C#/dotnet] LINQ (Language-Integrated Query)
  • [C#/dotnet] 식별자 명명 규칙
스응
스응
    반응형
    250x250
  • 스응
    이서영의 개발 블로그
    스응
  • 전체
    오늘
    어제
  • 글쓰기 관리
    • 분류 전체보기 (385)
      • Java (134)
        • Base (54)
        • Spring Boot (37)
        • JSP (16)
        • Swing (GUI) (20)
        • Design Pattern (7)
      • C# (13)
      • PHP (18)
      • SQL (27)
      • Vue.js (9)
      • Tailwind CSS (4)
      • TypeScript (7)
      • HTML & CSS (27)
      • JavaScript (26)
      • jQuery (10)
      • Android (3)
      • - - - - - - - - - - - - - - (0)
      • Hotkeys (5)
      • CS (30)
      • IT Notes (13)
      • Error Notes (17)
      • Team Project (24)
        • Airlines Web Project (12)
        • University Web Project (6)
        • Strikers 1945 GUI Project (6)
      • My Project (18)
        • Library Web Project (8)
        • Pet Shopping Mall GUI Project (10)
      • etc. (0)
  • 블로그 메뉴

    • Home
    • Write
  • 링크

    • 깃허브
  • 공지사항

  • 인기 글

  • 태그

    SQL
    js
    C#
    HTML
    jsp
    티스토리챌린지
    SpringBoot
    Wordpress
    cs
    Swing
    vuejs
    http
    tailwindcss
    면접
    jQuery
    typeScript
    개발일지
    zapier
    git
    php
    SWAGGER
    CSS
    SEO
    errorNote
    java
    Hotkeys
    Android
    오블완
    Codeigniter
  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.0
스응
[C#/dotnet] 일반적인 C# 코드 규칙
상단으로

티스토리툴바