개발/C#

.Net Framework Guideline - Member Design -2

huiyu 2021. 8. 30. 17:44

필드 디자인

캡슐화는 객체 지향 디자인에서 가장 중요한 개념이다. 이 원칙에 따르면 객체 내에 저장된 데이터에는 해당 객체에서만 접근할 수 있어야 한다. 이 해석은 모든 필드가 프라이빗이야 함을 의미한다.

상수 및 정적 읽기 전용 필드(static read-only fieled)와 같은 필드는 정의에 따라 변경할 필요가 없기 때문에 이 제한에서 제외한다.

- public/protected 인스턴스 필드를 제공하지 않는다.
 대신 필드에 접근하기 위한 속성(Property)을 제공해야 한다.
- 변경되지 않는 상수에는 상수 필드를 사용한다. 컴파일러는 'const'필드의 값을 호출 코드에 직접 포함한다. 따라서 호환성을 손상하지 않고 const 값을 변경할 수 없다.

- 미리 정의된 객체 인스턴스에 public static 'readonly' 필드를 사용한다. 미리 정의된 인스턴스가 있는 경우 형식 자체의 public read-only static 필드로 선언한다.

- 변경 가능한 형식의 인스턴스를 'readonly'필드에 할당하지 않는다.
 변경 가능한 형식은 인스턴스화된 후 수정할 수 있는 인스턴스가 있는 형식 입니다. 예를들어 배열, 컬렉션, 스트림은 변경 가능한 형식이지만 System.Int32, System.Uri, System.String은 모두 변경할 수 없다. 참조 형식 필드의 readonly 한정자는 필드에 저장된 인스턴스를 변경할 수는 없지만 인스턴스를 변경하는 멤버를 호출하여 필드의 인스턴스 데이터는 수정할 수 있다.

 

확장 메서드

C# 3.0부터 지원하는 특수한 종류의 static 메서드, 마치 클래스의 인스턴스 메서드인 것 처럼 사용되는 기능을 제공한다. 확장메서드는 클래스, 구조체, 인터페이스 등에 적용할 수 있다.
 -> 확장 메서드 : https://www.csharpstudy.com/CSharp/CSharp-extension-method.aspx

 

C# 확장메서드 1 - C# 프로그래밍 배우기 (Learn C# Programming)

C# 확장 메서드 (Extension Method) C# 3.0부터 지원하는 확장메서드(Extension Method)는 특수한 종류의 static 메서드인데, 확장메서드는 마치 다른 클래스(혹은 구조체)의 인스턴스 메서드인 것처럼 사용되

www.csharpstudy.com

 

이러한 확장메서드를 정의하는 클래스를 스폰서(sponsor) 클래스라고 하며, 정적(static)으로 선언해야 한다. 확장 메서드를 사용하기 위해서는 스폰서 클래스를 정의하는 네임스페이스를 가져와야 한다.

- 확장 메서드는 신중하게 정의한다. 특히 소유하지 않는 형식의 확장 메서드의 사용은 조심한다.
 소스코드를 소유하는 경우 일반적으로 인스턴스 메서드를 사용한다. 소유하지 않는 경우라면 메서드 추가를 주의한다. 확장 메서드를 자유롭게 사용하면 이러한 기존 사용에 설계되지 않은 형식의 API로 사용이 복잡해질 수 있다.

다음과 같은 시나리오에는 확장 메서드를 사용하는 것이 좋다.

 - 인터페이스 구현과 관련된 helper 기능을 제공하려는 경우 핵심 인터페이스 측면에서 기능을 작성할 수 있다. 인터페이스에는 구체적인 구현을 할당할 방법이 없기 때문인다. 예를들어 LINQ to Objects 연산자는 모든 IEnumeratble<T>형식에 대해 확장 메서드로 구현한다. 따라서 IEnumeratble<>구현은 자동으로 LINQ를 지원하게 된다.

- 인스턴스 메서드 일부 형식에 대해 종속성을 도입하는데 이러한 종속성이 종속성 관리 규칙을 위한 하는 경우, 예를들어 String이 System.Uri에 대해 종속성을 갖는 건 바람직하지 않을 수 있어 System.Uri를 반환하는 String.ToUri() 인스턴스 메서드는 종속성 관리 관점에서 잘못된 디자인이 된다.
 System.Uri를 반환하는 정적 확장 메서드 Uri.ToUri(this string str)를 제공하는 것이 훨씬 더 나은 디자인이다.

- System.Object에서 확장 메서드는 정의하지 않는다.(???)

- 인터페이스에서 메서드를 추가하거나 종속성 관리를 위한 경우가 아니라면 확장 형식과 동일한 네임스페이스에 확장 메서드를 넣지 않는다.

- 동일한 선언으로 둘 이상의 확장메서드를 정의하지 않는다. (서로 다른 네임스페이스에 있는 경우에도)

- 형식이 인터페이스인 경우 확장 메서드를 대부분 경우나 확장 메서드를 대부분 경우 사용한다면 동일한 네임스페이스에 확장메서드를 정의한다.

- 일반적으로 다른 기능과 연결된 네임스페이스에는 기능을 구현하는 확장 메서드를 정의하지 않는다. 확장 메서드가 속한 기능과 연결된 네임스페이스에 정의한다.

- 확장 메서드 전용 네임스페이스의 이름(Extensions)은 사용하지 않는다. 대신 설명이 포함된 이름을 사용한다.

 

연산자 오버로드

- 기본 제공 형식처럼 보여야 하는 경우를 제외하고는 연산자 오버로드를 정의하지 않는다.

- 기본 형식처럼 보여야 하는 형식에만 연산자 오버로드를 정의하는 것이 좋다.
 예를 들어 System.String에는 'operator==' 및 'operator!='가 정의되어 있다.

- 숫자를 나타내는 구조체에는 연산자 오버로드를 정의한다.(예 : System.Decimal)

- 피연산자 중 하나 이상이 오버로드를 정의하는 형식이 아닐 경우 연산자 오버로드를 제공하지 않는다.

- 대칭 방식으로 연산자를 오버로드 한다.
 -> 'operator=='를 오버로드 하는 경우 'operator!='도 오버로드 해야한다. 마찬가지로 'operator<'를 오버로드 한다면 'operator>'도 오버로드 해야한다.

- 오버로드된 각 연산자에 해당하는 식별 이름과 함께 메서드를 제공하는 것이 좋다.
 많은 언어에서 연산자 오버로드를 지원하지 않는다. 따라서 연산자를 오버로드 하는 형식에는 동일한 기능을 제공하는 적절한 보조 메서드를 포함하는 것이 좋다.

다음 표에는 연산자 목록과 해당하는 메서드 식별 이름이 나와 있다.

 

C# 연산자 기호 메타데이터 이름 친숙한 이름
N/A op_Implicit To<TypeName>/From<TypeName>
N/A op_Explicit To<TypeName>/From<TypeName>
+ (binary) op_Addition Add
- (binary) op_Subtraction Subtract
* (binary) op_Multiply Multiply
/ op_Division Divide
% op_Modulus Mod or Remainder
^ op_ExclusiveOr Xor
& (binary) op_BitwiseAnd BitwiseAnd
| op_BitwiseOr BitwiseOr
&& op_LogicalAnd And
|| op_LogicalOr Or
= op_Assign Assign
<< op_LeftShift LeftShift
>> op_RightShift RightShift
N/A op_SignedRightShift SignedRightShift
N/A op_UnsignedRightShift UnsignedRightShift
== op_Equality Equals
!= op_Inequality Equals
> op_GreaterThan CompareTo
< op_LessThan CompareTo
>= op_GreaterThanOrEqual CompareTo
<= op_LessThanOrEqual CompareTo
*= op_MultiplicationAssignment Multiply
-= op_SubtractionAssignment Subtract
^= op_ExclusiveOrAssignment Xor
<<= op_LeftShiftAssignment LeftShift
%= op_ModulusAssignment Mod
+= op_AdditionAssignment Add
&= op_BitwiseAndAssignment BitwiseAnd
|= op_BitwiseOrAssignment BitwiseOr
, op_Comma Comma
/= op_DivisionAssignment Divide
-- op_Decrement Decrement
++ op_Increment Increment
- (unary) op_UnaryNegation Negate
+ (unary) op_UnaryPlus Plus
~ op_OnesComplement OnesComplement

Operator == 오버로드

 'operator ==' 오버로드는 매우 복잡한 구현이다. 연산자의 구현은 'Object.Equals'와 같은 여러 멤버와 호환되도록 구현되어져야 한다.

변환 연산자(Conversion Operators)

변환 연산자는 형식 간 변환을 허용하는 단항 연산자이다. 연산자는 피연산자 또는 반환형식에서 정적(static)멤버로 선언되어야 한다. 변환 연산자에는 암시적(implicit)과 명시적(explicit) 변환 두가지 형식이 존재한다.

- 최종 사용자가 변환은 명확하게 예상하지 못한다면 변환 연산자를 제공하지 않는다.

- 형식의 도메인 외부에서 변환 연산자를 정의하지 않는다.
 예를들어 Int32, Double, Decimal은 모두 숫자 형식이지만 DateTime은 아니다. 따라서 Double(long)을 DateTime으로 변환하는 변환 연산자는 없어야 한다. 이러한 경우는 생성자를 사용하는게 좋다.

- 변환이 잠재적으로 손실될 수 있는 경우라면 암시적 변환은 제공하지 않는다. 예를 들어 Double은 Int32보다 범위가 더 넓기 때문에 암시적 변환이 없어야 한다. 그러나 잠재적으로 손실될 수 있는 경우 명시적 변환 연산자는 제공할 수 있다.

- 암시적 변환에서 예외를 throw하지 않는다. 사용자는 변환이 수행되고 있다는 사실을 알 수 없기 때문에 이해하기 어려운 상황이 발생할 수 있다.

- 캐스트 연산자 호출로 변환이 손실되고 손실된 허용을 하지 않는다면 'System.InvalidCastException'을 throw한다.

 

728x90
반응형