C ++의 관점에서 대답하겠습니다. 모든 핵심 개념을 C #으로 전송할 수 있다고 확신합니다.
선호하는 스타일이 "항상 예외 처리"인 것 같습니다.
int CalculateArea(int x, int y) {
if (x < 0 || y < 0) {
throw Exception("negative side lengths");
}
return x * y;
}
예외 처리가 무겁기 때문에 C ++ 코드에서 문제가 될 수 있습니다. 실패 사례가 느리게 실행되고 실패 사례가 메모리를 할당하고 (사용할 수없는 경우도 있음) 일반적으로 예측 가능성이 떨어집니다. EH의 헤비급은 "제어 흐름에 예외를 사용하지 마십시오"와 같은 사람들의 말을 듣는 이유 중 하나입니다.
따라서 일부 라이브러리 (예 <filesystem>
:)는 C ++에서 "이중 API"라고 부르는 것 또는 C #에서 Try-Parse
패턴을 부르는 것을 사용합니다 ( Peter 에게 팁을 주셔서 감사 합니다!)
int CalculateArea(int x, int y) {
if (x < 0 || y < 0) {
throw Exception("negative side lengths");
}
return x * y;
}
bool TryCalculateArea(int x, int y, int& result) {
if (x < 0 || y < 0) {
return false;
}
result = x * y;
return true;
}
int a1 = CalculateArea(x, y);
int a2;
if (TryCalculateArea(x, y, a2)) {
// use a2
}
"이중 API"의 문제점을 즉시 확인할 수 있습니다. 많은 코드 복제, 어떤 API가 "올바른"API인지에 대한 사용자 지침이 없으며 사용자는 유용한 오류 메시지 ( CalculateArea
)와 속도 ( TryCalculateArea
) 빠른 버전은 우리의 도움이 필요하기 때문에 "negative side lengths"
예외를하고에 그것을 아래로 평평하게 쓸모없는 false
- "문제가 발생했습니다, 나 무엇이나 물어 보지 않는다"고 말했다. (일부 듀얼 API는 같은 더 표현 오류 유형을 사용하여 int errno
C ++의 나 std::error_code
,하지만 여전히 당신을 말하지 않는 경우 오류가 발생 - 그것은 단지 한 곳 발생합니다.)
코드 작동 방식을 결정할 수없는 경우 언제든지 발신자에게 결정을 내릴 수 있습니다!
template<class F>
int CalculateArea(int x, int y, F errorCallback) {
if (x < 0 || y < 0) {
return errorCallback(x, y, "negative side lengths");
}
return x * y;
}
int a1 = CalculateArea(x, y, [](auto...) { return 0; });
int a2 = CalculateArea(x, y, [](int, int, auto msg) { throw Exception(msg); });
int a3 = CalculateArea(x, y, [](int, int, auto) { return x * y; });
이것은 본질적으로 동료가하는 일입니다. "오류 처리기"를 전역 변수에 포함시키는 것을 제외하고는 :
std::function<int(const char *)> g_errorCallback;
int CalculateArea(int x, int y) {
if (x < 0 || y < 0) {
return g_errorCallback("negative side lengths");
}
return x * y;
}
g_errorCallback = [](auto) { return 0; };
int a1 = CalculateArea(x, y);
g_errorCallback = [](const char *msg) { throw Exception(msg); };
int a2 = CalculateArea(x, y);
명시 적 함수 매개 변수 에서 전역 상태 로 중요한 매개 변수를 이동하는 것은 거의 항상 나쁜 생각입니다. 나는 그것을 권장하지 않습니다. ( 귀하의 경우 글로벌 상태가 아니라 단순히 인스턴스 전체 멤버 상태 라는 사실 은 악의를 약간 완화하지만 많이는 아닙니다.)
또한 동료가 불필요하게 오류 처리 동작의 수를 제한하고 있습니다. 그는 오류 처리 람다를 허용 하지 않고 두 가지만 결정했습니다.
bool g_errorViaException;
int CalculateArea(int x, int y) {
if (x < 0 || y < 0) {
return g_errorViaException ? throw Exception("negative side lengths") : 0;
}
return x * y;
}
g_errorViaException = false;
int a1 = CalculateArea(x, y);
g_errorViaException = true;
int a2 = CalculateArea(x, y);
이것은 아마도 이러한 가능한 전략 중 "사워 스팟"일 것입니다. 정확히 두 가지 오류 처리 콜백 중 하나를 사용하도록하여 최종 사용자로부터 모든 유연성을 확보했습니다 . 그리고 당신은 공유 된 세계 국가의 모든 문제를 가지고 있습니다; 그리고 당신은 여전히 모든 조건부 지점에 대한 비용을 지불하고 있습니다.
마지막으로 C ++ (또는 조건부 컴파일 기능이있는 모든 언어)의 일반적인 솔루션은 사용자가 컴파일 타임에 전 세계적으로 전체 프로그램을 결정하도록하여 취하지 않은 코드 경로를 완전히 최적화 할 수 있도록하는 것입니다.
int CalculateArea(int x, int y) {
if (x < 0 || y < 0) {
#ifdef NEXCEPTIONS
return 0;
#else
throw Exception("negative side lengths");
#endif
}
return x * y;
}
// Now these two function calls *must* have the same behavior,
// which is a nice property for a program to have.
// Improves understandability.
//
int a1 = CalculateArea(x, y);
int a2 = CalculateArea(x, y);
이런 식으로 작동하는 것의 예는 C와 C ++ 의 assert
매크로 로, 전 처리기 매크로에서 동작을 조절합니다 NDEBUG
.