|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
Contents
IntroductionThis article affects some important aspects of programming and presents a set of ideas. Unusual coding techniques are presented here. They are based on abstractions which are supported by specially designed library — ElementaryExtensions, which can be characterized as a set of general helpers for .NET programming. In spite of that fact that we see a great progress in programming languages and infrastructures, there are some directions which not seem to be completely developed at all. (One of them, for instance, are means for consistency protection and prevention of unexpected side effects). Trying to cover some existing problems is not a useless intention. I've decided to craft ElementaryExtensions by the following reasons: to satisfy my own coding needs and to realize important ideas in reusable piece of code (with quite universal interface). The library has not been tested yet as a part of any real-world application. It serves now mainly as a plaything, — for different experiments, for demonstration of ideas which it encapsulates and some tricks. It may be used while only as utility for creation of non-serious applications. The library has its own documentation, it is contained in the ZIP-archive for downloading. This HTML is a transformed and reduced version of ElementaryExtensions.Library.doc file (from the documentation). In the article you'll see only key directions of the ElementaryExtensions (some of them). BackgroundThis article assumes you are familiar with .NET and C# (or a similar language). There are two Code Project links (.NET section) which are related to the subject of this article. They are about exception handling problems. All that is described there is only a series of hypothetical ideas. You may get a look to them, but do not perceive that material seriously, — the ideas were not then well formed. (ElementaryExtensions presents more correct and suitable variants.) The Code Project articles (by Sergei Kitaev):
It was not correctly to mention Java's checked exceptions in that way (the second article). They were designed for some other purposes, mainly in order to prevent uncontrollable exception pass-through towards the top level. ElementaryExtensions library: Collection of additional general abstractions and helpers as a set of special lightweight extension components for .NET programmingAlmost any non-trivial program module requires (when it is crafted) a set of special auxiliary static routines and, may be, instance classes. These extensions are called often as helpers and auxiliary classes. Many of such things are placed as pieces of code into different projects with some modifications. Some routines no longer depend on the task specificity and become thus universal. ElementaryExtensions library is a set of utility assemblies (DLLs) that may be helpful for writing of .NET targeted code. The library serves as a set of miscellaneous simple abstractions and helpers with sufficient universality, so they may be suitable for different purposes. Of course, .NET provides a huge set of general purpose classes, so we don't have any lack of programming infrastructure (as in previous times). But nevertheless, ElementaryExtensions may be helpful for you, because it contains not only ordinary additions but also some unique things. ElementaryExtensions is targeted for .NET 2.0 and/or later framework versions, to function under OS Windows starting from Windows 2000. The main type of code where we can use the library is full-trusted code. It doesn't mean that the library is principally oriented for full-trusted coding only, but in this experimental version trying to use it for creation of modules with restricted code access rights is not recommended. ElementaryExtensions API is represented by a set of managed classes and other kinds of .NET types. In this library version there is only one namespace — ElementaryExtensions modules act as pieces of reusable code. They provide a set of helpers of quite general character, which can be intensively used in many places of your .NET code in order to make the coding some easy and/or better. For example, code snippet that is located below is used in order to parse decimal number from its string representation (culture invariant) without catching an exception. Draw attention to (E)-prefix of Parsing decimal value with help of the ElementaryExtensions library (this code snippet is from ConsoleCalculator sample): // Parsing the string as decimal value (culture invariant):
string strParseError;
if (!EValueParser.TryParseDecimalInvariant( strOperand,
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign |
NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite,
out operand, out strParseError ))
{
ReportErrorRedefineOperand(strParseError);
goto LOOP;
}
All ElementaryExtensions public types are prefixed with a special character (E) (U+0404: Cyrillic Capital Letter Ukrainian Ie) which is intended to mark all points of access to the library from client code. (This is like C-prefix in MFC or T-prefix in VCL.) Namespaces are also used for public types (which all currently belong to To enable typing of some special characters including (E), ElementaryExtensions provides the following extended keyboard layouts: KBDUS_X and KBDUSX_X (see library pack). Due to inability to use unicode encoding for this HTML (it is restricted to be in CP-1252 / ISO-8859-1), everywhere in the article special character (E) (U+0404: Cyrillic Capital Letter Ukrainian Ie) was intentionally replaced by ordinary E (U+0045: Latin Capital Letter E). Besides the DLLs (library assemblies) ElementaryExtensions provides a set of helpful command-line batches, some SQL-procedures and other utilities for programming tasks. Consistency problems (and others): Lacks of existing exception handling mechanisms, Absence of sufficient and reliable support in classic programming languages/runtimes; Idea of the Clean ResultError handling is a hard task that can not be solved easily in any way, even with help of exceptions. One well known existing method of fully reliable error control is usage of an API which is based on returning error codes (like NTSTATUS and/or Win32 Last Error). This strategy is heavy for its implementation and requires a strong attention regarding to prevention of leaks in result code checks. Such style is an inalienable aspect of low level programming (such as coding of kernel drivers, for instance). .NET has a more advanced error control. Everywhere, except some peculiar calls, it uses API which is fully based on exceptions. Exceptions are an automation of error control. They enforce program to react on a problem, whether it is expected or not (by throwing errors as exceptions). But such automation has a great influence on the code flow and in some cases may cause insidious situations. Further in this document there is a description of main problems/shortcomings related to existing exception handling mechanisms/strategies. Some of them were not fully solved up to the present time. One of the main aims of the ElementaryExtensions library is to provide support of a programming technique which can be named as strategy of the Clean Result, where any obtained information (computed/displayed/etc.) is absolutely clean / trust worthy. This does not mean that the code will have no errors at all, but it will be organized in a way, where any unexpected situations that may cause inconsistency or some wrong behavior will be prevented. Such style, of course, will require a lot of supplementary efforts in programming, but it may bring certain gain in some tasks where any error/incorrectness in output data is inadmissible. Do not confuse: this strategy is not related to Resistance against Failures (another important direction in the programming). I. First problem: Violation of logically unbreakable code region as implicit interruption of code flow caused by some unexpected exception; as result — inconsistency of program dataAlmost any non-trivial program has some critical places, whose incomplete passing may stay the program in some inconsistent state. (Such situation is very characteristic for manipulations with static data.) Let us consider an approximate example of potentially unreliable code. We'll make an assumption that centralized exception handling takes place (main window message loop has a uniform exception handler, that inform the user about caught exception / exception chain and allows to continue functioning). Imagine that we have to code a response for some window message/event in our GUI application. We implement this operation as routine where we change (modify/enable/disable) some UI elements and alter related invisible data. If we expect exceptions, we'll guard correspondent place properly by surrounding it with An appropriate abstraction for the problem is so called Logically Unbreakable Code Region, which, if started, can not be interrupted implicitly (by exception thrown from any lower layer); it must complete or cause a failure of entire application domain. Such critical places should be formed as special fault intolerant code blocks. The programmer has to mark such regions explicitly and should provide them with proper preventive checks. (Centralized exception handling, if used, will still inform us about an error in any non-critical place). II. Second problem: Exception misidentification, where unexpected exception thrown at some deep layer is treated at some upper layer as an expected problem in a near lower layer; as result — incorrect judging (based on such imperfect exception information)This situation is more sophisticated than the first one. An insidious side effect is described here. The problem is that the type of caught exception is not often sufficient information to make a decision about further code flow. In some cases, flow control in our application may principally depend on a caught exception. For instance: we do not simply report an error to the user, but make an important decision, on which branch we must switch to. This decision usually is based on the type of the exception. An API-function declaration typically says, that if the function encounters some problem — it throws a certain kind of exception (NOTHING ELSE is usually asserted). But if we've caught this kind of exception, can we be sure, that the associated problem REALLY takes place??? Strictly speaking — NO!!! ( Further you'll find a section about so called III. Third shortcoming: Loss of one exception chain as result of raising of some another exception during phase of deterministic finalization, which, in its turn, was initiated by throwing of the first exception; in other words — loss of some part of error informationThis is, on the whole, not a problem, but a shortcoming. Generally speaking, it is not really possible to handle exception together with its full error raising history, but this history may be useful for displaying error report to the user (to browse it and to examine visually). At present time, .NET exception object (represented by In spite of this side effect, we should not worry about our Clean Result, — it affects only a partial loss of exception information, but do not violate consistency. Other exception handling problems; Ideas in some advanced programming techniquesIn terms of the Clean Result Idea three main problems related to exceptions where introduced above. Among the other exception handling difficulties the most important are: Resistance against Failures, Reliability of Centralized Exception Handling and combining of the strategies. As applied to exception handling, ElementaryExtensions does not provide anything except consistency protection helpers. It covers only those lacks, which are an impedimental hole on a way to the strategy of the Clean Result. To program in terms of the Clean Result, where have to use some advanced technique. Programming languages do not still provide special constructions. Interesting fact: .NET framework 2.0 in comparison against the previous 1.1 version gives a way to terminate entire OS process by ElementaryExtensions provides library level support for the Clean Result Idea. Of course, such technique would look much better if to be implemented in the programming language, but the library based way is also suitable variant. Only one thing can not be realized absolutely reliable at library level — Unbreakable Code Region. Fully reliable implementation of this abstraction will require support in the compiler and execution engine. ElementaryExtensions API (some of key directions)ElementaryExtensions affects different sides of general programming. Here are described only those key directions that motivated the birth of the library. (For a full list of public types see the following library document: ElementaryExtensions.Types.doc, it contains brief description for each library type, where the types are listed in the alphabet order.) Consistency protection: Fatal Error, Strict Exceptions & Voronezh ConstructionsIn order to protect program data consistency and to write the code in terms of the Clean Result Idea, ElementaryExtensions provides some support. These are the following working abstractions: Fatal Error, Unbreakable Code Region and Strict Exceptions. FATAL ERRORSometimes, under some circumstances, it is strictly necessary to prevent further functioning of the program and to terminate the entire application domain. But it should be done gracefully and informatively. Notion Fatal Error serves just for it. Fatal Error is represented by Raising an error, you can specify one of predefined reasons and/or your custom message. By default, fatal error dialog(s) will appear, — there the error may be analyzed (the user will see sufficient information). After the Fatal Error Dialogs (if provided) and so called Emergency Callbacks (if provided), in the first procedural mode, entire OS process will be terminated, — via Finally, when application is terminated by the Fatal Error in console application — primary error dialog (this screenshot is from MiscellaneousDemo sample):
Fatal Error trace (such output is produced automatically):
Fatal Error full report — secondary error dialog (this screenshot is from MiscellaneousDemo sample):
Post Fatal Error system dialog — may appear after Environment.FailFast call:
Fatal Error record in Application Event Log — it is created by Environment.FailFast call (as an error from .NET Runtime source):
See MiscellaneousDemo sample application as an example (may simulate different fatal error situations). VORONEZH CONSTRUCTIONS AS LIBRARY LEVEL TOOLS FOR CONSISTENCY CONTROLIdeally, Unbreakable Code Regions and Strict Exceptions should be implemented directly in the programming language. But in spite of some lack of convenience, library level support allows to avail of these important abstractions. For that, ElementaryExtensions uses a term of Voronezh Constructions, a set of things for consistency protection, — those of them, that should be implemented in the programming language directly, but currently available at the library level. (Voronezh is the name of the city where ElementaryExtensions was invented.) UNBREAKABLE REGIONS (IN VORONEZH CONSTRUCTIONS)The idea is to have in function code such critical regions that can't be interrupted implicitly, by an exception issued from some lower layer. Only such keywords as This simple abstraction is supported in the ElementaryExtensions by Simplified unbreakable region (without explicit throwing and/or CER), implemented directly in client code: // Simple UR region:
try
{
// Unbreakable code block (exception throwing is not presumed):
: : : : :
}
// Interceptor:
catch (Exception e)
{
// Invoking Fatal Error:
EUnbreakableRegion.Crash(e);
}
Fully unbreakable region (with safe exception throwing, formed as CER), implemented directly in client code: // Advanced UR region:
EUnbreakableRegion ur=new EUnbreakableRegion();
System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions();
try {} finally
{
// CER (Constrained Exception Region):
try
{
// Unbreakable code block:
: : : : :
// Safe exception throwing (will not cause fatal error):
throw ur.PrepareForSafeThrowing(
new InvalidOperationException() );
: : : : :
}
// Interceptor:
catch (Exception e)
{
// Forwarding exception or invoking Fatal Error:
ur.CheckInterceptedException(e);
throw;
}
}
Fully unbreakable region implemented as user's callback (this code snippet is from MiscellaneousDemo sample): // Fully unbreakable region formed as user's callback (anonymous method):
EUnbreakableRegion.Execute
(
delegate (EUnbreakableRegion ur)
{
// Unbreakable code block:
if (bSafeThrowing)
// Safe exception throwing (will not cause fatal error):
throw ur.PrepareForSafeThrowing(
new ApplicationException("Simulated error.") );
else
// Unsafe exception throwing (will cause fatal error):
throw new ApplicationException("Simulated error.");
}
);
The most typical example is the third. The two first variants may be used when you need to access to your Safe exception throwing requires corresponding preparation before performing the If some thread tries to abort another thread, whose code-flow is passing through its unbreakable region (which is implemented as CER), this abort request will be deferred until code flow will leave the block. The abstraction is simple enough. It allows us to have a sequence of logically indissoluble operations (surrounded with UR) being sure, that no of such complex operations will be violated staying our application functioning. To avoid possible failures in such critical places (to avoid fatal error) we have to provide corresponding pre-checks. STRICT EXCEPTIONS (IN VORONEZH CONSTRUCTIONS)This is an intricate abstraction. Its mission is to solve insidious problem of exception misidentification (mentioned earlier) without wide usage of something like the URs (Unbreakable Regions). The root of the problem is in some lack of error information in standard exception handling practice. When we catch an exception, we can make some judging, which is often based only on the type of the caught object. But having such information we can not do absolutely right assertions. We code some branching based on type of exception that we expect from some nearly laid lower levels, and it seems that all should work correctly. But sometimes an exception of another nature but with the same type may be caught by our Thinking in this way, it is logically enough to suppose adding of some standard information for the most general exception type (some logical flag for instance). Also, there is an idea to separate program layers somehow (not so as it is used to be in existing programming languages); there is a need in some declarative enhancements for functions. (But these are only hints.) Concept of the Strict Exceptions (an abstract principle): If an API-function/method declares some exception as STRICT, such an exception may be thrown by this function as strict THEN and ONLY THEN, when a problem which is associated with this exception really takes place. The problem is declared together with the function (through XML-comment, for instance). The idea is to guarantee UNIVOCAL CORRESPONDENCE between DECLARED EXCEPTION and its ASSOCIATED PROBLEM at function scope. By this logic, exception that is strictly connected with declared problem (possible for some method) has to be marked as strict, in order to distinguish it from some other exceptions of the same type. Abstract example of strict function declaration, may throw two strict exceptions (declared together with the function) and any non-strict exception: /// <summary>
/// Example of a function that supports strict exceptions feature
/// </summary>
/// <exception cref="SomeException">
/// Is thrown when ... (possible problem)
/// </exception>
/// <exception cref="SomeException_1">
/// Exactly means ... (first problem)
/// </exception>
/// <exception cref="SomeException_2">
/// Exactly means ... (second problem)
/// </exception>
/// <remarks>
/// This example is in some hypothetical language (in style of C#).
/// Only strict exceptions are listed explicitly in the function declaration.
/// </remarks>
void StrictFunction() exceptions: SomeException_1,SomeException_2
{
: : : : :
}
Strict Exceptions Specification (in Voronezh Constructions): The principle which is described above is simple itself, but its maintenance is not easy. Ideally, special language constructions may support corresponding technique in a quite acceptable way. In the library, strict exceptions support is represented by the following classes: Accessing strict function, analyzing caught exception, checking its strict flag and performing strict judgment: try
{
// Accessing strict function:
StrictFunction();
}
catch (SomeException e)
{
if (EException.IsStrict(e))
{
// Here we do certainly deal with that problem which is associated
// with "SomeException" exception in context of "StrictFunction" routine.
: : : : :
}
throw; // forwarding non-strict exception (to upper layer)
}
If a function declares some exception as strict (has a strict exceptions feature), it must enclose all its instructions into special The Implementation of strict function (supports strict exceptions feature): /// <summary>
/// Example of a function that supports strict exceptions feature
/// </summary>
/// <exception cref="SomeException">
/// Is thrown when ... (possible problem)
/// </exception>
/// <exception cref="SomeException_1" strict="true">
/// Exactly means ... (first problem)
/// </exception>
/// <exception cref="SomeException_2" strict="true">
/// Exactly means ... (second problem)
/// </exception>
/// <remarks>
/// The example is in C#. In these XML comments, in "exceptions" tag,
/// there is non-standard attribute: "strict"
/// (it is ignored by C# compiler).
/// </remarks>
void StrictFunction()
{
// This function is based on an object of "EStrictFunction" class:
EStrictFunction sf=new EStrictFunction();
try
{
// Strict function code block:
: : : : :
// Usual throwing (non-strict):
throw new SomeException("Error description message.");
: : : : :
// Throwing exception as strict:
throw sf.PrepareForStrictThrowing(
new SomeException_1("Error description message.") );
: : : : :
}
// Interceptor:
catch (Exception e)
{
// Only those exception on which this local strict function object (sf)
// was prepared will become strict (will leave the function as strict).
// Any other exception, even strict exception issued from another strict
// function, will be reset (will leave the function as non-strict).
if (sf.AlterInterceptedException(e))
throw e;
throw;
}
}
If one strict function performs a call to another strict function, it may guard such call by providing an appropriate handler-interceptor, and to re-throw caught strict exception explicitly after that — by the Calling one strict function from another (tolerant block and strict re-throwing): : : : : :
// This block is tolerant agains strict exception of type
// "System.InvalidOperationException" which may issued from
// "AnotherStrictFunction" routine as strict:
try
{ ...=AnotherStrictFunction(...); }
catch (InvalidOperationException ioe)
{ throw sf.PrepareForStrictRethrowing(ioe); }
: : : : :
// This block is relatively tolerant block:
try
{
// Accessing strict function:
...=AnotherStrictFunction(...);
}
catch (ArgumentException ae)
{
// Performing some checking:
if ( ... )
// Strict re-throwing. Only strict exception may be re-thrown
// as strict by relation to the current function.
throw sf.PrepareForStrictRethrowing(ae);
// By default, simply forwarding exception
// (it will not be strict in context of the current function):
throw;
}
: : : : :
The problem of exception misidentification also may be solved with help of another coding technique, where strict exceptions are not thrown, but are returned via function result value or See StrictExceptionsDemo sample application to understand the idea. This is the only place where strict exceptions technique has been probed. (At present time ElementaryExtensions itself neither provides public strict functions nor use the strategy internally.) Universal arithmetic operations in generics (over Indeterminate Value Types)Generics are wonderful invention. Using idea of parameterized types and functions we may craft universal things (such as flexible collections and other). In comparison with C++ templates, as for access from client code, generics may reside in DLL, instead of staying in header files. However (in contrast to C++ templates), generics are strictly limited by their type parameter(s), which are .NET interfaces. We can not do anything with parameter expect those, that such interface allows doing. This is generally not a problem, but even an advantage of strictness, — we have to add some operations into our interface in order to extend capabilities of generic function/type. But what about arithmetic operators over built-in types, can we code universal computations somehow? (There are no interfaces such as ElementaryExtensions provides a set of widely-used arithmetic operations over so called Indeterminate Value Types (built-in and user defined), so we gain possibility to code simple parameterized arithmetic (only limited set of operations). Using For standard CLR number types (such as These are types which are responsible for universal arithmetic operations (you may explore them in the Object Browser, by browsing ElementaryExtensions.Complementary.dll): Simplified Console API (teletype mode only)There is a list of circumstances by which sometime we do not want to deal with GUI, but prefer simple text console instead. We may choose this way for test application, where we mastering something. A lot of command-line utilities exist. (They can be batched in a CMD-script, so we can gain some advantages.). Many sample applications are frequently realized as console programs. Etc. .NET framework provides a way to access text console through ElementaryExtensions provides relatively universal way for programming of so called Easy Console UI, with help of Simplified Console API (teletype mode only) that is represented by To apply Simplified Console API you have the following: Main aspects of Simplified Console API (teletype mode only):
Read about the following samples: MiscellaneousDemo and ConsoleCalculator, — both have a lot of examples how to code with help of this API. Explore source codes (CS files), probe the apps in action. Other directions (APIs)The library has a lot of types with different purposes. Among them there are the following directions (not full list): General Operations, Alternative Value Parsing, Advanced Tracing, Some File Operations, Simple Text Manipulations, Additional Support for Arrays and Collections, Beeper API, etc. See the following document: ElementaryExtensions.Types.doc (contains brief description for each library type, where the types are listed in the alphabet order). Explore assemblies ElementaryExtensions.*.dll (in the Object Browser). ElementaryExtensions samples (.NET demo applications)ElementaryExtensions contains a series of samples for .NET programming. Some special techniques are demonstrated there (based on the library). The samples are located in Samples\Demo Applications directory). You may simply compile them with help of corresponding CMD-batches (provided for each project) or by Visual Studio. .NET samples are organized in such way, that inside library pack you may probe them (run, translate) without properly installed ElementaryExtensions. For command-line builds environment variable ElementaryExtensions_ROOT is set automatically (it is used for additional targets files). Application configuration file .exe.config provide information for assembly binding (ElementaryExtensions DLLs from library pack). The samples are shipped with library as already compiled applications, so you may just probe them in action with help of the corresponding CMD-batches. (Some tests require to be launched from CMD-batches, where command-line parameters are specified.) Sample projects contain some additional project files: .csproj.user, .csproj.targets and .csproj.settings. They are uses for advanced builds from command-line and do not have any effect to result of IDE-build. Some ElementaryExtensions targets are invoked when you translate these projects by provided CMD-batches. Explore the scripts. Trying to open sample project in Visual Studio will cause security warning, because the .user file contains CustomAfterMicrosoftCommonTargets property (that is considered as insecure). In the samples this property is affected only for command-line build (where it is used to include .targets file). Ignore the warning, — your choice will be saved into .suo hidden file, so the warning will be suppressed next time when you open the solution. Conditions for successful sample build:
MiscellaneousDemoThis sample is designed for library presentation purposes. It demonstrates some aspects of programming with help of the ElementaryExtensions. This is a console application with some advanced elements (the program is based on Simplified Console API). Explore CS-files, probe the sample in action. Screenshot from MiscellaneousDemo sample application:
StrictExceptionsDemoThis sample demonstrates a situation, where we may potentially misidentify an exception of type The program performs some hypothetical X-Calculation (never mind what this word does mean), whose result is fully determined by three input values: Alpha, Beta and Gamma, — real numbers which are placed as command-line arguments for this console application. The example has a place (in the most deep layer of function execution graph), that specially was made as unstable. From time to time (depending on current system time) exception of very general type Using Strict Exceptions Technique we may guarantee that mentioned above situation (exception misidentification) is impossible, — unexpected exception from the depth will not leave strict routine (X-Calculation algorithm) as strict, and we will not misidentify it (because we do analyze its strict flag). The sample has one "unreliable" place (treating of command-line arguments) where we may result some wrong judgment based on exceptions which may issue from For more information about this sample see ElementaryExtensions documentation — ElementaryExtensions.Library.doc file (contains function structure and console output listings). Explore source files (CS). To see hidden side effect perform different tests many times, — there are 3 variants (tests, represented by corresponded CMD batches). Simulated unexpected error does not cause wrong result (as we may have by eliminating strict enhancements). In a case of unexpected exception which is raised from the depth, the output is unexpected but its meaning is fully right. ConsoleCalculatorThis application is a console variant of very simple calculator. The program demonstrates functionality of Simplified Console API. All code occupies nearly 4.5 hundred lines (C#). Supposing to achieve analogous result by coding your own console helpers, you'll need to write much more. Calculation is based on Explore CS-files, probe the sample in action. Screenshot from ConsoleCalculator sample application:
What's later?This is an early version of the library, no warranty about correct functioning is guaranteed. Its key task is not a fluent implementation but the ideas which are represented by the interface (ElementaryExtensions API). I hope that this software will be found helpful for somebody. Waiting for comments about the library and the introduced ideas.
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||