TL; DR 맨 아래로 스크롤하십시오.
내가 본 것에서 C # 위에 새로운 언어를 구현하고 있습니다. 열거 형은 식별자의 유형 (또는 이름이 있고 새로운 언어의 소스 코드에 나타나는 것)을 나타내는 것으로 보이며, 이는 프로그램의 트리 표현에 추가 될 노드에 적용되는 것으로 보입니다.
이 특정 상황에서는 서로 다른 유형의 노드 사이에 다형성 동작이 거의 없습니다. 나무가 매우 다른 종류의 (변형)의 노드를 포함 할 수 있어야하는 것이 필요하다 동안 즉,이 노드의 실제 방문은 기본적으로 거대한 경우 - 당시 다른 체인 (또는에 의존합니다 instanceof
/ is
확인). 이 거대한 수표는 프로젝트의 여러 곳에서 일어날 것입니다. 이것이 열거 형이 도움이 될 수있는 이유이거나 적어도 확인 instanceof
/ is
확인 만큼 도움이되는 이유 입니다.
방문자 패턴 이 여전히 유용 할 수 있습니다. 즉,의 거대한 체인 대신 사용할 수있는 다양한 코딩 스타일이 있습니다 instanceof
. 그러나 다양한 이점과 단점에 대한 토론을 원한다면 instanceof
열거 형에 대해 혼란스러워하는 대신 프로젝트에서 가장 추악한 체인의 코드 예제를 보여 주려고했을 것입니다.
이것은 클래스와 상속 계층이 유용하지 않다는 것은 아닙니다. 정반대. 모든 선언 유형에 걸쳐 작동하는 다형성 동작은 없지만 (모든 선언에는 Name
속성이 있어야한다는 사실은 제외 ) 인근 형제들이 공유하는 풍부한 다형성 동작이 많이 있습니다. 예를 들어, Function
그리고 Procedure
아마도 몇 가지 행동을 (모두 호출되는 및 입력 된 입력 인수 목록을 받아들이) 공유하고 PropertyGet
의지에서 확실히 상속 행동 Function
(모두가를 가진 ReturnType
). 거대한 if-then-else 체인에 대해 열거 형 또는 상속 검사를 사용할 수 있지만 조각화되었지만 다형성 동작은 여전히 클래스에서 구현되어야합니다.
남용 instanceof
/ is
확인 에 대한 많은 온라인 조언이 있습니다 . 성능은 그 이유 중 하나가 아닙니다. 오히려, 그 이유는 유기 것처럼, 적절한 행동 다형성을 발견으로부터 프로그래머를 방지하기 위함이다 instanceof
/ is
버팀목이다. 그러나이 노드는 공통점이 거의 없으므로 상황에 따라 다른 선택이 없습니다.
다음은 구체적인 제안입니다.
비 리프 그룹화를 나타내는 몇 가지 방법이 있습니다.
원래 코드의 다음 발췌 부분을 비교하십시오 ...
[Flags]
public enum DeclarationType
{
Member = 1 << 7,
Procedure = 1 << 8 | Member,
Function = 1 << 9 | Member,
Property = 1 << 10 | Member,
PropertyGet = 1 << 11 | Property | Function,
PropertyLet = 1 << 12 | Property | Procedure,
PropertySet = 1 << 13 | Property | Procedure,
LibraryFunction = 1 << 23 | Function,
LibraryProcedure = 1 << 24 | Procedure,
}
이 수정 된 버전으로 :
[Flags]
public enum DeclarationType
{
Nothing = 0, // to facilitate bit testing
// Let's assume Member is not a concrete thing,
// which means it doesn't need its own bit
/* Member = 1 << 7, */
// Procedure and Function are concrete things; meanwhile
// they can still have sub-types.
Procedure = 1 << 8,
Function = 1 << 9,
Property = 1 << 10,
PropertyGet = 1 << 11,
PropertyLet = 1 << 12,
PropertySet = 1 << 13,
LibraryFunction = 1 << 23,
LibraryProcedure = 1 << 24,
// new
Procedures = Procedure | PropertyLet | PropertySet | LibraryProcedure,
Functions = Function | PropertyGet | LibraryFunction,
Properties = PropertyGet | PropertyLet | PropertySet,
Members = Procedures | Functions | Properties,
LibraryMembers = LibraryFunction | LibraryProcedure
}
이 수정 된 버전은 비 콘크리트 선언 유형에 비트를 할당하지 않습니다. 대신, 비 콘크리트 선언 유형 (선언 유형의 추상 그룹)은 단순히 모든 자식에 대해 비트 단위 또는 비트 단위의 열거 형 값을 갖습니다.
하나의 자식이있는 추상 선언 유형이 있고 추상적 인 것 (부모)과 구체적인 것 (자식)을 구별 할 필요가있는 경우, 추상적 인 것에는 여전히 고유 한 비트가 필요합니다 .
이 질문에 대한 한 가지주의 사항 : a Property
는 처음에는 식별자입니다 (코드에서 어떻게 사용되는지 보지 않고 이름 만 보았을 때), 어떻게 사용되는지 보자 마자 PropertyGet
/ PropertyLet
/ PropertySet
로 변환 될 수 있습니다 코드에서. 즉, 구문 분석의 여러 단계에서 Property
식별자를 "이 이름은 속성을 나타냅니다"로 표시 한 다음 나중에 "이 코드 행은 특정 방식으로이 속성에 액세스하고 있습니다"로 변경해야 할 수 있습니다.
이 경고를 해결하려면 두 세트의 열거 형이 필요할 수 있습니다. 하나의 열거 형은 이름 (식별자)이 무엇인지를 나타냅니다. 또 다른 열거 형은 코드가 수행하려는 작업을 나타냅니다 (예 : 무언가의 본문 선언, 특정 방식으로 무언가 사용하려고 함).
대신 각 열거 형 값에 대한 보조 정보를 배열에서 읽을 수 있는지 고려하십시오.
이 제안은 2의 거듭 제곱 값을 작은 음이 아닌 정수 값으로 다시 변환해야하기 때문에 다른 제안과 상호 배타적입니다.
public enum DeclarationType
{
Procedure = 8,
Function = 9,
Property = 10,
PropertyGet = 11,
PropertyLet = 12,
PropertySet = 13,
LibraryFunction = 23,
LibraryProcedure = 24,
}
static readonly bool[] DeclarationTypeIsMember = new bool[32]
{
?, ?, ?, ?, ?, ?, ?, ?, // bit[0] ... bit[7]
true, true, true, true, true, true, ?, ?, // bit[8] ... bit[15]
?, ?, ?, ?, ?, ?, ?, true, // bit[16] ... bit[23]
true, ... // bit[24] ...
}
static bool IsMember(DeclarationType dt)
{
int intValue = (int)dt;
return (intValue < 0 || intValue >= 32) ? false : DeclarationTypeIsMember[intValue];
// you can also throw an exception if the enum is outside range.
}
// likewise for IsFunction(dt), IsProcedure(dt), IsProperty(dt), ...
유지 보수성이 문제가 될 것입니다.
C # 형식 (상속 계층의 클래스)과 열거 형 값 사이의 일대일 매핑 여부를 확인하십시오.
(또는 유형과 일대일 매핑을 보장하기 위해 열거 형 값을 조정할 수 있습니다.)
C #에서는 많은 라이브러리 Type object.GetType()
가 좋은 방법으로 나쁜 방법을 악용합니다 .
열거 형을 값으로 저장하는 모든 곳에서 Type
대신 값으로 저장할 수 있는지 여부를 스스로에게 묻을 수 있습니다 .
이 트릭을 사용하기 위해 두 가지 읽기 전용 해시 테이블을 초기화 할 수 있습니다.
// For disambiguation, I'll assume that the actual
// (behavior-implementing) classes are under the
// "Lang" namespace.
static readonly Dictionary<Type, DeclarationType> TypeToDeclEnum = ...
{
{ typeof(Lang.Procedure), DeclarationType.Procedure },
{ typeof(Lang.Function), DeclarationType.Function },
{ typeof(Lang.Property), DeclarationType.Property },
...
};
static readonly Dictionary<DeclarationType, Type> DeclEnumToType = ...
{
// same as the first dictionary;
// just swap the key and the value
...
};
클래스와 상속 계층 구조를 제안하는 사람들을위한 최종 검증
열거 형이 상속 계층 구조에 대한 근사치 임을 알면 다음 조언이 유지됩니다.
- 상속 계층 구조를 먼저 디자인 (또는 개선)하고
- 그런 다음 돌아가서 해당 상속 계층 구조와 비슷한 열거 형을 디자인하십시오.
DeclarationType
. 내가 여부를 결정하려는 경우x
의 하위 유형은y
, 아마 같은 것을 쓰고 싶은거야x.IsSubtypeOf(y)
하지,x && y == y
.