1. 지역변수를 선언 할때는 var가 더 낫다.
그렇다면 var를 무작정 사용하면 타입 선정하는 데 걸리는 시간이 늘어나 컴파일 타임에 악영향을 주는게 아닐까?
- 암시적으로 변수를 선언한 코드가 더 잘 읽힌다.
변수의 의미 자체에 더 집중할 수 있다. 하지만 변수의 타입 정보가 필요할 때, 정확하게 유추할 수 있도록 코드를 짜는 편이 좋다.
예를 들어, JSON object의 경우 타입 자체에 대한 정보가 아닌 그에 담긴 데이터가 중요한경우가 많으므로
var로 선언 해주는 것이 좋다. 또한, 내장 숫자 타입을 리턴하는 메서드의 경우 메서드 이름에 숫자 타입이 적혀있지 않다면 해당 메서드가 출력하는 리턴값의 타입이 어떤 것인지 확신할 수 없다. 따라서 내장 숫자 타입 같은 경우엔 var보다는 명시적으로 선언해주는 것이 좋다.
- 명시적으로 지정했을 때 성능적으로 악영향을 끼칠 가능성이 있다.
예를 들어, IQueryable은 IEnumerable을 상속 받고 있는데, IQueryable로 나올수 있는 변수 타입에 대해 IEnumerable로 캐스트하여 변수를 선언하고, Linq로 쿼리를 사용 했을때, 성능이 더 좋지가 않다. 이런 일이 발생 할 수 있기에 미연에 방지하고자, var로 사용해준다.
2. const보다 readonly가 더 낫다.
const는 컴파일 상수 readonly는 런타임 상수라고 생각하면 좋다. const는 컴파일 했을 때, 값이 상수 값이 결정 되고, readonly는 컴파일에 상수에 대해 참조가 되고, 런타임에 값이 지정이 되기 때문에, readonly는 더 다양한 타입을 가질수 있게 된다.
즉, 상수 타입의 폭이 넓은 장점과 호환성에서 유리하다는 장점. 즉, 유연성에서 readonly 상수가 const 상수에 비해 많이 유리하다.
하지만 성능이 매우 중요할 때는 const 상수를 써주면 좋고, 컴파일할 때 사용되는 상숫값을 정의할 때도 반드시 const를 사용해야 한다. 예로 들어, Attribute의 매개변수, switch/case 문의 Label, enum 정의 시에는 컴파일 시에 사용되므로 const를 통해서 초기화되어야 한다.
3. 캐스트보다는 is, as가 더 좋다
더 안전하고 성능면에서도 런타임에 더 효율적으로 동작한다. 가장 큰 차이는 사용자 정의 형변환을 다루느냐 다루지 않느냐하는 점. as는 지정한 타입이거나 혹은 지정한 타입을 상속한 타입이어야 한다. 그 외의 경우에는 전부 null을 반환한다.
캐스팅은 사용자 정의 형변환이 개입될 수 있다.
사용자 정의 형변환
public static implicit operator string(student s){
return s.Name;
}
try, catch가 필요없기 때문에 성능이 더 좋고, 반환 타입이 예상하기 쉽다.
as는 지정 타입이 예외 경우에는 null을 반환하는 데, 값타입인 경우에는 null을 가질수 없다.
그러니 어떤 객체에 대해 nullable로 바꾼후 null체크를 해주는게 바람직하다.
var i = o as int ?;
if(i == null){ ~~
4. string.format이 아닌 보간 문자열
$"~~"을 쓰자.
5. nameof잘쓰자.
서로 다른 플랫폼끼리 데이터 교화할때, string 식별자에 의존하는 라이브러리가 많은 데, 이럴경우 타입정보에 대한 손실이 올수 있는데, nameof()를 쓰면 방지 할수 있다.
6. 델리게이트로 콜백을 쓰자.
자주 사용하는 델리게이트
Predicate<T> : 조건을 검사하여 bool값을 반환하는 델리게이트
Func<> : 여러 개의 매개변수를 받아 특정 타입의 단일 결과값을 반환하는 델리게이트
(Func<T, bool> = Predicate<T>
Action<> : 여러 개의 매개변수를 받지만 결과값의 타입이 void인 델리게이트
콜백을 사용해야 하는 클라이언트를 더단순하게 구성할 수 있다.
런타임에 콜백 함수를 구성할 수 있다.
하나의 델리게이트에 여러 개의 콜백 함수를 추가할 수 있다.
.NET 환경에서 콜백이 필요한 경우에는 반드시 델리게이트 사용해야한다.
7. 이벤트 호출 시에는 null 조건 연산자(?.)를 사용하자
멀티쓰레드 환경에서 안전한 코드를 신경쓰기는 참 어렵다. 코드를 안전하게 만들기도 어렵고 멀티 쓰레드 관련 에러 상황을 구현하기는 더 어렵기 때문이다.
이벤트 구현에 있어서 null체크와 쓰레드 안전성을 고려해서 짜면, 코드 자체가 직관적으로 왜 이렇게 짜여졌는지 알기 어렵고, 모든 이벤트 핸들러에 위와 같은 코드를 작성해놓기엔 여간 복잡해 보이는게 아니다.
?.이면 해결
8. 박싱과 언박싱은 피하자.
박싱 : Value 타입의 객체를 타입이 정해져 있지 않은 임의의 참조 타입 내부에 포함시키는 방법
언박싱 : 박싱되어 있는 참조(reference) 타입의 객체로부터 Value 타입 객체의 복사본을 가져오는 방법
이런 박싱과 언박싱은 System.Object 타입이나 인터페이스 타입이 필요한 부분에 Value 타입의 객체를 적용하기 위해서 필요한 기능이지만 가능하면 쓰지 않는 것이 좋다.
9. 베이스 클래스가 업그레이드된 경우에만 new 한정자를 사용하라
클래스의 명명 범위(naming scope) 내에 새로운 메서드를 추가하는 역할을 수행한다. 즉, 상속하는 A라는 클래스 a메소드를 상속되는 B클래스에 new a라고 지정해준다.
왜 지양할까
new 한정자를 활용해도 좋은 경우는 베이스 클래스에서 이미 사용하고 있는 메서드를 재정의하여 완전히 새로운 베이스 클래스를 만들어야 하는 경우 정도 밖에 없다.
->상 메서드의 경우에는 클래스의 가능성을 제약한다기 보다는 클래스 활용 방법에 대한 가이드라인을 제시하는 느낌으로 사용하기 때문에 첫 개발자가 해당 메서드의 다형성을 고려하지 않고 가상 메서드로 작성하지 않았을 수 있다. 이럴 경우 new 한정자를 사용하는 것
베이스 클래스가 업그레이드되어 메서드의 이름이 충돌하는 경우는 매우 특별한 경우라 new 한정자를 고려해볼 수는 있으나 이 경우에도 신중하게 사용되어져야 하고 이 이외에는 절대로 new 한정자를 사용해서는 안 된다
'Programming > C#' 카테고리의 다른 글
C# - 부동 소수점 오차 (0) | 2023.08.20 |
---|---|
c++ -> c#인터프리터 정리 (0) | 2023.07.03 |
Effective C# - 제네릭의 활용) 타입 매겨변수가 IDispoable을 구현할 경우를 대비해 제네릭을 만든다. (0) | 2022.09.11 |
Effective C# - 제네릭의 활용) 런타임에 타입을 확인하여 최적의 알고리즘을 사용하자. (0) | 2022.09.11 |
Effective C#-제네릭 활용: 필요한 제약 조건만 설정 +) 제네릭? (2) | 2022.09.02 |