Syntactic sugar (C#): Enum



Last week I had the opportunity to work with a rather expensive OCR SDK, one that could have been a great deal more elegant if the creators of the SDK used this sexy piece of syntactic chocolate, syntactic steak, syntac... uh I think you get the picture, observe the following extract.

 
public class OCRContext : IDisposable
{
	public OCRContext(int iLanguage)
	{
 

There are two things I dislike about the constructor in the preceding snippet:
  1. Naming convention - iLanguage? Are you serious? This is what is called hungarian notation, the i describes the type of the variable, in this case its trying to tell us that language is an integer.

    Personally I feel that this convention doesn't have any place in a strongly typed language like C#.

  2. Use of a magic number - we need to pass an integer to the constructor, which tells the class which language to use, but what number represents English for example? Ideally we don't want someone to dig through a thousand page manual for something so trivial.

    This "magic number" convention can lead to all kinds of confusion, observe the if condition below:

     
    if ((iLanguage == 17) || (iLanguage == 2))
    {
    	// Do some funky stuff
    }
     


    Not all that clear from this snippet that 17 represents polish, while 2 is supposedly french...

But wait, it gets a little bit more interesting, the developer included the following crude class:

 
public class Languages
{
	public const int Afrikaans = 33;
	public const int English = 0;
 

This is somewhat helpful, but it would have been nicer if we had a programmatic relation enforcing this class (telling us where we can find our languages), providing some kind of constraint at the same time.

No enumerator
Like you can see from preceding image, we still need to pass an int value, nothing is preventing or telling us to do something else.

This is where our syntactic sugar in question comes into play, we can group our constants into a neat strongly typed little box of values, which constrains us to what we can send to the parameter and leads the developer to the list of possible constants.

 
public class OCRContext : IDisposable
{
	public OCRContext(Languages language) 
	{
	// something
	}
	// etc..
}
 
// Poorly designed enum don't do this, read on..
public enum Languages
{
	English,
	Afrikaans
}
 

Enumerator

Obviously (like with everything else), we can misuse this piece of sugar ending up with a system suffering from hyperglycemia.

Seeing as the underlying type of our enum is an int (can also use other integral types) it can lead to some interesting issues when using an enum as bit flags via bitwise operators.

Have a look at the following snippet (using the Languages enum in this post):

 
Languages languages = Languages.Afrikaans | Languages.English;
 
if (languages == Languages.Afrikaans)
    Console.WriteLine("Afrikaans");
 
 
if (languages == Languages.English)
    Console.WriteLine("English");
 
 
if (languages == (Languages.Afrikaans | Languages.English))
    Console.WriteLine("AfrikaansEnglish");
 

(Note: When an enumeration is to be used in bitwise operations, be sure to set the flags attribute)

Which produces the following:

Broken Enumeration

What happend here exactly? Why did the first condition validate as true?

Well, its rather simple, if we don't provide enums with values, each member will simply be incremented starting by 0, so the value behind Languages.English is 0 - which technically means we didn't really add English to the equation.

To solve this issue you can add values to your members like seen below.

 
public enum Languages
{
	English = 1,
	Afrikaans = 2
}
 


Or according to best practices rather provide a sensible default value.

 
public enum Languages
{
	None,
	English,
	Afrikaans
}
 


There are a few other valuable design guidelines provided by microsoft (not going to go into too much detail in this post) with regards to enumeration, which you can read more about over here.

There is one last tip I want to give you before I send you on your way.

Lets assume that you did infact design your class like the one seen at the beginning of this post and you want to use an enum instead, but unfortunately you've got a billion people using your class already, you can simply use the obsolete attribute like seen in the following snippet.

 
class OCRContext
{
    [Obsolete("Rather use OCRContext(Languages language)")]
    public OCRContext(int iLanguage)
    {
        // some code
    }
    public OCRContext(Languages language)
    {
        // some code
    }
}
 


If the developer tries to use the old constructor, he will get the following warning in his IDE (assuming he's using Visual Studio):

Obsolete

Additional reading

Enumeration Design
Enumeration Types (C# Programming Guide)
When to Use an Enumeration





Post/View comments
 

OCR (C#) : IRIS (iDRS 14.0) Part 2 - Writing some code



In part 1 we had a look at how to get the OCR "engine" up and running, in this post we're going have a look at how to use the SDK itself.

Under the samples folder of the SDK supplied by IRIS you'll find some C++/C# and VB.net examples, but I feel these samples are too bulky, so in this post I am going to show you in just a few lines of code the basics of using this SDK.

Observe the following snippet:

 
using IDRSNET;
 
static string ImageToText(String imagePath)
{
    using (OCRContext context = new OCRContext(Languages.English))
    using (Reader reader = new Reader())
    {
        reader.SetOCRParameters(context);
        reader.SetOrientationDetectionParameters(true, true);
 
        using (Page page = new Page())
        {
            using (Doc doc = new Doc())
            {
                page.LoadSourceImage(imagePath);
                reader.ReadFmt(page);
                DocPage docPage = doc.ConvertPage(page);
                return docPage.GetText();
            }
        }
    }
}		
 


Nothing much to explain here, we pass a language to the OCRContext, which we pass to our Reader class, set a few parameters and pass an image to the page class.

You will notice however that the GetText method on the DocPage class doesn't actually exist, I added this as extension method to simplify retrieving text from a DocPage object, this is what the method looks like:

 
static class Extensions
{
    public static String GetText(this DocPage docPage)
    {
        using (StringWriter sw = new StringWriter())
        {
            foreach (DocZone zone in docPage.Zones)
            {
                foreach (DocLine line in zone.Lines)
                    foreach (DocWord word in line.Words)
                    {
                        foreach (DocBlob blob in word.Blobs)
                            sw.Write(blob.Text);
                        sw.Write(" ");                            
                    }
                sw.WriteLine();
            }
            return sw.ToString();
        }
    }
}		
 


In the following snippet we pass images to the ImageToText method and save the output to a text file, notice the IDRSLicenses class:

 
static void Main(string[] args)
{
    using (IDRSLicenses licenses = new IDRSLicenses())
    {
        FileInfo[] files = new DirectoryInfo(@"c:\ocr\temp").GetFiles("*.jpg");
        using (StreamWriter sw = new StreamWriter(@"c:\ocr\text.txt"))
        {
            foreach (FileInfo file in files)
            {
                string content = ImageToText(file.FullName);
                sw.WriteLine(content);
            }
        }
    }
}		
 


The IDRSLicenses class is a class I wrote to simplify the licensing needed to use these classes, observe:

 
class IDRSLicenses : IDisposable
{
    LicenseDRS _licenseDRS = new LicenseDRS();
    LicensePreprocessing _licensePrepro = new LicensePreprocessing();
    LicenseFormatting _licenseFmt = new LicenseFormatting();
    LicenseImageFile _licenseImageFile = new LicenseImageFile();
 
    public IDRSLicenses()
    {
        SetupKeys();
        SetupModules();
    }
 
	// Retrieve licenses from app.config
    private void SetupKeys()
    {
        _licenseDRS.LicenseType = _licensePrepro.LicenseType = _licenseFmt.LicenseType = _licenseImageFile.LicenseType = LicenseTypes.Software;
        _licenseDRS.SoftwareKey = ConfigurationManager.AppSettings["LicenseDRS"];
        _licenseDRS.ResourcesPath = ConfigurationManager.AppSettings["ResourcesPath"];
        _licensePrepro.SoftwareKey = ConfigurationManager.AppSettings["LicensePreprocessing"]; ;
        _licenseFmt.SoftwareKey = ConfigurationManager.AppSettings["LicenseFormatting"];
        _licenseImageFile.SoftwareKey = ConfigurationManager.AppSettings["LicenseImageFile"];
        _licenseImageFile.ExtensionsToLoad[ImageFileExtensions.JPEG] = true;
        _licenseImageFile.ExtensionsSoftwareKeys[ImageFileExtensions.JPEG] = ConfigurationManager.AppSettings["ImageFileExtensions.JPEG"];
    }
 
    private void SetupModules()
    {
        IDRSManager.SetupModule(ModuleTypes.DRS, _licenseDRS);
        IDRSManager.SetupModule(ModuleTypes.Preprocessing, _licensePrepro);
        IDRSManager.SetupModule(ModuleTypes.Formatting, _licenseFmt);
        IDRSManager.SetupModule(ModuleTypes.ImageFile, _licenseImageFile);
    }
 
	// We need to unload the IDRSManager once we're done, else we get an ugly exception
    public void Dispose()
    {
        IDRSManager.Unload();
    }
}		
 


Just one last rant before I sign off and leave you to fend for yourself and that is about the IDRSException class or should I rather say glorified error code handler?

I really hate the design of this class, if any exception occurs you simply get a message telling you "An error occured in the iDRS" along with an error code (in the IDRSErrorCode property of the exception) which you need to reference in the documentation provided with the SDK.

It would have been a great deal more elegant if the message contained the actual message relating to the error, perhaps consider adding an extension method mapping the error code to an actual useful message like seen in the following snippet:

 
public static String GetMessage(this IDRSException ex)
{
    switch (ex.IDRSErrorCode)
    {
        case 6005:
            return "Cannot talk to the license server on the specified host. The license server is not running.";
        case 6092:
            return "The given license code is invalid. Hence, it could not be added to this license server.";
    }
    return ex.Message;
}
 





Post/View comments
 
First 1 2 3 4 5 6 7 8 9 10 Last / 62 Pages (124 Entries)

Latest Posts

Be the best stalker you can be


2011-12-13 22:33:54

Syntactic sugar (C#): Enum


2011-08-04 16:50:18

Top 5 posts

Simple WYSIWYG Editor


Creating a WYSIWYG textbox for your website is actually quite simple.
2007-02-01 12:00:00

Moving items between listboxes in ASP.net/PHP example


Move items between two listboxes in ASP.net(C#, VB.NET) and PHP
2008-06-12 17:07:43

Cross Browser Issues: Firefox Word Wrapping


Firefox word wrapping issues
2008-06-09 09:51:21

Populate a TreeView Control C#


Populate a TreeView control in a windows application.
2009-08-27 16:01:03

C# YouTube : Google API


Post on how to integrate with YouTube using the Google Data API
2011-03-12 08:37:51