Hugoware

The product of a web developer with a little too much caffeine

Posts Tagged ‘Enumeration

Quirks Of Using For Each

leave a comment »

Collections can be tricky sometimes when you’re writing code. This is especially true when you need to modify them while you’re looping through them. Let’s look at a couple examples that can get you into trouble.

//create a typical list of items
List<string> items = new List<string> {
    "first", "second", "third", "fourth"
};

//now clear the list
foreach (string item in items) {
    items.Remove(item);
}

Of course, this won’t work. Why?

InvalidOperationException: Collection was modified; enumeration operation may not execute

Once our loop has started the boundaries of the collection have been defined. Adding or removing items will change those boundaries and create errors. Naturally, most languages aren’t going to like this and will crash.

Fortunately, we do get an Exception message (which is the best kind of error message). However, we should be more concerned about subtle problems that could arise if we change a collection while looping through it. For example, consider the ForEach method attached to Generic Lists…

//get a list of the employees that 
//should be deleted from the system
List<string> firedEmployees = new List<string> {
    "jeff", "blake", "steve"
};

//and delete them
firedEmployees.ForEach(employee => {
    EmployeeSystem.Delete(employee);
    firedEmployees.Remove(employee);
});

Everyone deleted? Nope…

I don’t know about you, but I was surprised to find that one employee remained on this list after the code was run (the second employee at that). ForEach made sure to keep track of our collection as it changed but unfortunately it created an undesirable result.

To take it a step further, if you were to add a new item each time then you’d end up with an infinite loop, an error that would be caught sooner but a problem all the same.

This straight forward code has potential for some serious problems.

So, abandon the framework! Move to Java! .NET is doomed!

…Or we can just write an extension method to take care of both problems at the same time.

public static class EnumerableExtensions {

    /// <summary>
    /// Safely enumerates through a collection
    /// </summary>
    public static IEnumerable<T> Each<T>(this IEnumerable<T> collection, Action<T> action) {

        //move the collection into its own list and
        //perform the work on each item
        collection.ToList().ForEach(action);
        return collection;

    }

}

The fix itself isn’t very impressive. One additional call to ToList() and we’re pretty much okay. But this does keep the original collection safe while we enumerate through the protected one.

Advertisements

Written by hugoware

July 22, 2010 at 11:03 pm

Enumeration Extensions 2.0

with 8 comments

A while back I had posted some code about making it easier to work with enumerated types — Especially with the Flags attribute. An experienced programmer won’t have a hard time understanding all the voodoo magic behind the the bitwise operators but for the rest of us then something a little easier to read is always welcome.

The problem with the previous code is that it was created to work with the project I was currently working on so I didn’t put a lot of effort into making it work with multiple types. That said, below is the official version 2.0 of the EnumerationExtensions class.

using System;

namespace Extensions {

    /// <summary>
    /// Extension methods to make working with Enum values easier
    /// </summary>
    public static class EnumerationExtensions {

        #region Extension Methods

        /// <summary>
        /// Includes an enumerated type and returns the new value
        /// </summary>
        public static T Include<T>(this Enum value, T append) {
            Type type = value.GetType();

            //determine the values
            object result = value;
            _Value parsed = new _Value(append, type);
            if (parsed.Signed is long) {
                result = Convert.ToInt64(value) | (long)parsed.Signed;
            }
            else if (parsed.Unsigned is ulong) {
                result = Convert.ToUInt64(value) | (ulong)parsed.Unsigned;
            }

            //return the final value
            return (T)Enum.Parse(type, result.ToString());
        }

        /// <summary>
        /// Removes an enumerated type and returns the new value
        /// </summary>
        public static T Remove<T>(this Enum value, T remove) {
            Type type = value.GetType();

            //determine the values
            object result = value;
            _Value parsed = new _Value(remove, type);
            if (parsed.Signed is long) {
                result = Convert.ToInt64(value) & ~(long)parsed.Signed;
            }
            else if (parsed.Unsigned is ulong) {
                result = Convert.ToUInt64(value) & ~(ulong)parsed.Unsigned;
            }

            //return the final value
            return (T)Enum.Parse(type, result.ToString());
        }

        /// <summary>
        /// Checks if an enumerated type contains a value
        /// </summary>
        public static bool Has<T>(this Enum value, T check) {
            Type type = value.GetType();

            //determine the values
            object result = value;
            _Value parsed = new _Value(check, type);
            if (parsed.Signed is long) {
                return (Convert.ToInt64(value)& (long)parsed.Signed) == (long)parsed.Signed;
            }
            else if (parsed.Unsigned is ulong) {
                return (Convert.ToUInt64(value) & (ulong)parsed.Unsigned) == (ulong)parsed.Unsigned;
            }
            else {
                return false;
            }
        }

        /// <summary>
        /// Checks if an enumerated type is missing a value
        /// </summary>
        public static bool Missing<T>(this Enum obj, T value) {
            return !EnumerationExtensions.Has<T>(obj, value);
        }

        #endregion

        #region Helper Classes

        //class to simplfy narrowing values between 
        //a ulong and long since either value should
        //cover any lesser value
        private class _Value {

            //cached comparisons for tye to use
            private static Type _UInt64 = typeof(ulong);
            private static Type _UInt32 = typeof(long);

            public long? Signed;
            public ulong? Unsigned;

            public _Value(object value, Type type) {

                //make sure it is even an enum to work with
                if (!type.IsEnum) {
                    throw new ArgumentException("Value provided is not an enumerated type!");
                }

                //then check for the enumerated value
                Type compare = Enum.GetUnderlyingType(type);

                //if this is an unsigned long then the only
                //value that can hold it would be a ulong
                if (compare.Equals(_Value._UInt32) || compare.Equals(_Value._UInt64)) {
                    this.Unsigned = Convert.ToUInt64(value);
                }
                //otherwise, a long should cover anything else
                else {
                    this.Signed = Convert.ToInt64(value);
                }

            }

        }

        #endregion

    }

}

This code results in a much easier to read syntax and is mildly better at avoiding type casting issues. Instead of defaulting to int as the other version did, this version attempts to decide between Int64 or UInt64 since either could meet the requirements for any of their lesser counterparts.

Now we can use syntax similar like you see below…

//create the typical object
RegexOptions options = RegexOptions.None;

//Assign a value
options = options.Include(RegexOptions.IgnoreCase); //IgnoreCase

//Or assign multiple values
options = options.Include(RegexOptions.Multiline | RegexOptions.Singleline); //IgnoreCase, Multiline, Singleline

//Remove values from the list
options = options.Remove(RegexOptions.IgnoreCase); //Multiline, Singleline

//Check if a value even exists
bool multiline = options.Has(RegexOptions.Multiline); //true
bool ignoreCase = options.Missing(RegexOptions.IgnoreCase); //true

Anyways, a whole lot easier to read in my opinion. Enjoy!

Written by hugoware

February 23, 2010 at 10:26 pm