Simple solution to get a MIME type from a file extension. Source –> http://stackoverflow.com/a/7161265/341706

public static class MIMEAssistant
{
  private static readonly Dictionary<string, string> MIMETypesDictionary = new Dictionary<string, string>
  {
    {"ai", "application/postscript"},
    {"aif", "audio/x-aiff"},
    {"aifc", "audio/x-aiff"},
    {"aiff", "audio/x-aiff"},
    {"asc", "text/plain"},
    {"atom", "application/atom+xml"},
    {"au", "audio/basic"},
    {"avi", "video/x-msvideo"},
    {"bcpio", "application/x-bcpio"},
    {"bin", "application/octet-stream"},
    {"bmp", "image/bmp"},
    {"cdf", "application/x-netcdf"},
    {"cgm", "image/cgm"},
    {"class", "application/octet-stream"},
    {"cpio", "application/x-cpio"},
    {"cpt", "application/mac-compactpro"},
    {"csh", "application/x-csh"},
    {"css", "text/css"},
    {"dcr", "application/x-director"},
    {"dif", "video/x-dv"},
    {"dir", "application/x-director"},
    {"djv", "image/vnd.djvu"},
    {"djvu", "image/vnd.djvu"},
    {"dll", "application/octet-stream"},
    {"dmg", "application/octet-stream"},
    {"dms", "application/octet-stream"},
    {"doc", "application/msword"},
    {"docx","application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
    {"dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"},
    {"docm","application/vnd.ms-word.document.macroEnabled.12"},
    {"dotm","application/vnd.ms-word.template.macroEnabled.12"},
    {"dtd", "application/xml-dtd"},
    {"dv", "video/x-dv"},
    {"dvi", "application/x-dvi"},
    {"dxr", "application/x-director"},
    {"eps", "application/postscript"},
    {"etx", "text/x-setext"},
    {"exe", "application/octet-stream"},
    {"ez", "application/andrew-inset"},
    {"gif", "image/gif"},
    {"gram", "application/srgs"},
    {"grxml", "application/srgs+xml"},
    {"gtar", "application/x-gtar"},
    {"hdf", "application/x-hdf"},
    {"hqx", "application/mac-binhex40"},
    {"htm", "text/html"},
    {"html", "text/html"},
    {"ice", "x-conference/x-cooltalk"},
    {"ico", "image/x-icon"},
    {"ics", "text/calendar"},
    {"ief", "image/ief"},
    {"ifb", "text/calendar"},
    {"iges", "model/iges"},
    {"igs", "model/iges"},
    {"jnlp", "application/x-java-jnlp-file"},
    {"jp2", "image/jp2"},
    {"jpe", "image/jpeg"},
    {"jpeg", "image/jpeg"},
    {"jpg", "image/jpeg"},
    {"js", "application/x-javascript"},
    {"kar", "audio/midi"},
    {"latex", "application/x-latex"},
    {"lha", "application/octet-stream"},
    {"lzh", "application/octet-stream"},
    {"m3u", "audio/x-mpegurl"},
    {"m4a", "audio/mp4a-latm"},
    {"m4b", "audio/mp4a-latm"},
    {"m4p", "audio/mp4a-latm"},
    {"m4u", "video/vnd.mpegurl"},
    {"m4v", "video/x-m4v"},
    {"mac", "image/x-macpaint"},
    {"man", "application/x-troff-man"},
    {"mathml", "application/mathml+xml"},
    {"me", "application/x-troff-me"},
    {"mesh", "model/mesh"},
    {"mid", "audio/midi"},
    {"midi", "audio/midi"},
    {"mif", "application/vnd.mif"},
    {"mov", "video/quicktime"},
    {"movie", "video/x-sgi-movie"},
    {"mp2", "audio/mpeg"},
    {"mp3", "audio/mpeg"},
    {"mp4", "video/mp4"},
    {"mpe", "video/mpeg"},
    {"mpeg", "video/mpeg"},
    {"mpg", "video/mpeg"},
    {"mpga", "audio/mpeg"},
    {"ms", "application/x-troff-ms"},
    {"msh", "model/mesh"},
    {"mxu", "video/vnd.mpegurl"},
    {"nc", "application/x-netcdf"},
    {"oda", "application/oda"},
    {"ogg", "application/ogg"},
    {"pbm", "image/x-portable-bitmap"},
    {"pct", "image/pict"},
    {"pdb", "chemical/x-pdb"},
    {"pdf", "application/pdf"},
    {"pgm", "image/x-portable-graymap"},
    {"pgn", "application/x-chess-pgn"},
    {"pic", "image/pict"},
    {"pict", "image/pict"},
    {"png", "image/png"}, 
    {"pnm", "image/x-portable-anymap"},
    {"pnt", "image/x-macpaint"},
    {"pntg", "image/x-macpaint"},
    {"ppm", "image/x-portable-pixmap"},
    {"ppt", "application/vnd.ms-powerpoint"},
    {"pptx","application/vnd.openxmlformats-officedocument.presentationml.presentation"},
    {"potx","application/vnd.openxmlformats-officedocument.presentationml.template"},
    {"ppsx","application/vnd.openxmlformats-officedocument.presentationml.slideshow"},
    {"ppam","application/vnd.ms-powerpoint.addin.macroEnabled.12"},
    {"pptm","application/vnd.ms-powerpoint.presentation.macroEnabled.12"},
    {"potm","application/vnd.ms-powerpoint.template.macroEnabled.12"},
    {"ppsm","application/vnd.ms-powerpoint.slideshow.macroEnabled.12"},
    {"ps", "application/postscript"},
    {"qt", "video/quicktime"},
    {"qti", "image/x-quicktime"},
    {"qtif", "image/x-quicktime"},
    {"ra", "audio/x-pn-realaudio"},
    {"ram", "audio/x-pn-realaudio"},
    {"ras", "image/x-cmu-raster"},
    {"rdf", "application/rdf+xml"},
    {"rgb", "image/x-rgb"},
    {"rm", "application/vnd.rn-realmedia"},
    {"roff", "application/x-troff"},
    {"rtf", "text/rtf"},
    {"rtx", "text/richtext"},
    {"sgm", "text/sgml"},
    {"sgml", "text/sgml"},
    {"sh", "application/x-sh"},
    {"shar", "application/x-shar"},
    {"silo", "model/mesh"},
    {"sit", "application/x-stuffit"},
    {"skd", "application/x-koan"},
    {"skm", "application/x-koan"},
    {"skp", "application/x-koan"},
    {"skt", "application/x-koan"},
    {"smi", "application/smil"},
    {"smil", "application/smil"},
    {"snd", "audio/basic"},
    {"so", "application/octet-stream"},
    {"spl", "application/x-futuresplash"},
    {"src", "application/x-wais-source"},
    {"sv4cpio", "application/x-sv4cpio"},
    {"sv4crc", "application/x-sv4crc"},
    {"svg", "image/svg+xml"},
    {"swf", "application/x-shockwave-flash"},
    {"t", "application/x-troff"},
    {"tar", "application/x-tar"},
    {"tcl", "application/x-tcl"},
    {"tex", "application/x-tex"},
    {"texi", "application/x-texinfo"},
    {"texinfo", "application/x-texinfo"},
    {"tif", "image/tiff"},
    {"tiff", "image/tiff"},
    {"tr", "application/x-troff"},
    {"tsv", "text/tab-separated-values"},
    {"txt", "text/plain"},
    {"ustar", "application/x-ustar"},
    {"vcd", "application/x-cdlink"},
    {"vrml", "model/vrml"},
    {"vxml", "application/voicexml+xml"},
    {"wav", "audio/x-wav"},
    {"wbmp", "image/vnd.wap.wbmp"},
    {"wbmxl", "application/vnd.wap.wbxml"},
    {"wml", "text/vnd.wap.wml"},
    {"wmlc", "application/vnd.wap.wmlc"},
    {"wmls", "text/vnd.wap.wmlscript"},
    {"wmlsc", "application/vnd.wap.wmlscriptc"},
    {"wrl", "model/vrml"},
    {"xbm", "image/x-xbitmap"},
    {"xht", "application/xhtml+xml"},
    {"xhtml", "application/xhtml+xml"},
    {"xls", "application/vnd.ms-excel"},                        
    {"xml", "application/xml"},
    {"xpm", "image/x-xpixmap"},
    {"xsl", "application/xml"},
    {"xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
    {"xltx","application/vnd.openxmlformats-officedocument.spreadsheetml.template"},
    {"xlsm","application/vnd.ms-excel.sheet.macroEnabled.12"},
    {"xltm","application/vnd.ms-excel.template.macroEnabled.12"},
    {"xlam","application/vnd.ms-excel.addin.macroEnabled.12"},
    {"xlsb","application/vnd.ms-excel.sheet.binary.macroEnabled.12"},
    {"xslt", "application/xslt+xml"},
    {"xul", "application/vnd.mozilla.xul+xml"},
    {"xwd", "image/x-xwindowdump"},
    {"xyz", "chemical/x-xyz"},
    {"zip", "application/zip"}
  };

  public static string GetMIMEType(string fileName)
  {
    if (Path.GetExtension(fileName).Length > 1 && MIMETypesDictionary.ContainsKey(Path.GetExtension(fileName).Remove(0, 1)))
    {
      return MIMETypesDictionary[Path.GetExtension(fileName).Remove(0, 1)];
    }
    return "unknown/unknown";
  }
}

Unity comes with a helper method for generating a unique asset paths called AssetDatabase.GenerateUniqueAssetPath. The documentation for this method is rather minimal but the code example below should help you understand how to use it a little better.

GenerateUniqueAssetPath

[MenuItem("CBX/Test")]
public static void Test()
{
    // outputs "Assets/Rules.xml" if "Assets/Rules.xml" file does not already exist.
    // if "Assets/Rules.xml" already exists it outputs "Assets/Rules 1.xml"  
    Debug.Log(AssetDatabase.GenerateUniqueAssetPath("Assets/Rules.xml"));

    // expects path to start with "Assets/" & outputs a console error in the console "pathName.find("assets/") == 0"  
    // then outputs "Rules.xml" 
    Debug.Log(AssetDatabase.GenerateUniqueAssetPath("Rules.xml")); 
}

I needed a quick file caching solution and here is what I came up with …

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;

    /// <summary>
    /// Provides a file model used by the <see cref="FileCache"/> type.
    /// </summary>
    public class FileModel
    {
        /// <summary>
        /// Gets or sets information about the file.
        /// </summary>
        public FileInfo Info { get; set; }

        /// <summary>
        /// Gets or sets the last time that the file had it's contents cached.
        /// </summary>
        public DateTime LastUpdate { get; set; }

        /// <summary>
        /// Gets or sets the file content for the file.
        /// </summary>
        public StringBuilder  Data { get; set; }
    }

    /// <summary>
    /// Provides a caching for file content based on file extensions.
    /// </summary>
    public class FileCache
    {
        /// <summary>
        /// Holds a singleton instance of a <see cref="FileCache"/> type.
        /// </summary>
        private static FileCache singleton;

        /// <summary>
        /// Used to hold the contents of the files.
        /// </summary>
        private readonly Dictionary<string, FileModel> files;

        /// <summary>
        /// Holds the list of extensions for files that will have there contents cached.
        /// </summary>
        private readonly List<string> extensions;

        /// <summary>
        /// Gets or sets a time value used to determine if a files content should be read again.
        /// </summary>
        public TimeSpan CacheTime { get; set; }

        /// <summary>
        /// Gets a list of file extension that will have there contents cached.
        /// </summary>
        public List<string> Extensions
        {
            get
            {
                return this.extensions;
            }
        }

        /// <summary>
        /// Default constructor.
        /// </summary>
        public FileCache()
        {
            this.files = new Dictionary<string, FileModel>();
            this.extensions = new List<string>();
            this.extensions.AddRange(new[] { ".htm" });
            this.CacheTime = TimeSpan.FromSeconds(2);
        }

        /// <summary>
        /// Provides a helper method for retrieving a file.
        /// </summary>
        /// <param name="path">The path to the file.</param>
        /// <returns>Returns a <see cref="FileModel"/> type containing information about the file.</returns>
        public static FileModel GetFile(string path)
        {
            return Instance.Get(path);
        }

        /// <summary>
        /// Provides a helper method for retrieving a file.
        /// </summary>
        /// <param name="path">The path to the file.</param>
        /// <returns>Returns a <see cref="FileModel"/> type containing information about the file.</returns>
        public FileModel Get(string path)
        {
            // if file does not exit just return null
            if (!File.Exists(path))
            {
                return null;
            }

            // check if file already exists in the cache
            FileModel model;
            if (this.files.ContainsKey(path))
            {
                // get existing entry
                model = this.files[path];
            }
            else
            {
                // add new entry
                model = new FileModel { LastUpdate = DateTime.Now, Info = new FileInfo(path) };
                this.files.Add(path, model);
            }

            // check to cache file data
            if (this.extensions.Contains(model.Info.Extension))
            {
                // not data has been read yet OR (the current time has surpassed the last update time plus the cache time AND 
                // the current time is greater then the last write time  ) we can read the contents of the file
                if (model.Data == null || (DateTime.Now > model.LastUpdate + this.CacheTime && DateTime.Now > model.Info.LastWriteTime))
                {
                    model.Data = new StringBuilder(File.ReadAllText(model.Info.FullName));
                }
            }

            // return the modal data
            return model;
        }

        /// <summary>
        /// Gets a singleton instance of a <see cref="FileCache"/> type.
        /// </summary>
        public static FileCache Instance
        {
            get
            {
                return singleton ?? (singleton = new FileCache());
            }
        }
    }

Below is a code sample containing drawing methods for drawing check boxes or any control type in a grid based layout similar to the SelectionGrid.

 
    using System;

    using UnityEngine;

    public class ControlGrid
    {
        /// <summary>
        /// Draw a grid of check boxes similar to SelectionGrid.
        /// </summary>
        /// <param name="checkedValues">Specifies the checked values of the check boxes.</param>
        /// <param name="text">The content for each individual check box.</param>
        /// <param name="columns">The number of columns in the grid.</param>
        /// <param name="style">The style to be applied to each check box.</param>
        /// <param name="options">Specifies layout options to be applied to each check box.</param>
        /// <returns>Returns the checked state for each check box.</returns>
        /// <remarks><p>Check boxes are drawn top to bottom, left to right.</p>  </remarks>
        /// <exception cref="IndexOutOfRangeException">Can occur if the size of the array is too small.</exception>
        public static bool[] DrawCheckBoxGrid(bool[] checkedValues, string[] text, int columns, GUIStyle style, params GUILayoutOption[] options)
        {
            // convert string content into gui content
            var content = new GUIContent[text.Length];
            for (var i = 0; i < content.Length; i++)
            {
                content[i] = new GUIContent(text[i]);
            }
            return DrawCheckBoxGrid(checkedValues, content, columns, style, options);
        }

        /// <summary>
        /// Draw a grid of check boxes similar to SelectionGrid.
        /// </summary>
        /// <param name="checkedValues">Specifies the checked values of the check boxes.</param>
        /// <param name="textures">The content for each individual check box.</param>
        /// <param name="columns">The number of columns in the grid.</param>
        /// <param name="style">The style to be applied to each check box.</param>
        /// <param name="options">Specifies layout options to be applied to each check box.</param>
        /// <returns>Returns the checked state for each check box.</returns>
        /// <remarks><p>Check boxes are drawn top to bottom, left to right.</p>  </remarks>
        /// <exception cref="IndexOutOfRangeException">Can occur if the size of the array is too small.</exception>
        public static bool[] DrawCheckBoxGrid(bool[] checkedValues, Texture2D[] textures, int columns, GUIStyle style, params GUILayoutOption[] options)
        {
            // convert texture content into gui content
            var content = new GUIContent[textures.Length];
            for (var i = 0; i < content.Length; i++)
            {
                content[i] = new GUIContent(string.Empty, textures[i]);
            }
            return DrawCheckBoxGrid(checkedValues, content, columns, style, options);
        }

        /// <summary>
        /// Draw a grid of check boxes similar to SelectionGrid.
        /// </summary>
        /// <param name="checkedValues">Specifies the checked values of the check boxes.</param>
        /// <param name="content">The content for each individual check box.</param>
        /// <param name="columns">The number of columns in the grid.</param>
        /// <param name="style">The style to be applied to each check box.</param>
        /// <param name="options">Specifies layout options to be applied to each check box.</param>
        /// <returns>Returns the checked state for each check box.</returns>
        /// <remarks><p>Check boxes are drawn top to bottom, left to right.</p>  </remarks>
        /// <exception cref="IndexOutOfRangeException">Can occur if the size of the array is too small.</exception>
        public static bool[] DrawCheckBoxGrid(bool[] checkedValues, GUIContent[] content, int columns, GUIStyle style, params GUILayoutOption[] options)
        {
            return DrawGenericGrid((e, i, s, o) => GUILayout.Toggle(e[i], content[i], style, options), checkedValues, content, columns, style, options);
        }

        /// <summary>
        /// Draw a grid of controls using a draw callback similar to SelectionGrid.
        /// </summary>
        /// <param name="drawCallback">Specifies a draw callback that is responsible for performing the actual drawing.</param>
        /// <param name="values">Specifies the values of the controls.</param>
        /// <param name="content">The content for each individual control.</param>
        /// <param name="columns">The number of columns in the grid.</param>
        /// <param name="style">The style to be applied to each control.</param>
        /// <param name="options">Specifies layout options to be applied to each control.</param>
        /// <returns>Returns the value for each control.</returns>
        /// <remarks><p>Controls are drawn top to bottom, left to right.</p>  </remarks>
        /// <exception cref="IndexOutOfRangeException">Can occur if the size of the array is too small.</exception>
        /// <exception cref="ArgumentNullException">If the drawCallback is null.</exception>
        public static T[] DrawGenericGrid<T>(Func<T[], int, GUIStyle, GUILayoutOption[], T> drawCallback, T[] values, GUIContent[] content, int columns, GUIStyle style, params GUILayoutOption[] options)
        {
            if (drawCallback == null)
            {
                throw new ArgumentNullException("drawCallback");
            }

            GUILayout.BeginVertical();
            var rowIndex = 0;
            var columnIndex = 0;
            var index = rowIndex * columns + columnIndex;

            GUILayout.BeginHorizontal();
            while (index < values.Length)
            {
                // draw control
                values[index] = drawCallback(values, index, style, options);

                // move to next column
                columnIndex++;

                // if passed max columns move down to next row and set to first column
                if (columnIndex > columns - 1)
                {
                    columnIndex = 0;
                    rowIndex++;

                    // remember to start a new horizontal layout
                    GUILayout.EndHorizontal();
                    GUILayout.BeginHorizontal();
                }

                // re-calculate the index
                index = rowIndex * columns + columnIndex;
            }
            GUILayout.EndHorizontal();

            GUILayout.EndVertical();
            return values;
        }
    } 

Here is a simple example of how to use the draw methods

var emptyContent = new[] { GUIContent.none, GUIContent.none, GUIContent.none, 
                            GUIContent.none, GUIContent.none, GUIContent.none, 
                            GUIContent.none, GUIContent.none, GUIContent.none };

GUILayout.BeginVertical(GUILayout.MinWidth(128));
rule.IgnoreUpper = GUILayout.Toggle(rule.IgnoreUpper, "Upper Neighbors");
rule.NeighborsUpper = ControlGrid.DrawCheckBoxGrid(rule.NeighborsUpper, emptyContent, 3, GUI.skin.button, GUILayout.MaxWidth(32), GUILayout.MaxHeight(32));
GUILayout.EndVertical();

ControlGrid


MEFHelpers provides a helper class that provides a static method to make MEF composition easier.

    /// <summary>
    /// Provides a helper class for MEF composition.
    /// </summary>
    public sealed class MEFHelpers
    {
        /// <summary>
        /// Composes MEF parts.
        /// </summary>
        /// <param name="parts">The composable object parts.</param>
        public static void Compose(params object[] parts)
        {
            Compose(parts, new string[0]);
        }

        /// <summary>
        /// Composes MEF parts.
        /// </summary>
        /// <param name="searchFolders">Provides a series of search folders to search for *.dll files.</param>
        /// <param name="parts">The composable object parts.</param>
        public static void Compose(IEnumerable<string> searchFolders, params object[] parts)
        {
            // setup composition container
            var catalog = new AggregateCatalog();

            // check if folders were specified
            if (searchFolders != null)
            {
                // add search folders
                foreach (var folder in searchFolders.Where(System.IO.Directory.Exists))
                {
                    catalog.Catalogs.Add(new DirectoryCatalog(folder, "*.dll"));
                }
            }

            // compose and create plug ins
            var composer = new CompositionContainer(catalog);
            composer.ComposeParts(parts);
        }
    }

A usage scenario is provided below

    public class KissCSMEFComposer  
    {
        [ImportMany(typeof(IProcessor))]
        public List<Lazy<IProcessor>> StringProcessors;

        [ImportMany(typeof(ICommandRepository))]
        public List<Lazy<ICommandRepository>> CommandRepository;
    }

    var composer = new KissCSMEFComposer();
    // try to connect with MEF types
    try
    {
        MEFHelpers.Compose(this.SearchFolders, composer);
    }
    catch (Exception ex)
    {
        // ERR: handle error
    }

    // register string processors
    foreach (var processor in composer.StringProcessors)
    {
        StringProcessorRepository.Instance.Register(processor.Value);
    }

    // etc ...

The code below can be used to log performance of your code.

    using System.Collections.Generic;
    using System.Diagnostics;

    /// <summary>
    /// Provides a simple performance testing class that utilizes <see cref="Stopwatch"/>.
    /// </summary>
    /// <typeparam name="T">The type that will be used as the indexer.</typeparam>
    public class PerformanceTesting<T>
    {
        /// <summary>
        /// Provides a model that contains timer information.
        /// </summary>
        private class TimerModel
        {
            /// <summary>
            /// Used to record performance timings.
            /// </summary>
            public readonly Stopwatch Timer;

            /// <summary>
            /// Used to record how many times the <see cref="Timer"/> has been started.
            /// </summary>
            public int Count;

            /// <summary>
            /// Used to store the enabled state.
            /// </summary>
            private bool enabled;

            /// <summary>
            /// Gets or sets a value whether or not this timer if enabled.
            /// </summary>
            /// <remarks>If <see cref="Timer"/> has been started and Enabled is set to false the timer will be stopped.</remarks>
            public bool Enabled
            {
                get
                {
                    return this.enabled;
                }

                set
                {
                    this.enabled = value;

                    // be sure to stop the timer if disabled
                    if (!value && this.Timer.IsRunning)
                    {
                        this.Timer.Stop();
                    }
                }
            }

            /// <summary>
            /// Default constructor.
            /// </summary>
            public TimerModel()
            {
                this.Timer = new Stopwatch();
                this.enabled = true;
            }
        }

        /// <summary>
        /// Holds a reference to a singleton instance.
        /// </summary>
        private static PerformanceTesting<T> singleton;

        /// <summary>
        /// Used to store various timer information.
        /// </summary>
        private readonly Dictionary<T, TimerModel> timers;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public PerformanceTesting()
        {
            this.timers = new Dictionary<T, TimerModel>();
        }

        /// <summary>
        /// Creates a new timer.
        /// </summary>
        /// <param name="key">The unique key for the timer.</param>
        /// <remarks>It is best to create the timer at the start of the application getting it ready for use.</remarks>
        public void Create(T key)
        {
            this.timers.Add(key, new TimerModel());
        }

        /// <summary>
        /// Creates a new timer for each key.
        /// </summary>
        /// <param name="keys">The unique keys for the timers.</param>
        /// <remarks>It is best to create the timer at the start of the application getting it ready for use.</remarks>
        public void Create(T[] keys)
        {
            foreach (var key in keys)
            {
                this.timers.Add(key, new TimerModel());
            }
        }

        /// <summary>
        /// Returns the total ticks that this timer has observed.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        /// <returns>Returns a time value in ticks.</returns>
        public long TotalTicks(T key)
        {
            var model = this.timers[key];
            return model.Timer.ElapsedTicks;
        }

        /// <summary>
        /// Returns the total ticks that this timer has observed.
        /// </summary>
        /// <param name="keys">The keys to the timers information.</param>
        /// <returns>Returns the sum of the time values in ticks.</returns>
        public long TotalTicks(T[] keys)
        {
            long total = 0;
            foreach (var key in keys)
            {
                var model = this.timers[key];
                total += model.Timer.ElapsedTicks;
            }

            return total;
        }

        /// <summary>
        /// Gets the start count.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        /// <returns>The number of times the timer has started.</returns>
        public int GetStartCount(T key)
        {
            var model = this.timers[key];
            return model.Count;
        }

        /// <summary>
        /// Gets the start count for specified timers.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        /// <returns>The sum total of times all specified the timers have started.</returns>
        public int GetStartCount(T[] keys)
        {
            int total = 0;
            foreach (var key in keys)
            {
                var model = this.timers[key];
                total += model.Count;
            }

            return total;
        }

        /// <summary>
        /// Returns the total ticks that this timer has observed.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        /// <returns>Returns a time value in milliseconds.</returns>
        public long TotalMilliseconds(T key)
        {
            var model = this.timers[key];
            return model.Timer.ElapsedMilliseconds;
        }

        /// <summary>
        /// Returns the total ticks that this timer has observed.
        /// </summary>
        /// <param name="keys">The keys to the timers information.</param>
        /// <returns>Returns the sum of the time values in milliseconds.</returns>
        public long TotalMilliseconds(T[] keys)
        {
            long total = 0;
            foreach (var key in keys)
            {
                var model = this.timers[key];
                total += model.Timer.ElapsedMilliseconds;
            }

            return total;
        }

        /// <summary>
        /// Calculates the average time in ticks that elapsed while this timer was recording.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        /// <returns>Returns the average time in ticks that elapsed between each start and stop.</returns>
        public long AverageTicks(T key)
        {
            var model = this.timers[key];
            return model.Timer.ElapsedTicks / model.Count;
        }

        /// <summary>
        /// Calculates the total average time in ticks that elapsed while the specified timers were recording.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        /// <returns>Returns the total average time in ticks that elapsed between each start and stop for all the specified timers.</returns>
        public long AverageTicks(T[] keys)
        {
            long total = 0;
            var count = 0;
            foreach (var key in keys)
            {
                var model = this.timers[key];
                total += model.Timer.ElapsedTicks;
                count += model.Count;
            }

            return total / count;
        }

        /// <summary>
        /// Calculates the average time in milliseconds that elapsed while this timer was recording.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        /// <returns>Returns the average time in milliseconds that elapsed between each start and stop.</returns>
        public long AverageMilliseconds(T key)
        {
            var model = this.timers[key];
            if (model.Count == 0)
            {
                return 0;
            }

            return model.Timer.ElapsedMilliseconds / model.Count;
        }

        /// <summary>
        /// Calculates the total average time in milliseconds that elapsed while the specified timers were recording.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        /// <returns>Returns the total average time in milliseconds that elapsed between each start and stop for all the specified timers.</returns>
        public long AverageMilliseconds(T[] keys)
        {
            long total = 0;
            var count = 0;
            foreach (var key in keys)
            {
                var model = this.timers[key];
                total += model.Timer.ElapsedMilliseconds;
                count += model.Count;
            }

            if (count == 0)
            {
                return 0;
            }

            return total / count;
        }

        /// <summary>
        /// Removes the specified timers.
        /// </summary>
        /// <param name="keys">The keys to the timers that will be removed.</param>
        public void Remove(T[] keys)
        {
            foreach (var key in keys)
            {
                this.timers.Remove(key);
            }
        }

        /// <summary>
        /// Removes a timer.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        public void Remove(T key)
        {
            this.timers.Remove(key);
        }

        /// <summary>
        /// Resets all the timers.
        /// </summary>
        public void ResetAll()
        {
            foreach (var pair in this.timers)
            {
                pair.Value.Timer.Reset();
            }
        }

        /// <summary>
        /// Resets all the timers.
        /// </summary>
        /// <param name="resetCounts">If true will set each timer start count to 0.</param>
        public void ResetAll(bool resetCounts)
        {
            foreach (var pair in this.timers)
            {
                pair.Value.Timer.Reset();
                if (resetCounts)
                {
                    pair.Value.Count = 0;
                }
            }
        }

        /// <summary>
        /// Gets an array of timer keys.
        /// </summary>
        /// <returns>Returns an array of timer keys.</returns>
        public T[] GetKeys()
        {
            var keys = new T[this.timers.Count];
            this.timers.Keys.CopyTo(keys, 0);
            return keys;
        }

        /// <summary>
        /// Sets the enabled state of the timer.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        /// <param name="enabled">If true the timer will be enabled. If false the timer will be stopped if it is running.</param>
        public void SetEnabled(T key, bool enabled)
        {
            var model = this.timers[key];
            model.Enabled = enabled;
        }

        /// <summary>
        /// Sets the enabled state of the specified timers.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        /// <param name="enabled">If true the timers will be enabled. If false the timers will be stopped if they are running.</param>
        public void SetEnabled(T[] keys, bool enabled)
        {
            foreach (var key in keys)
            {
                var model = this.timers[key];
                model.Enabled = enabled;
            }
        }

        /// <summary>
        /// Gets the enabled state of the timer.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        public bool IsEnabled(T key)
        {
            var model = this.timers[key];
            return model.Enabled;
        }

        /// <summary>
        /// Gets the enabled state of the specified timers.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        /// <param name="enabled">Will contain the enabled state for each specified key.</param>
        public void IsEnabled(T[] keys, out bool[] enabled)
        {
            var enabledStates = new bool[keys.Length];
            for (int i = 0; i < keys.Length; i++)
            {
                var model = this.timers[keys[i]];
                enabledStates[i] = model.Enabled;
            }

            enabled = enabledStates;
        }

        /// <summary>
        /// Starts the timer.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        /// <remarks>Will not start if the enabled state is false.</remarks>
        public void Start(T key)
        {
            var model = this.timers[key];
            if (!model.Enabled)
            {
                return;
            }
            model.Count++;
            model.Timer.Start();
        }

        /// <summary>
        /// Starts the timers.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        /// <remarks>Will not start if the timers enabled state is false.</remarks>
        public void Start(T[] keys)
        {
            foreach (var key in keys)
            {
                var model = this.timers[key];
                if (!model.Enabled)
                {
                    continue;
                }
                model.Count++;
                model.Timer.Start();
            }
        }

        /// <summary>
        /// Stops the timer.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        public void Stop(T key)
        {
            var model = this.timers[key];
            if (!model.Enabled)
            {
                return;
            }
            model.Timer.Stop();
        }

        /// <summary>
        /// Stops the timers.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        public void Stop(T[] keys)
        {
            foreach (var key in keys)
            {
                var model = this.timers[key];
                if (!model.Enabled)
                {
                    continue;
                }
                model.Timer.Stop();
            }
        }

        /// <summary>
        /// Resets the timer.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        public void Reset(T key)
        {
            this.Reset(key, false);
        }

        /// <summary>
        /// Resets the timers.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        public void Reset(T[] keys)
        {
            this.Reset(keys, false);
        }

        /// <summary>
        /// Resets the timer.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        /// <param name="resetCount">If true the start count for the timer will be set to 0.</param>
        public void Reset(T key, bool resetCount)
        {
            var model = this.timers[key];
            model.Timer.Reset();
            if (resetCount)
            {
                model.Count = 0;
            }
        }

        /// <summary>
        /// Resets the timers.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        /// <param name="resetCounts">If true the start count for the timers will be set to 0.</param>
        public void Reset(T[] keys, bool resetCounts)
        {
            foreach (var key in keys)
            {
                var model = this.timers[key];
                model.Timer.Reset();
                if (resetCounts)
                {
                    model.Count = 0;
                }
            }
        }

        /// <summary>
        /// Resets the timer start count to 0.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        public void ResetCount(T key)
        {
            var model = this.timers[key];
            model.Count = 0;
        }

        /// <summary>
        /// Resets each timer start count to 0.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        public void ResetCount(T[] keys)
        {
            foreach (var key in keys)
            {
                var model = this.timers[key];
                model.Count = 0;
            }
        }

        /// <summary>
        /// Restarts the timer.
        /// </summary>
        /// <param name="key">The key to the timer information.</param>
        /// <param name="resetCount">If true the start count for the timer will be set to 0.</param>
        /// <remarks>The timer will be reset then started again.</remarks>
        public void Restart(T key, bool resetCount)
        {
            var model = this.timers[key];
            model.Timer.Reset();
            if (resetCount)
            {
                model.Count = 0;
            }

            this.Start(key);
        }

        /// <summary>
        /// Restarts the specified timers.
        /// </summary>
        /// <param name="keys">The keys to the timer information.</param>
        /// <param name="resetCounts">If true the start count for the timers will be set to 0.</param>
        /// <remarks>Each timer will be reset then started again.</remarks>
        public void Restart(T[] keys, bool resetCounts)
        {
            foreach (var key in keys)
            {
                var model = this.timers[key];
                model.Timer.Reset();
                if (resetCounts)
                {
                    model.Count = 0;
                }
            }

            this.Start(keys);
        }

        /// <summary>
        /// Gets a singleton instance of the <see cref="PerformanceTesting{T}"/> class.
        /// </summary>
        public static PerformanceTesting<T> Instance
        {
            get
            {
                return singleton ?? (singleton = new PerformanceTesting<T>());
            }
        }
    }

Setting up a performance counter

#if PERFORMANCE
            var perf = PerformanceTesting<string>.Instance;
            perf.Create("ScanLines");
#endif

A sample usage

#if PERFORMANCE
                var perf = PerformanceTesting<string>.Instance;
                perf.Reset("ScanLines");
                perf.Start("ScanLines");
#endif
                this.scanner.Calculate(this.points);
#if PERFORMANCE
                perf.Stop("ScanLines");
#endif

How to report the results

#if PERFORMANCE
        public static void ReportPerformanceTimes()
        {
            var perf = PerformanceTesting<string>.Instance;
            foreach (var value in perf.GetKeys())
            {
                Debug.Log(string.Format("{0} - Total: {1}ms Average: {2} Count: {3}", value, perf.TotalMilliseconds(value), perf.AverageMilliseconds(value), perf.GetStartCount(value)));
            }

            Debug.Log(string.Format("Total Performance Times - Total: {0}ms", perf.TotalMilliseconds("ScanLines")));
        }
#endif

This code uses a Point type that contains X/Y as integers similar to System.Drawing.Point. This class is used to calculate vertical scan lines for a polygon. It has been tested with 3 points but beyond that I am unsure if it will work.

using System;

/// <summary>
/// Helper class used to calculate scan lines of a triangle
/// </summary>
public class TriangleScanLineCalculator
{
    /// <summary>
    /// Provides a high low type for storing min & max values for scan lines.
    /// </summary>
    public struct HiLoTYPE
    {
        public int High;
        public int Low;
    }

    /// <summary>
    /// Holds scan line information.
    /// </summary>
    protected internal HiLoTYPE[] scanLines;

    /// <summary>
    /// Holds the calculated scan line count.
    /// </summary>
    protected internal int scanLineCount;

    /// <summary>
    /// Gets the right most side of the triangle.
    /// </summary>
    public int MaximumX { get; private set; }

    /// <summary>
    /// Gets the left most side of the triangle.
    /// </summary>
    public int MinimumX { get; private set; }

    /// <summary>
    /// Gets the number of scan lines.
    /// </summary>
    public int Count
    {
        get { return scanLineCount; }
    }

    /// <summary>
    /// Calculates the min & max y values for each scan line.
    /// </summary>
    /// <param name="points">The points that make up the triangle</param>
    public void Calculate(Point[] points)
    {
        var xMax = 0;
        var xMin = 0;
        float XDelta = 0;
        float YDelta = 0;
        // X/Y distance between 2 vertexes
        float YPos = 0;
        float YSlope = 0;
        var VertIndex1 = 0;
        var VertIndex2 = 0;
        var tempIndex = 0;

        // Step 1: Find the min and max 'X' dimensions of the polygon
        xMax = int.MinValue;
        xMin = int.MaxValue;
        for (var i = 0; i <= points.Length - 1; i++)
        {
            if (xMax < points[i].X)
            {
                xMax = points[i].X;
            }

            if (xMin > points[i].X)
            {
                xMin = points[i].X;
            }
        }
        this.MinimumX = xMin;
        this.MaximumX = xMax;
        this.scanLineCount = xMax - xMin;

        // Step 2: Resize scan line array to hold all the high and low x values
        if (this.scanLines == null || this.scanLines.Length < xMax - xMin)
        {
            // allocate 
            Array.Resize(ref this.scanLines, (xMax - xMin) + 100);
        }

        // Step3: Set the height value of all scan lines to there min or max value(s)
        for (var i = 0; i <= this.scanLines.Length - 1; i++)
        {
            this.scanLines[i].High = int.MinValue;
            this.scanLines[i].Low = int.MaxValue;
        }

        // Step 4: Set up the Y highs and lows for each X scan line between the min X and Max X points
        for (var i = 0; i < points.Length; i++)
        {
            // Step4a: Determine witch sides of the polygon we will be setting up
            VertIndex1 = i;
            VertIndex2 = i + 1;
            if (VertIndex2 == points.Length) VertIndex2 = 0;

            // Step4b: check if the first vertex if farther right then the second vertex
            // and if so swap vertex indexes
            if (points[VertIndex1].X > points[VertIndex2].X)
            {
                tempIndex = VertIndex1;
                VertIndex1 = VertIndex2;
                VertIndex2 = tempIndex;
            }

            // Step4c: Find the X/Y dist between vert1 and vert2
            XDelta = points[VertIndex2].X - points[VertIndex1].X;
            YDelta = points[VertIndex2].Y - points[VertIndex1].Y;

            // Step4d: Determine the Y slope to use.
            // YSlope determines how much to move down for every move we make to the right
            if (XDelta != 0)
            {
                YSlope = YDelta / XDelta;
            }
            else
            {
                YSlope = 0;
            }

            // Save the starting y position in YPos
            YPos = points[VertIndex1].Y;

            // Step4e: Process all of scan lines between vert1 and vert2
            for (var idy = points[VertIndex1].X; idy < points[VertIndex2].X; idy++)
            {
                // If the scan lines higher value has already been set then set the lower value
                // we use the formula 'idy - XMin' to determine what index into the scan line
                // array to use. (ScanLineIndex = AnyPositionBetweenVert1AndVert2 - LeftMostPartOfPoly)

                // Store the scan line index in the TmpIndex variable so we don't
                // have the overhead of doing 5 subtractions
                tempIndex = idy - xMin;

                var item = this.scanLines[tempIndex];

                // Check if Scan(TmpIndex).High has been set already
                if (item.High == int.MinValue)
                {
                    // High has not been set yet
                    item.High = (int)YPos;
                }
                else
                {
                    // High has been set yet so we set the low point
                    item.Low = (int)YPos;

                    // Ensure that the High is actually a higher value then low
                    if (item.High < item.Low)
                    {
                        var tempValue = item.High;
                        item.High = item.Low;
                        item.Low = tempValue;
                    }
                }
                this.scanLines[tempIndex] = item;

                // update the Y position
                YPos += YSlope;
            }
        }
    }

    /// <summary>
    /// Retrieves a scan line.
    /// </summary>
    /// <param name="index">The index of the scan line.</param>
    /// <returns>Returns information about the scan line.</returns>
    public HiLoTYPE ScanLine(int index)
    {
        return this.scanLines[index];
    }

    /// <summary>
    /// Gets all the scan lines.
    /// </summary>
    /// <returns>Returns all the scan line information.</returns>
    public HiLoTYPE[] GetScanLines()
    {
        // if there are no scan lines return null.
        if (this.scanLineCount == 0)
        {
            return null;
        }

        // resize the array 
        Array.Resize(ref this.scanLines, this.scanLineCount);
        return this.scanLines;
    }

    /// <summary>
    /// Calculates the scan lines for a triangle.
    /// </summary>
    /// <param name="points">The triangle points.</param>
    /// <param name="minXValue">Will return the minimum x value that the triangle starts at.</param>
    /// <returns>Returns the min max y values of the scanline.</returns>
    public static HiLoTYPE[] Calculate(Point[] points, out int minXValue)
    {
        // create scan line calculator and get values from it
        var scanner = new TriangleScanLineCalculator();
        scanner.Calculate(points);
        var items = scanner.GetScanLines();
        minXValue = scanner.MinimumX;
        return items;
    }
}

I recently came across a strange behavior while loading text resource assets. In particular the Resources.LoadAll method does not accept Path.DirectorySeparatorChar characters in a path. In fact it only accepts Path.AltDirectorySeparatorChar characters. This behavior is different then standard .net file/folder methods that accept either Path.AltDirectorySeparatorChar or Path.DirectorySeparatorChar without distinction. What this means is that you can’t directly use Path.Combine to build a path and pass it to the Resources.LoadAll method you first have to replace any Path.DirectorySeparatorChar characters with Path.AltDirectorySeparatorChar characters.

The documentation for Resources.Load also does not mention this behavior.

I have submitted a bug report here –> https://fogbugz.unity3d.com/default.asp?533268_jgvrk2lbu1qm398e

    using System.IO;

    using UnityEditor;

    using UnityEngine;

    /// <summary>
    /// Handles settings registration.
    /// </summary>
    [InitializeOnLoad]
    public class EditorInitialization
    {
        /// <summary>
        /// Holds a value indicating whether the RunCallbacks method has been called at least once before.
        /// </summary>
        private static bool ranOnce;

        /// <summary>
        /// Initializes static members of the <see cref="EditorInitialization"/> class.
        /// </summary>
        static EditorInitialization()
        {
            EditorApplication.update += RunCallbacks;
        }

        private static void RunCallbacks()
        {
            if (!ranOnce)
            {
                // try to load resource
                var path = Path.Combine("Test/SubFolder", "testfile"); // result is Test/SubFolder\testfile

                // var data = Resources.LoadAll("Test/SubFolder/testfile", typeof(TextAsset)); // this line is successful
                var data = Resources.LoadAll(path, typeof(TextAsset));  // this line fails

                if (data != null && data.Length != 0)
                {
                    Debug.Log("found it");
                }
                else
                {
                    Debug.Log("not found! " + path);
                }

                ranOnce = true;
                return;
            }

            // do stuff 
        }
    }

Source - http://stackoverflow.com/questions/11518935/mvc-redirect-to-default-route/11520787#11520787

Redirect route handler

public class RedirectRouteHandler : IRouteHandler
{
    private string _redirectUrl;

    public RedirectRouteHandler(string redirectUrl)
    {
        _redirectUrl = redirectUrl;
    }

    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        if (_redirectUrl.StartsWith("~/"))
        {
            string virtualPath = _redirectUrl.Substring(2);
            Route route = new Route(virtualPath, null);
            var vpd = route.GetVirtualPath(requestContext,
                requestContext.RouteData.Values);
            if (vpd != null)
            {
                _redirectUrl = "~/" + vpd.VirtualPath;
            }
        }

        return new RedirectHandler(_redirectUrl, false);
    } 
}

Redirect http handler

public class RedirectHandler : IHttpHandler
{
    private readonly string _redirectUrl;

    public RedirectHandler(string redirectUrl, bool isReusable)
    {
        _redirectUrl = redirectUrl;
        IsReusable = isReusable;
    }

    public bool IsReusable { get; private set; }

    public void ProcessRequest(HttpContext context)
    {
        context.Response.Status = "301 Moved Permanently";
        context.Response.StatusCode = 301;
        context.Response.AddHeader("Location", _redirectUrl);
    }
}

Extension methods

public static class RouteExtensions
{
    public static void Redirect(this RouteCollection routes, string url, string redirectUrl)
    {
        routes.Add(new Route(url, new RedirectRouteHandler(redirectUrl)));
    }
}

The code below allows you to copy a directory structure with or without sub folders including files. Also includes a callback that can be used to filter out files or folders and reports the copy progress.

/// <summary>
/// Copies a directory structure to the destination.
/// </summary>
/// <param name="source">The directory structure to be copied.</param>
/// <param name="destination">The destination where the directory structure will be copied to.</param>
/// <param name="copySubDirectories">true to copy all subdirectories.</param>
/// <param name="overwriteFiles">true if the destination files can be overwritten; otherwise, false.</param>
/// <param name="callback">Provides a callback function for reporting progress. </param>
/// <remarks><p>The callback invoked just before a file copy occurs providing a way of being notified.</p>
/// <p>The callback parameter order is source file, destination file, progress.</p>
/// <p>If the callback is specified it should return true to allow the file copy to occur.</p> 
/// <p>The progress parameter reports progress from 0 to 100. Values to the left of the decimal represent folder copy progress and values to the
/// right of the decimal from 0.000 to 0.99 represent the current file copy progress for the folder that is being copied.</p>
/// <p>To get the current file copy progress as a value from 0 to 100 use the formula fileProgress = progress - 100 * 100.</p></remarks>
public static void CopyDirectories(string source, string destination, bool copySubDirectories, bool overwriteFiles, Func<string, string, float, bool> callback)
{
    // ensure source folder exists
    if (!Directory.Exists(source))
    {
        throw new DirectoryNotFoundException("The path specified in source is invalid (for example, it is on an unmapped drive).");
    }

    // create destination folder
    Directory.CreateDirectory(destination);

    // get all files in source and copy them to destination folder
    var files = Directory.GetFiles(source);
    var progress = 0f; // used to report the progress from 0 to 100

    // set up action to copy files
    var fileProcessor = new Action<float, string[], string>((folderProgress, filesToCopy, folder) =>
        {
            // copy files
            for (var i = 0; i < filesToCopy.Length; i++)
            {
                // get file
                var file = filesToCopy[i];

                // set default result
                var result = true;

                // build destination filename
                var fileName = Path.GetFileName(file);
                if (fileName == null) // should never happen
                {
                    return;
                }

                fileName = Path.Combine(folder, fileName);

                // check if callback specified
                if (callback != null)
                {
                    // store result from callback
                    result = callback(file, fileName, progress);
                }

                // if result is true we are allowed to copy the file
                if (result)
                {
                    File.Copy(file, fileName, overwriteFiles);
                }

                // (folder progress * 100) + file progress
                progress = folderProgress + ((float)i / filesToCopy.Length);
            }
        });

    // copy initial files
    fileProcessor(0, files, destination);

    // check to copy sub directories
    if (!copySubDirectories)
    {
        return;
    }

    // get the folder tree for the source folder
    var folders = Directory.GetDirectories(source, "*.*", SearchOption.AllDirectories);

    // process each sub folder
    for (var index = 0; index < folders.Length; index++)
    {
        // get folder and increment index
        var folder = folders[index];

        // get files
        files = Directory.GetFiles(folder);

        // crop source root from destination and build destination folder path
        folder = folder.Remove(0, source.Length);
        folder = Path.Combine(destination, folder);

        // create destination folder
        Directory.CreateDirectory(folder);

        // process file copying
        fileProcessor((index / folders.Length) * 100, files, folder);
    }
}

Created by: X

Just another personal website in this crazy online world

Name of author Dean Lunz (aka Created by: X)
Computer programming nerd, and tech geek.
About Me -- Resume