Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Presentation of ElementaryExtensions library, Strict Exceptions and Voronezh Constructions

0.00/5 (No votes)
18 Mar 2010 1  
An article on some advanced programming techniques which are supported by specially designed library - ElementaryExtensions

ElementaryExtensions character/emblem

At present time ElementaryExtensions is superseded by XHelpers Library (available as C# source codes):

Curvostella http://sergcode.net/ ⇐ Visit SergCODE Web-site (my current project)

SergCODE Package (Freeware) contains XHelpers Library and a lot of other interesting things.
Here you may download archive file (sergcode_package.zip) and explore large collection of programming utilities.



Contents

Introduction

This 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.

Background

This 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 programming

Almost 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, and all public types are declared in a managed assemblies — ElementaryExtensions.*.dll. There is also one internal assembly designed for implementation purpose — ElementaryExtensions.Internal.dll (per-platform version: x86/x64), do not try to access it directly. (This module contains those library parts which depend on platform calls, and it performs them in natural manner.)

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 Є-prefix of ЄValueParser class, — such naming style is used for all ElementaryExtensions public types.

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 (!ЄValueParser.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 Є (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 ElementaryExtensions namespace), but the hieroglyph prefixing gives the most obvious effect. Also, sign Є serves as symbol of the product and is associated with the name of the library (words Elementary & Extension).

To enable typing of some special characters including Є, ElementaryExtensions provides the following extended keyboard layouts: KBDUS_X and KBDUSX_X (see library pack).

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 Result

Error 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 data

Almost 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 try-catch-finally block, and provide the code with correct behavior. (UI manipulations may be considered as safe enough, we usually don't guard them.) Those places where exceptions are not really expected will stay non-surrounded. If some unexpected exception will be raised in that place, central exception handler will inform us about it, but UI elements or non-UI data may become formed incorrectly (because of incomplete altering). Further program execution is shady! We may watch such situation in some known modern .NET-based applications (it will not be courteously to list them explicitly). Also some analogous situations may not relate with UI and centralized exception handling.

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!!! (System.ArgumentException, for example, is a very general exception. Therefore there is a high probability to have a mistake about its cause, when it occurs somewhere in the depth of function execution graph.)

Further you'll find a section about so called Strict Exceptions, — an abstraction that was specially designed to solve this non-trivial problem.

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 information

This 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 System.Exception class) has InnerException instance property, which may contain direct exception cause. This property gives a possibility for so called exception chaining (by keeping a link from exception object to a causal exception). But in our situation there are two (ore more) different exception chains. Here is not direct cause of exception, but chronologically previous exception chain. That is why, there is a need in some new property for Exception object, in order to organize concatenation of several exception chains, — PreviousException for example (to provide a link from tail element of particular exception chain to previously raised exception chain).

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 techniques

In 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 System.Environment.FailFast static method. However, we do not have a specially oriented programming infrastructure (something such as Clean Result API).

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 Constructions

In 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 ERROR

Sometimes, 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 ЄFatalError static class. Its overloaded method Raise executes fatal error in one of the two following modes represented by ЄFatalErrorRaisingMode enumeration: TerminateEntireProcess and ThrowFatalErrorException. The first way is takes place by default for the default domain application. (This mode is specified in the default instance of runtime parameters and takes effect after activation of the parameters. See documentation.)

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 Environment.FailFast call (with informative error message). In the second throw-mode — ЄFatalException exception (with informative message) will be thrown in a separate thread (so it may be caught only at the top level).

Finally, when application is terminated by the FailFast call, the message that is passed to this method is saved in the Application Event Log.

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 CONTROL

Ideally, 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 return, break, goto and throw (explicit throwing) may be used to leave the block. Moreover, fully unbreakable region should be implemented as CER (Constrained Exception Region), — in order to prevent asynchronous exception of type System.Threading.ThreadAbourtException (may be initiated by some another thread through System.Threading.Thread.Abort).

This simple abstraction is supported in the ElementaryExtensions by ЄUnbreakableRegion instance class. Look at the code snippets below.

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:
   ЄUnbreakableRegion.Crash(e);
}

Fully unbreakable region (with safe exception throwing, formed as CER), implemented directly in client code:

// Advanced UR region:
ЄUnbreakableRegion ur=new ЄUnbreakableRegion();
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):
ЄUnbreakableRegion.Execute
(
   delegate (ЄUnbreakableRegion 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 ref or out parameters (that is not possible from anonymous method). User's callback may be of the following two types: ЄESimpleCallback (safe throwing is not presumed) and ЄUnbreakableRegionCallback (where you may throw exceptions explicitly — in the so called safe manner).

Safe exception throwing requires corresponding preparation before performing the throw instruction (as shown in the second and third snippets). In such preparation UR-object stores its expected exception object, — then it will not consider it as fatal in the interceptor section (thus, exception will be simply forwarded). But if something unexpected will be caught (unprepared exception), interceptor will invoke fatal error.

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 catch-block, and then our "stainless" logic will misidentify that error, treating it as expected (and will cause an incorrect judging with more or less harmful result). Sole exception type is insufficient information for strict decision.

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: ЄStrictFunction (instance class) and EStrictHelpers (static class). Any exception can be examined, whether it is strict or not, — it is done by EStrictHelpers.IsExceptionStrict static method. Any "strict" decision about the associated problem is based on such checking. (Internally, strict exception flag is implemented in the ElementaryExtensions through a special key of System.Exception.Data property.)

Accessing strict function, analyzing caught exception, checking its strict flag and performing strict judgment:

try
{
   // Accessing strict function:
   StrictFunction();
}
catch (SomeException e)
{
   if (ЄException.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 try-catch block which is based on instance of ЄStrictFunction class. If exception is declared as strict, it may be thrown by the function in a special strict manner. The function performs so called strict throwing / strict re-throwing ONLY when it decides, that the associated problem really takes place. This operation is done through the following instance methods: ЄStrictFunction.PrepareForStrictThrowing / ЄStrictFunction.PrepareForStrictRethrowing, and the throw instruction.

The catch-section at the function bottom (interceptor) serves for marking explicitly thrown/re-thrown exceptions as strict. It is also responsible for preventing strict exception to leave the function as strict, if it was originated in another strict function. Any "implicit" strict exception (thrown somewhere from the depth) will be reconsidered and re-thrown here as non-strict.

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 "ЄStrictFunction" class:
   ЄStrictFunction sf=new ЄStrictFunction();
   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 PrepareForStrictRethrowing instance method (which ignores non-strict exceptions). Such strict re-throwing should be done ONLY if we consider the caught exception as strict THE SAME WAY. We have NOT put calls of non-strict function in such tolerant block, at least those, which potentially may rely on another strict functions (else, we'll break our "strict" strategy).

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 out-parameter. Then, an implementation of strict function may be some easy. However an API that is exposed in such heterogeneous style will be looked and accessed much badly than the uniform view described above (fully based on throw-able exceptions).

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 ISummable, IDividable, or IInvertible.)

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 ЄNumberOperatorAccess and ЄArith static classes you may substitute any value type as parameter for provided generic function/operation, independently of whether the operation is supported by the type or not. Error checking will take effect at runtime, only you are responsible for correctness of your code (compiler can not detect all possible errors here).

For standard CLR number types (such as System.Double or System.Int32) arithmetic operators are realized directly by the compiler (not by framework calls). That is why, for manipulations with these values ElementaryExtensions invokes internal stub functions that are equivalent to operators over built-in types. But all these functions are never tolerant to arithmetic errors (exception is thrown in case of integer overflow).

These are types which are responsible for universal arithmetic operations (you may explore them in the Object Browser, by browsing ElementaryExtensions.Complementary.dll): ЄNumberOperatorType, ЄNumberOperatorArgsType, ЄNumberOperator, ЄNumberOperatorAccess, and ЄArith. Among methods of ЄArith static class there are such generic functions as Oper_Addition, Oper_BitwiseAnd, Oper_LessThanOrEqual, etc. Using them you can universally code common operations, — it will work for any value type that provides corresponded operators (and for any standard CLR number type).

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 System.Console static class. However, if you wish to organize some good looking appearance (different colors, vignette elements, etc.) and/or advanced user interaction (cancellable string input, password masking, different dialogs, etc.) you will have to pay grate efforts projecting a set of helpers. There is no absolutely universal view for so called Console UI, but it would be well to have some general support.

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 ЄConsole static class. The class contains a set of routines for colorized text output, cancellable string input, different dialogs, etc. Of course, this unification is not absolutely general, but such UI looks quite well and may be customized to some extent by programmer's taste.

To apply Simplified Console API you have the following: ЄConsole static class, a set of ЄConsole... satellite types. (Standard .NET class System.Console may also be accessed for some needs.)

Main aspects of Simplified Console API (teletype mode only):

  • Using of predefined color types which may be customized (at application scope)
  • Colorized text output (methods: Print..., Type..., OutToConsole)
  • Keyboard input acceptance routines (methods: AcceptKey, ReadKeyIfAvailable, WaitInput, ResetInput)
  • Cancellable string input routine (methods: InputString, InputStringModify)
  • Different dialogs (methods: Dialog, NumberedListDialog, YesNoDialog, Press...Dialog)

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:

  • .NET Framework 2.0 is required (v2.0.50727)
  • Your PATH variable must include path to .NET version subfolder v2.0.50727, in order to access MSBUILD system from CMD-scripts. (This condition is not required for build inside the IDE.)

MiscellaneousDemo

This 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:

StrictExceptionsDemo

This sample demonstrates a situation, where we may potentially misidentify an exception of type System.ArgumentException, but we do prevent such misidentification with help of the Strict Exceptions and Voronezh Constructions.

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 System.ArgumentException is thrown for test purpose. In the usual coding technique (by supposing that this sample is not enforced with strict functions) this unexpected exception is a subject of misidentification, because another expected problem associated with this exception type exists. A routine (at the middle layer) that is responsible for X-Calculation algorithm is implemented so, that in case of incorrect input values it must report that error by throwing System.ArgumentException exception.

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 System.Double.Parse method. But it is possible only theoretically, if some unexpected situation in the framework will occur.

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.

ConsoleCalculator

This 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 System.Decimal number type. (Power operation is performed with help of System.Double type.) The following arithmetic operations are available: negation, addition, subtraction, multiplication, division, power.

Explore CS-files, probe the sample in action.

Screenshot from ConsoleCalculator sample application:

What's later?

This is purely experimental 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). Visit SergCODE Web-site (my current project): http://sergcode.net/.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here