언제 Debug.Assert ()를 사용해야합니까?
CS 전문가로 졸업 한 후 약 1 년 동안 전문 소프트웨어 엔지니어로 일했습니다. 나는 C ++과 C에서 한동안 어설 션에 대해 알고 있었지만 최근까지 C #과 .NET에 전혀 존재하지 않았다.
우리의 생산 코드에는 어떤 주장도 포함되어 있지 않으며 내 질문은 이것입니다 ...
프로덕션 코드에서 Asserts를 사용해야합니까? 그렇다면 언제 사용하는 것이 가장 적합합니까? 하는 것이 더 합리적일까요?
Debug.Assert(val != null);
또는
if ( val == null )
throw new exception();
에서 디버깅 마이크로 소프트 .NET 2.0 응용 프로그램 존 로빈스의 주장에 큰 부분을 가지고있다. 그의 주요 요점은 다음과 같습니다.
- 자유롭게 주장하십시오. 너무 많은 어설 션을 가질 수 없습니다.
- 어설 션은 예외를 대체하지 않습니다. 코드에서 요구하는 사항은 예외입니다. 주장은 가정하는 것을 포함합니다.
- 잘 작성된 주장은 무슨 일이 있었는지와 예외 (예 : 예외)뿐만 아니라 그 이유를 알려줄 수 있습니다.
- 예외 메시지는 종종 암호가 될 수 있으므로 오류를 일으킨 컨텍스트를 다시 작성하기 위해 코드를 거꾸로 작업해야합니다. 어설 션은 오류가 발생한 시점의 프로그램 상태를 보존 할 수 있습니다.
- 어설 션은 문서로 두 배가되어 다른 개발자에게 코드가 의존하는 가정을 알려줍니다.
- 어설 션이 실패 할 때 나타나는 대화 상자를 사용하면 프로세스에 디버거를 연결할 수 있으므로 중단 점을 배치 한 것처럼 스택 주위를 찌를 수 있습니다.
추신 : Code Complete가 마음에 드 셨다면이 책을 참조하십시오. WinDBG 및 덤프 파일 사용에 대해 배우기 위해 구입했지만 처음에는 버그를 피하는 데 도움이되는 팁이 있습니다.
Debug.Assert()
불변성을 보장하기 위해 위생 검사를 원하는 코드의 모든 곳에 넣습니다 . 릴리스 빌드를 컴파일 할 때 (즉, DEBUG
컴파일러 상수 없음 ) 호출 Debug.Assert()
은 제거되어 성능에 영향을 미치지 않습니다.
를 호출하기 전에 여전히 예외를 처리해야합니다 Debug.Assert()
. 어설 션은 개발 중에도 모든 것이 예상대로 이루어 지도록합니다.
에서 코드 완성
8 방어 프로그래밍
8.2 주장
어설 션은 개발 중에 사용되는 코드 (일반적으로 루틴 또는 매크로)로 프로그램이 실행될 때 자체적으로 확인할 수 있습니다. 어설 션이 true이면 모든 것이 예상대로 작동한다는 의미입니다. False이면 코드에서 예기치 않은 오류가 감지되었음을 의미합니다. 예를 들어, 시스템에서 고객 정보 파일에 레코드가 50,000 개를 넘지 않는다고 가정하면 프로그램에 레코드 수가 50,000보다 작거나 같다는 주장이 프로그램에 포함될 수 있습니다. 레코드 수가 50,000보다 작거나 같은 한 어설 션은 자동입니다. 그러나 50,000 개가 넘는 레코드가 발견되면 프로그램에 오류가 있다고 크게 "어설트"합니다.
어설 션은 특히 크고 복잡한 프로그램과 높은 안정성의 프로그램에서 유용합니다. 이를 통해 프로그래머는 불일치 한 인터페이스 가정, 코드 수정시 발생하는 오류 등을보다 신속하게 제거 할 수 있습니다.
어설 션은 일반적으로 두 가지 인수, 즉 사실로 가정되는 가정을 설명하는 부울 식과 그렇지 않은 경우 표시 할 메시지를받습니다.
(…)
일반적으로 사용자는 프로덕션 코드에서 어설 션 메시지를 보지 않기를 원합니다. 어설 션은 주로 개발 및 유지 관리 중에 사용됩니다. 어설 션은 일반적으로 개발시 코드로 컴파일되고 프로덕션 코드에서 컴파일됩니다. 개발 중에 어설 션은 모순 된 가정, 예기치 않은 조건, 잘못된 값이 루틴에 전달되는 등을 제거합니다. 생산하는 동안 어설 션이 시스템 성능을 저하시키지 않도록 코드에서 컴파일됩니다.
FWIW ... 공개 메소드가 if () { throw; }
패턴을 사용하여 메소드가 올바르게 호출되도록하는 경향이 있습니다 . 내 개인 방법은을 사용하는 경향이 있습니다 Debug.Assert()
.
아이디어는 내 개인 메서드를 사용하여 제어하는 방법이므로 잘못된 매개 변수를 사용하여 자체 개인 메서드 중 하나를 호출하기 시작하면 어딘가에 내 자신의 가정을 어겼습니다. 그 상태로. 프로덕션 환경에서 이러한 개인 주장은 내부 상태를 유효하고 일관되게 유지해야하므로 이상적으로 불필요한 작업이어야합니다. 런타임에 누구나 호출 할 수있는 공용 메소드에 제공된 매개 변수와 대조 : 여전히 예외를 발생시켜 매개 변수 제약 조건을 적용해야합니다.
또한 런타임에 무언가 작동하지 않는 경우에도 개인 메서드가 여전히 예외를 throw 할 수 있습니다 (네트워크 오류, 데이터 액세스 오류, 타사 서비스에서 검색 한 잘못된 데이터 등). 내 주장은 그 물체의 상태에 대한 내 자신의 가정을 깨뜨리지 않도록하기 위해 거기에 있습니다.
어설 션을 사용하여 개발자 가정을 확인하고 예외를 환경 가정을 확인하십시오.
내가 당신이라면 내가 할 것입니다 :
Debug.Assert(val != null);
if ( val == null )
throw new exception();
또는 반복적 인 상태 점검을 피하기 위해
if ( val == null )
{
Debug.Assert(false,"breakpoint if val== null");
throw new exception();
}
프로덕션 코드 (예 : 릴리스 빌드)에 Asserts를 원하는 경우 Debug.Assert 대신 Trace.Assert를 사용할 수 있습니다.
이것은 물론 프로덕션 실행 파일에 오버 헤드를 추가합니다.
또한 응용 프로그램이 사용자 인터페이스 모드에서 실행중인 경우 어설 션 대화 상자가 기본적으로 표시되며 이는 사용자에게 약간의 혼란을 줄 수 있습니다.
DefaultTraceListener를 제거하여이 동작을 무시할 수 있습니다. MSDN의 Trace.Listeners 설명서를 참조하십시오.
요약하자면,
Debug.Assert를 자유롭게 사용하여 디버그 빌드에서 버그를 잡을 수 있습니다.
사용자 인터페이스 모드에서 Trace.Assert를 사용하는 경우 사용자가 당황하지 않도록 DefaultTraceListener를 제거하려고 할 수 있습니다.
테스트중인 조건이 앱이 처리 할 수없는 조건 인 경우 예외가 발생하여 실행이 계속되지 않도록하는 것이 좋습니다. 사용자는 어설 션을 무시하도록 선택할 수 있습니다.
Asserts are used to catch programmer (your) error, not user error. They should be used only when there is no chance a user could cause the assert to fire. If you're writing an API, for example, asserts should not be used to check that an argument is not null in any method an API user could call. But it could be used in a private method not exposed as part of your API to assert that YOUR code never passes a null argument when it isn't supposed to.
I usually favour exceptions over asserts when I'm not sure.
In Short
Asserts
are used for guards and for checking Design by Contract constraints, viz:
Asserts
should be for Debug and non-Production builds only. Asserts are typically ignored by the compiler in Release builds.Asserts
can check for bugs / unexpected conditions which ARE in the control of your systemAsserts
are NOT a mechanism for first-line validation of user input or business rulesAsserts
should not be used to detect unexpected environmental conditions (which are outside the control of the code) e.g. out of memory, network failure, database failure, etc. Although rare, these conditions are to be expected (and your app code cannot fix issues like hardware failure or resource exhaustion). Typically, exceptions will be thrown - your application can then either take corrective action (e.g. retry a database or network operation, attempt to free up cached memory), or abort gracefully if the exception cannot be handled.- A failed Assertion should be fatal to your system - i.e. unlike an exception, do not try and catch or handle failed
Asserts
- your code is operating in unexpected territory. Stack Traces and crash dumps can be used to determine what went wrong.
Assertions have enormous benefit:
- To assist in finding missing validation of user inputs, or upstream bugs in higher level code.
- Asserts in the code base clearly convey the assumptions made in the code to the reader
- Assert will be checked at runtime in
Debug
builds. - Once code has been exhaustively tested, rebuilding the code as Release will remove the performance overhead of verifying the assumption (but with the benefit that a later Debug build will always revert the checks, if needed).
... More Detail
Debug.Assert
expresses a condition which has been assumed about state by the remainder of the code block within the control of the program. This can include the state of the provided parameters, state of members of a class instance, or that the return from a method call is in its contracted / designed range. Typically, asserts should crash the thread / process / program with all necessary info (Stack Trace, Crash Dump, etc), as they indicate the presence of a bug or unconsidered condition which has not been designed for (i.e. do not try and catch or handle assertion failures), with one possible exception of when an assertion itself could cause more damage than the bug (e.g. Air Traffic Controllers wouldn't want a YSOD when an aircraft goes submarine, although it is moot whether a debug build should be deployed to production ...)
When should you use Asserts?
- At any point in a system, or library API, or service where the inputs to a function or state of a class are assumed valid (e.g. when validation has already been done on user input in the presentation tier of a system, the business and data tier classes typically assume that null checks, range checks, string length checks etc on input have been already done). - Common Assert
checks include where an invalid assumption would result in a null object dereference, a zero divisor, numerical or date arithmetic overflow, and general out of band / not designed for behaviour (e.g. if a 32 bit int was used to model a human's age, it would be prudent to Assert
that the age is actually between 0 and 125 or so - values of -100 and 10^10 were not designed for).
.Net Code Contracts
In the .Net Stack, Code Contracts can be used in addition to, or as an alternative to using Debug.Assert
. Code Contracts can further formalize state checking, and can assist in detecting violations of assumptions at ~compile time (or shortly thereafter, if run as a background check in an IDE).
Design by Contract (DBC) checks available include:
Contract.Requires
- Contracted PreconditionsContract.Ensures
- Contracted PostConditionsInvariant
- Expresses an assumption about the state of an object at all points in its lifespan.Contract.Assumes
- pacifies the static checker when a call to non-Contract decorated methods is made.
Mostly never in my book. In the vast majority of occasions if you want to check if everything is sane then throw if it isn't.
What I dislike is the fact that it makes a debug build functionally different to a release build. If a debug assert fails but the functionality works in release then how does that make any sense? It's even better when the asserter has long left the company and no-one knows that part of the code. Then you have to kill some of your time exploring the issue to see if it is really a problem or not. If it is a problem then why isn't the person throwing in the first place?
To me this suggests by using Debug.Asserts you're deferring the problem to someone else, deal with the problem yourself. If something is supposed to be the case and it isn't then throw.
I guess there are possibly performance critical scenarios where you want to optimise away your asserts and they're useful there, however I am yet to encounter such a scenario.
According to the IDesign Standard, you should
Assert every assumption. On average, every fifth line is an assertion.
using System.Diagnostics;
object GetObject()
{...}
object someObject = GetObject();
Debug.Assert(someObject != null);
As a disclaimer I should mention I have not found it practical to implement this IRL. But this is their standard.
Use assertions only in cases where you want the check removed for release builds. Remember, your assertions will not fire if you don't compile in debug mode.
Given your check-for-null example, if this is in an internal-only API, I might use an assertion. If it's in a public API, I would definitely use the explicit check and throw.
All asserts should be code that could be optimised to:
Debug.Assert(true);
Because it's checking something that you have already assumed is true. E.g.:
public static void ConsumeEnumeration<T>(this IEnumerable<T> source)
{
if(source != null)
using(var en = source.GetEnumerator())
RunThroughEnumerator(en);
}
public static T GetFirstAndConsume<T>(this IEnumerable<T> source)
{
if(source == null)
throw new ArgumentNullException("source");
using(var en = source.GetEnumerator())
{
if(!en.MoveNext())
throw new InvalidOperationException("Empty sequence");
T ret = en.Current;
RunThroughEnumerator(en);
return ret;
}
}
private static void RunThroughEnumerator<T>(IEnumerator<T> en)
{
Debug.Assert(en != null);
while(en.MoveNext());
}
In the above, there are three different approaches to null parameters. The first accepts it as allowable (it just does nothing). The second throws an exception for the calling code to handle (or not, resulting in an error message). The third assumes it can't possibly happen, and asserts that it is so.
In the first case, there's no problem.
In the second case, there's a problem with the calling code - it shouldn't have called GetFirstAndConsume
with null, so it gets an exception back.
In the third case, there's a problem with this code, because it should already have been checked that en != null
before it was ever called, so that it isn't true is a bug. Or in other words, it should be code that could theoretically be optimised to Debug.Assert(true)
, sicne en != null
should always be true
!
I thought I would add four more cases, where Debug.Assert can be the right choice.
1) Something I have not seen mentioned here is the additional conceptual coverage Asserts can provide during automated testing. As a simple example:
When some higher-level caller is modified by an author who believes they have expanded the scope of the code to handle additional scenarios, ideally (!) they will write unit tests to cover this new condition. It may then be that the fully integrated code appears to work fine.
However, actually a subtle flaw has been introduced, but not detected in test results. The callee has become non-deterministic in this case, and only happens to provide the expected result. Or perhaps it has yielded a rounding error that was unnoticed. Or caused an error that was offset equally elsewhere. Or granted not only the access requested but additional privileges that should not be granted. Etc.
At this point, the Debug.Assert() statements contained in the callee coupled with the new case (or edge case) driven in by unit tests can provide invaluable notification during test that the original author's assumptions have been invalidated, and the code should not be released without additional review. Asserts with unit tests are the perfect partners.
2) Additionally, some tests are simple to write, but high-cost and unnecessary given the initial assumptions. For example:
If an object can only be accessed from a certain secured entry point, should an additional query be made to a network rights database from every object method to ensure the caller has permissions? Surely not. Perhaps the ideal solution includes caching or some other expansion of features, but the design does not require it. A Debug.Assert() will immediately show when the object has been attached to an insecure entry point.
3) Next, in some cases your product may have no helpful diagnostic interaction for all or part of its operations when deployed in release mode. For example:
Suppose it is an embedded real-time device. Throwing exceptions and restarting when it encounters a malformed packet is counter-productive. Instead the device may benefit from best-effort operation, even to the point of rendering noise in its output. It also may not have a human interface, logging device, or even be physically accessible by human at all when deployed in release mode, and awareness of errors is best provided by assessing the same output. In this case, liberal Assertions and thorough pre-release testing are more valuable than exceptions.
4) Lastly, some tests are unneccessary only because the callee is perceived as extremely reliable. In most cases, the more reusable code is, the more effort has been put into making it reliable. Therefore it is common to Exception for unexpected parameters from callers, but Assert for unexpected results from callees. For example:
If a core String.Find
operation states it will return a -1
when the search criteria is not found, you may be able to safely perform one operation rather than three. However, if it actually returned -2
, you may have no reasonable course of action. It would be unhelpful to replace the simpler calculation with one that tests separately for a -1
value, and unreasonable in most release environments to litter your code with tests ensuring core libraries are operating as expected. In this case Asserts are ideal.
Quote Taken from The Pragmatic Programmer: From Journeyman to Master
Leave Assertions Turned On
There is a common misunderstanding about assertions, promulgated by the people who write compilers and language environments. It goes something like this:
Assertions add some overhead to code. Because they check for things that should never happen, they'll get triggered only by a bug in the code. Once the code has been tested and shipped, they are no longer needed, and should be turned off to make the code run faster. Assertions are a debugging facility.
There are two patently wrong assumptions here. First, they assume that testing finds all the bugs. In reality, for any complex program you are unlikely to test even a miniscule percentage of the permutations your code will be put through (see Ruthless Testing).
Second, the optimists are forgetting that your program runs in a dangerous world. During testing, rats probably won't gnaw through a communications cable, someone playing a game won't exhaust memory, and log files won't fill the hard drive. These things might happen when your program runs in a production environment. Your first line of defense is checking for any possible error, and your second is using assertions to try to detect those you've missed.
Turning off assertions when you deliver a program to production is like crossing a high wire without a net because you once made it across in practice. There's dramatic value, but it's hard to get life insurance.
Even if you do have performance issues, turn off only those assertions that really hit you.
You should always use the second approach (throwing exceptions).
Also if you're in production (and have a release-build), it's better to throw an exception (and let the app crash in the worst-case) than working with invalid values and maybe destroy your customer's data (which may cost thousand of dollars).
You should use Debug.Assert to test for logical errors in your programs. The complier can only inform you of syntax errors. So you should definetely use Assert statements to test for logical errors. Like say testing a program that sells cars that only BMWs that are blue should get a 15% discount. The complier could tell you nothing about if your program is logically correct in performing this but an assert statement could.
I've read the answers here and I thought I should add an important distinction. There are two very different ways in which asserts are used. One is as a temporary developer shortcut for "This shouldn't really happen so if it does let me know so I can decide what to do", sort of like a conditional breakpoint, for cases in which your program is able to continue. The other, is a as a way to put assumptions about valid program states in your code.
In the first case, the assertions don't even need to be in the final code. You should use Debug.Assert
during development and you can remove them if/when no longer needed. If you want to leave them or if you forget to remove them no problem, since they won't have any consequence in Release compilations.
But in the second case, the assertions are part of the code. They, well, assert, that your assumptions are true, and also document them. In that case, you really want to leave them in the code. If the program is in an invalid state it should not be allowed to continue. If you couldn't afford the performance hit you wouldn't be using C#. On one hand it might be useful to be able to attach a debugger if it happens. On the other, you don't want the stack trace popping up on your users and perhaps more important you don't want them to be able to ignore it. Besides, if it's in a service it will always be ignored. Therefore in production the correct behavior would be to throw an Exception, and use the normal exception handling of your program, which might show the user a nice message and log the details.
Trace.Assert
has the perfect way to achieve this. It won't be removed in production, and can be configured with different listeners using app.config. So for development the default handler is fine, and for production you can create a simple TraceListener like below which throws an exception and activate it in the production config file.
using System.Diagnostics;
public class ExceptionTraceListener : DefaultTraceListener
{
[DebuggerStepThrough]
public override void Fail(string message, string detailMessage)
{
throw new AssertException(message);
}
}
public class AssertException : Exception
{
public AssertException(string message) : base(message) { }
}
And in the production config file:
<system.diagnostics>
<trace>
<listeners>
<remove name="Default"/>
<add name="ExceptionListener" type="Namespace.ExceptionTraceListener,AssemblyName"/>
</listeners>
</trace>
</system.diagnostics>
I don't know how it is in C# and .NET, but in C will assert() only work if compiled with -DDEBUG - the enduser will never see an assert() if it's compiled without. It's for developer only. I use it really often, it's sometimes easier to track bugs.
I would not use them in production code. Throw exceptions, catch and log.
Also need to be careful in asp.net, as an assert can show up on the console and freeze the request(s).
참고URL : https://stackoverflow.com/questions/129120/when-should-i-use-debug-assert
'Programing' 카테고리의 다른 글
C ++ 함수에서 여러 값 반환 (0) | 2020.04.27 |
---|---|
List에서 캐스트하기위한 더 짧은 구문 (0) | 2020.04.27 |
파일을 어떻게 이동합니까? (0) | 2020.04.27 |
jQuery-선택한 옵션에서 사용자 정의 속성 가져 오기 (0) | 2020.04.27 |
Swift에서 버튼에 둥근 테두리를 만들려면 어떻게해야합니까? (0) | 2020.04.27 |