개발/C#

.Net Framework Guideline - Type Design(형식 디자인)-2

huiyu 2021. 8. 30. 19:51

인터페이스(Interface) 디자인

대부분 API는 클래스 및 구조체를 사용하여 모델링 하는 것이 좋지만, 인터페이스가 적합한 경우가 있다.
CLR은 다중 상속을 지원하지 않지만, 인터페이스를 사용하면 다중 상속의 효과를 얻을 수 있다.

- 멤버가 없는 인터페이스인 마커 인터페이스(marker Interfaces)는 사용하지 않는다.
  *마커 인터페이스 : https://www.csharpstudy.com/Story/Article/7
  만약 클래스에 특정 특성을 표시해야 하는 경우(Marker), 마커 인터페이스가 아닌 attribute를 이용해야 한다.

- 값 형식을 포함하는 형식에서 몇 가지 공용 API를 지원해야한다면 인터페이스를 정의한다.

- 다른 타입을 이미 상속받은 형식에서 기능을 지원해야할 경우 인터페이스를 정의하는 것이 좋다.

- 인터페이스를 구현한 형식(type)을 하나 이상 제공할 것. 인터페이스 디자인의 유효성을 검사하는데 도움이 된다.
 ->List<T>는 Ilist<T> 인터페이스를 구현

- 정의하는 각 인터페이스를 사용하는 하나 이상의 API를 제공할 것 (인터페이스를 매개 변수로 사용하는 매서드나 인터페이스를 이용한 프로퍼티 제공)
 ->List<T>.Sort는 System.Collections.Generic.Icomparer<T>인터페이스를 사용하여 구현

- 이전에 추가한 인터페이스에 멤버를 추가하지 말 것. 버전 관리 문제를 해결하기 위해선 새 인터페이스를 만들어야 한다.

 

구조체 디자인

일반적인 값 형식(general-purpose value type)은 struct로 사용된다.

- 구조체에 대해 매개 변수가 없는 생성자를 제공하지 말 것.
  배열의 각 항목에서 생성자를 실행하지 않고도 구조체의 배열을 만들 수 있는데 기본 매개 변수가 없는 생성자를 통해 생성된다.
  이미 생성이 되어있기 때문에 C#에서는 구조체에 매개 변수가 없는 생성자를 사용할 수 없다. * 컴파일러 오류 CS0568 | Microsoft Docs  -> 구조체는 이미 개체를 0으로 초기화하는 매개 변수가 없는 생성자가 이미 있다. 따라서 구조체에서 생성자를 만들 경우엔 매개 변수를 하나 이상 추가해야한다.

- 변경 가능한 값 형식을 정의하지 말 것.
 변경 가능한 값 형식(mutable value types) 에는 여러가지 문제가 있다. 속성 getter에서 값 형식을 반환하는 경우 호출자는 복사본을 받게 된다. 복사본은 암시적으로 만들어지기 때문에 개발자는 원래 값이 아닌 복사본을 변경한다는 사실을 인식할 수 없다. 또한 역참조되는 경우 로컬 변수도 복사본을 만들 수 있으므로 주의가 필요하다.

- 상황에 따라 모든 인스턴스 데이터가 0, false, 또는 null로 설정된 상태가 유효한지 확인한다. 구조체가 초기값에 따라 생성될때 실수로 잘못된 인스턴스가 만들어질 수도 있다.

- 값 형식에서 IEquable<T>를 구현한다. 값 형식의 Object.Equals 메서드로 인해 boxing이 발생하고 기본 구현은 리플렉션을 사용하기 떄문에 효율적이지 않다. Equals는 훨씬 더 향상된 성능을 제공하면 boxing이 발생하지 않도록 구현할 수 있다.

- ValueType을 명시적으로 확장해서 구현하지 않는다. 대부분 언어에서는 이러한 확장을 방지하고 있다.

*일반적으로 구조체는 매우 유용하지만 자주 Boxing되지 않고 변경 불가능한 단일 값에서만 사용해야한다.

열거형 디자인

특수한 형태의 값 형식이다. 열거형에는 단순 열거형과 플래그 열거형 두종류가 존재한다. 단순 열거형은 작은 닫힌 선택 집합(small closed sets of choices)을 나타낸다. 간단한 예제는 Color 집합이다. 플래그 열거형은 열거형 값에 대한 비트 연산을 지원하도록 디자인 되어져 있다. 플래그 열거의 일반적인 예는 옵션 목록이다.

- 파라미터, 프로퍼티, 리턴값을 특정한 값의 형태로 묶어서 관리할 수 있다.
- 정적 상수(static constants)보단 enum을 선호한다.
- 개방형 집합(open sets)에는 enum을 사용하지 않느다(예:OS version, 친구이름 등).. Open sets의 의미는 무엇이지?
- 나중에 사용하기 위한 예약된 enum 값은 제공하지 않는다, 나중에라도 enum에 값을 추가 할 수 있다. 열거형에 값 추가 관련한 내용은 아래 참고. 예약된 값은 더 혼란시킬 수 있다.


- 값이 하나만 있는 열거형은 공개적(public)으로 노출하지 않는다
 ->  내용은 이해가 잘 안감;

A common practice for ensuring future extensibility of C APIs is to add reserved parameters to method signatures. Such reserved parameters can be expressed as enums with a single default value. This should not be done in managed APIs. Method overloading allows adding parameters in future releases.

- 열거형(enum)에는 센티널 값(보초 값)을 포함하지 않는다.
 *센티널 값 : 특정 알고리즘의 문맥 안에서 특별한 의미를 지니는 값, 보통 반복문이나 재귀를 종료하는 조건으로 존재하는 값이다.

센티널 값은 프레임워크 개발자에겐 유용하지만 사용자에겐 혼동을 줄 수 있다. 이 경우 집합의 값 중 하나가 아니라 열거형의 상태를 추적하는데 사용된다(?)

- 단순 열거형에 0 값을 제공한다.
 'None' 과 같은 값을 호출하는 것이 좋다. 이러한 특정 열거형에 적절한 값이 없는 경우 열거형의 가장 일반적인 기본값은 0의 기본값을 할당해야 한다.

 다음에 해당하지 않는 경우엔 Int32를 열거형의 기본형식으로 사용한다.

 - 열거형이 플래그 열거형이며 32개 이상의 플래그를 포함하거나 더 많은 것이 필요해질 경우
 - The underlying type needs to be different than Int32 for easier interoperability with unmanaged code expecting different-size enums.

- smaller underlying type would result in substantial savings in space. If you expect the enum to be used mainly as an argument for flow of control, the size makes little difference. The size savings might be significant if:

  • You expect the enum to be used as a field in a very frequently instantiated structure or class.
  • You expect users to create large arrays or collections of the enum instances.
  • You expect a large number of instances of the enum to be serialized.

For in-memory usage, be aware that managed objects are always DWORD-aligned, so you effectively need multiple enums or other small structures in an instance to pack a smaller enum with in order to make a difference, because the total instance size is always going to be rounded up to a DWORD.

*모르겠음 영어부분

- flag enum의 이름은 복수 명사 또는 명사구를 사용.
 또는 단일 명사나 명사구를 사용하는 단순 열거형 플래그를 지정한다.

- System.Enum은 직접 확장하지 않는다.
 System.Enum은 CLR에서 사용자 정의 열거형을 만드는데 사용하는 특수한 형식이다. 대부분 프로그래밍 언어는 이 기능에 대한 엑세스를 제공하여, eunm 키워드를 사용하여 열거형을 정의하는데 사용할 수 있다.

 

플래그 열거형 디자인

- 'System.FlagsAttribute'를 플래그 열거형(flag enums)에 적용한다.  단순 열거형에는 이 특성을 적용하지 않는다.

- 플래그 열거형 값은 2의 거듭제곱을 사용하므로 'OR'연산자를 사용하여 자유롭게 결합할 수 있다.

- 일반적으로 사용되는 플래그 조합에 특수한 열거형 값을 제공하는 것이 좋다.
  비트연산은 고급개념이므로 간단한 작업에는 필요하지 않다.
  여기서 특수한 열거형 값은 FileAccess enum 값에서 ReadWrtie를 의미한다. Read : 1, Wrtie : 2를 제공하면서 ReadWrite:3역시 같이 제공한다!
  https://docs.microsoft.com/ko-kr/dotnet/api/system.io.fileaccess?view=net-5.0#System_IO_FileAccess_ReadWrite 

 

FileAccess 열거형 (System.IO)

파일에 대한 읽기, 쓰기 또는 읽기/쓰기 액세스 권한에 대한 상수를 정의합니다.Defines constants for read, write, or read/write access to a file.

docs.microsoft.com

- 값이 특정 조합이 유효하지 않는 다면 플래그 열거형을 만들지 않는다.

- 아래 가이드에서 지침한대로 모든 플래그를 지우는 경우를 제외하곤 열거형 값을 0으로 하지 않는다.

- 'None' flag enum의 값은 0으로 한다. 이 경우 모든 플래그가 지워졌다를 의미해야 한다.

열거형에 값 추가

제공되고 있는 기존 열거형에 값을 추가하는 것은 일반적인 경우이다. 잘못 작성한 응용 프로그램은 새 값을 올바르게 처리하지 못할 수 있으므로 새로 추가 된 값이 기존 API에서 반환될 때 호환성 문제가 발생할 수 있다.

- 호환성의 위험이 있더라도 열거형에 값을 추가하는 것이 좋다.

  열거형 추가로 호환성 문제가 발생할 경우 새값과 이전값을 모두 반환하는 새 API를 추가하고, 이전 값만 반환하는 API는 사용중단하는게 좋다. 이렇게 하면 기존 응용 프로그램이 호환되는 상태로 유지된다.

 

중첩 형식(Nested Types) - 확인필요

 중첩 형식은 다른 형식의 범위 내에 정의된 형식을 말한다.  안쪽에 정의된 형식을 중첩형식, 바깥에 정의된 형식을 바깥쪽 형식(enclosing type)이라고 한다. 중첩 형식은 바깥쪽 형식의 모든 멤버에 액세스할 수 있다. 예를들어 바깥쪽 형식에 정의된 프라이빗 필드와 바깥쪽 형식의 모든 상위 항목에 정의된 protected 필드에 접근할 수 있다.

 일반적으로 중첩 형식은 필요한 경우에만 사용해야 한다. 중첩 형식은 바깥쪽 형식과 매우 긴밀하게 결합되어 있으므로 범용형식에는 적합하지 않다.

 중첩 형식은 바깥쪽 형식의 구현 세부 정보를 모델링하는데 가장 적합하다. 최종 사용자는 중첩 형식의 변수를 거의 선언할 필요가 없으며 중첩 형식을 명시적으로 인스턴스화할 필요도 없다. 예를들어 컬렉션의 열거자는 해당 컬렉션의 중첩형식일 수 있다. 일반적으로 열거자는 바깥쪽 형식에 의해 인스턴스화되며, 대부분 언어에서 foreach문을 지원하기 때문에 최종 사용자가 열거형 변수를 선언해야 하는 경우는 없다.

- 중첩 형식과 외부 형식 간 관계가 멤버 접근성 의미에서 바람직한 경우 중첩 형식을 사용한다.(???)

- public 중첩 형식을 논리적 그룹화 생성자로 사용하지 않는다. 대신 네임스페이스를 사용한다.(???)

- 공개적으로 노출된 중첩 형식을 사용하지 않는다. 단, 중첩 형식의 변수를 서브클래싱 또는 다른 고급 사용자 지정 시나리오 같은 드문 시나리오에서 선언하는 경우는 예외이다(???)

- 형식이 포함하는 형식 외부에서 참조될 가능성이 있는 경우 중첩형식을 사용하지 않는다. 예를 들어 클래스에 정의된 메서드에 전달된 열거형은 클래스에서 중첩 형식으로 정의할 수 없다.

- 클라이언트 코드에서 인스턴스화해야 하는 경우 중첩 형식을 사용하지 않는다. 형식에 퍼블릭 생성자가 있는 경우 중첩할 수 없다. 내부의 형식은 외부 형식에 대한 관계가 없이 외부에서 광범위하게 사용될 수 없다.

- 중첩 형식을 인터페이스의 멤버로 정의하지 않는다. 대부분 이러한 구문은 지원하지 않는다.

 

728x90
반응형