- 나이 : 96년생
- 특이사항 : 조금씩 늙어가고 있음
- 좋아하는 음식 : 햄부기, 치킨, 고기
🥷기술
🐱 우리집 고양이 소개


- 이름 : 콜라
- 나이 : 14년생
- 종 : Nado moreum
📱 개인 프로젝트
🏢 참여한 프로젝트
🌱 내 잔디밭
Effective C# 요약 본문
1. 지역변수를 선언할 때는 var 를 사용하는 것이 낫습니다.
- 변수의 이름을 지을 때 조금 더 의미가 강조되기 때문에 가독성이 좋아집니다.
- 참고로 Effective C++ 에서 auto 키워드 사용에 대한 이유와 일치합니다.
- 구문의 반환 타입을 정확히 알지 못할 때 명시적으로 타입을 지정하는 경우 예상치 못하게 강제 형 변환이 일어날 수 있습니다.
2. const보다 readonly가 좋습니다.
- const 는 숫자, enum, 문자열, null 과 같은 내장 타입에 대해서만 사용될 수 있지만 readonly는 어떤 타입이든 사용할 수 있습니다.
- readonly의 위와 같은 유연성은 컴파일 타임 상수인 const 의 성능적인 장점보다 훨씬 두드러집니다.
하지만 컴파일할 때 참조되는 상숫값을 정의 하는 목적일 때는 불가피하게 반드시 const를 사용해야 합니다. (예를들면 switch/case 문의 레이블이나 enum 정의할 때 사용하는 상수)
3. 상속 관계에서 명시적 형변환 보다는 is, as가 좋습니다.
- as 나 is 는 런타임에 예외적인 상황이 아니라면 대부분 타입을 검증하는 것을 제외하고는 어떠한 작업도 하지 않기 때문에 캐스트보다 빠릅니다.
4. string.Format() 대신 보간 문자열($)을 사용하세요.
- '보간 문자열' 이란 $ 로 시작하는 리터럴 문자열. ex) $"저는 {name} 입니다."
5. 국가별로 다른 문자열을 생성하려면 FormattableString가 좋습니다.
- https://learn.microsoft.com/ko-kr/dotnet/api/system.formattablestring?view=net-7.0
6. nameof() 연산자를 적극 활용합시다.
- 메소드나 클래스, 프로퍼티 등의 이름을 리터럴 문자열로 적기보다는 nameof() 연산자를 사용하면, IDE 에서 이름 변경과 같은 작업을 진행할때 참조하고 있는 코드들을 전부 일괄 변경할 수 있습니다.
- 유니티에서 StartCoroutine 과 같은 메소드의 매개변수로 nameof() 를 사용하는 경우 코드 에디터에서 참조를 표기해주기 때문에 편리합니다.
7. 델리게이트(delegate)를 이용하여 콜백을 표현하세요.
- 애초에 델리게이트 말고 콜백을 표현할 방법이라고 해봐야 딱히 생각나는게 없습니다.
8. 이벤트 호출 시에는 null 조건 연산자를 사용하세요.
- 이벤트 호출시 SomeEvent?.Invoke(); 이렇게 호출하면 간결하고, 멀티 스레드에서도 안전합니다.
9. 박싱과 언박을 최소화 하세요.
- 박싱과 언박싱에 대해 이해하세요.
- 컬렉션은 (.NET 2.0 이상) 제네릭 컬렉션을 사용하세요.
10. 베이스 클래스 업그레이드된 경우에만 new 한정자를 사용하세요.
- new 한정자를 활용해도 좋은 경우는 베이스 클래스에서 이미 사용되고 있는 메소드를 재정의하여 '완전히' 새로운 베이스 클래스를 만들어야 하는 상황 정도입니다.
- 어지간하면 new 한정자를 사용하지 않는 것이 좋습니다. 프로그래머가 호출부에서 동작을 예상하기 어려워지기 때문입니다.
11. .NET 리소스 관리에 대한 이해
- 가비지 수집기가 효율적인 알고리즘(Mark/Compact)으로 더이상 사용되지 않는 객체를 자동으로 정리합니다.
- 하지만, 비관리 리소스 (데이터 베이스 연결, 시스템, COM 객체) 등은 개발자가 직접 관리해야 합니다.
- 비관리 리소스 생명주기를 관리할 때 finalizer와 IDisposable 인터페이스 두 가지 메커니즘을 사용할 수 있지만 IDisposable 인터페이스를 통해 구현하는것이 좋습니다.
- finalizer 는 가비지 수집기의 알고리즘에 의존하여 호출되기 때문에 어느 시점에 호출될지 아무도 알 수 없기 때문입니다. 심지어, 가비지 수집기로 부터 정리되는 시기를 늦추기 때문에 성능에도 영향을 끼칩니다.
12. 생성자 할당 구문보다 생성과 동시에 초기화 하는 형태의 멤버 초기화 구문이 좋습니다
- 생성자에서 할당 구문으로 값을 초기화 하지 말고, 멤버 초기화 구문을 이용해서 선언과 동시에 초기화를 정의하세요. 자쳇 초기화 코드를 누락할 수 있기 때문입니다.
- 멤버 초기화 구문을 이용할 때 다음 경우를 주의하세요.
- 기본 값이 0이나 null 인 경우 이미 메모리 블럭을 0으로 설정하니 초기화 구문을 넣을 필요가 없습니다. 굳이 추가적인 코드를 생성해서 컴파일러에게 괜한 일을 시킬 필요가 없죠.
- 동일 객체를 반복해서 초기화 하지 마세요. 예를들어 선언과 동시에 객체를 초기화 하고, 생성자에서 또 다시 초기화 구문을 넣는 상황을 피하십시오.
- 초기화 과정에서 예외를 처리해야하는 경우, try로 감쌀 수 없기 때문에 이 경우에는 생성자 내부에서 예외 처리 코드를 적절히 구현하면 됩니다.
13. 정적 클래스 멤버를 올바르게 초기화하세요.
static 변수를 초기화할 때 간단한 초기화라면 멤버 초기화 구문도 괜찮지만 복잡한 초기화 구문이라면 static 생성자를 사용해보는 것도 좋습니다. 싱글톤(Singleton) 패턴을 구현하는 예입니다.
public class MySingleton
{
private static readonly MySingleton theOneAndOnly;
// static 생성자는 private 혹은 다른 관용구를 사용해서는 안됩니다.
static MySingleton()
{
theOneAndOnly = new MySingleton();
}
public static MySingleton TheOnly => theOneAndOnly;
private MySingleton()
{
}
}
14. 초기화 코드가 중복되는 것을 최소화하세요.
여러 생성자 내에서 동일한 코드를 반복적으로 사용해야 하는 경우에는 private 헬퍼 메소드를 만들어서 중복되는 코드를 줄이는 것 보다는, 공용으로 사용할 수 있는 생성자를 작성하세요.
15. 불필요한 객체를 만들지 마세요.
- 자주 사용(호출)되는 지역변수를 멤버 변수로 변경하세요. 예를 들면 매 프레임 호출되는 업데이트나 드로우 이벤트에서 객체를 생성하는 코드가 있을 때, 멤버 변수로 변경할 수 있다면 변경하세요.
- 변경 불가능한 불변(immutable) 타입을 가공할 때, 가비지가 생성되는 것에 주의하세요. 대표적으로 String 객체도 불변 타입(객체 내의 문자열을 수정할 수 없음)임을 인지하면 좋습니다.
물론 String 의 문자열을 프로그래밍 하면서 계속 수정해왔던 경험 때문에 문자열을 수정할 수 있다고 착각할 수 있습니다.
public static void Main(string[] args)
{
string msg = "Hello, ";
msg += userName;
msg += ". Today is";
msg += System.DateTime.Now.ToString();
}
하지만 위와 같은 코드는 실제로는 다음과 같이 비효율적으로 작동합니다.
public static void Main(string[] args)
{
string msg = "Hello, ";
string tmp1 = new String(msg + userName);
msg = tmp1;
string tmp2 = new String(msg + ". Today is");
msg = tmp2;
string tmp3 = new String(msg + System.DateTime.Now.ToString());
msg = tmp3;
}
그래서 만약 많은 문자열을 좀 더 복잡하게 다뤄야 하는 상황이라면 StringBuilder 클래스를 사용하면 좋습니다.
StringBuilder msg = new StringBuilder("Hello, ");
msg.Append(yourName);
msg.Append(". Today is ");
msg.Append(DateTime.Now.ToString());
string finalMsg = msg.ToString();
이러면 StringBuilder 클래스는 마지막에 ToString() 할때 한번만 문자열 객체를 생성합니다.
하지만 추가적으로 알아두면 좋을게 최근 컴파일러에서는 리터럴 문자열끼리 + 연산자로 문자열을 묶는 경우 알아서 내부적으로 최적화를 해줍니다. (+= 는 아님) 자세한 내용은 https://overworks.github.io/unity/2018/08/30/finding-best-string-concatenation.html 를 참고해주세요.
16. 생성자 내에서는 절대로 가상 함수를 호출하지 마세요.
객체가 완전히 생성되기 전에 가상 함수를 호출하면 정상 동작하지 않을 수 있습니다.
17. 표준 Dispose 패턴을 구현하세요.
반드시 비관리 리소스 변수를 포함하는 경우 finalizer를 구현해야 합니다. 표준 Dispose 패턴은 다음과 같이 구현할 수 있습니다.
먼저, 최상위 클래스에서 다음 내용을 구현합니다.
- IDisposable 인터페이스를 구현합니다.
- 비관리 리소스 변수가 있다면 혹시 모르니 finalizer를 추가해서 방어적으로 동작하도록 합니다.
- Dispose와 finalizer의 실제 리소스 정리 작업을 수행하는 기능은 다른 가상 메소드에 위엄하여 동작하도록 작성합니다. (이래야 상속받은 클래스에서 이 가상 메소드를 재정의하여 고유의 리소스 정리 작업을 할 때 편합니다.)
상속받은 클래스는 다음 작업을 수행합니다.
- 상위 클래스에 구현한 리소스 정리 작업을 수행하는 가상 함수를 오버라이드해서 재정의합니다. (이 때 상위 클래스의 가상 함수를 재호출 해야겠죠?)
- 마찬가지로, 비관리 리소스 변수가 있다면 finalizer를 추가합니다.
또한, IDisposable.Dispose() 메소드에서는 다음 작업을 반드시 수행해야 합니다.
- 모든 비관리/관리 리소스를 정리합니다.
- 객체가 이미 정리되었음을 나타내기 위한 상태 플래그를 설정하고, 이미 정리되었을 경우 플래그를 체크해서 ObjectDisposed 예외를 발생시킵니다. (중복 정리를 막기 위해서)
- Dispose 호출시 finalizer가 별도로 호출되지 않게 하기 위해 GC.SuppressFinalize(this) 를 호출합니다.
- Dispose 메소드는 한번 이상 호출될 수 있으므로 여러 번 호출하더라도 동일하게 동작할 수 있도록 구현합니다.
18. 제네릭 활용 : 반드시 필요한 제약 조건만 설정하세요.
(작성중)
'글 묶음 > 내 밥줄 Unity, C#' 카테고리의 다른 글
Unity Jenkins를 이용한 빌드 자동화 구축 과정 정리 (0) | 2025.02.16 |
---|---|
횡스크롤 뷰 2D 게임 강 셰이더 만들기 (2) | 2023.12.17 |
싱글톤 줄이기 (1) | 2023.09.27 |
API Level 31 이상 앱 업로드시 android:export 이슈 (17) | 2022.08.13 |
유니티 UGUI 기초 정리 (4) | 2020.06.29 |