Other Posts in Data Types

  1. Replacing List
  2. Factory Pattern using Generics in C#
  3. Vector Class and Events in .Net
  4. Binary Trees in C# using Generics
  5. Priority Queue in C#
  6. Cheap Trick for Fluent Interfaces
  7. Building an DI Container

Vector Class and Events in .Net

8/27/2009

If you came to C# from C++, you're going to notice rather fast that one data type is missing from the System.Collections namespace. Namely a vector class. In C++ there's the std::vector class which is used about any time that you need an array in C++ (unless of course you like recreating an array when you out grow it). In C#, there is no class like that. We have the List class, which is used with about the same frequency and has a similar function but you don't have that great of control over the capacity. I can set the capacity of it but I can't really control how much is added each time it needs to resize (not to mention I would prefer to have some sort of tie in with events, etc.). So I ended up creating my own Vector class.

   1: /*
   2: Copyright (c) 2009 <a href="http://www.gutgames.com">James Craig</a>
   3: 
   4: Permission is hereby granted, free of charge, to any person obtaining a copy
   5: of this software and associated documentation files (the "Software"), to deal
   6: in the Software without restriction, including without limitation the rights
   7: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8: copies of the Software, and to permit persons to whom the Software is
   9: furnished to do so, subject to the following conditions:
  10: 
  11: The above copyright notice and this permission notice shall be included in
  12: all copies or substantial portions of the Software.
  13: 
  14: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20: THE SOFTWARE.*/
  21:  
  22: #region Usings
  23: using System;
  24: using System.Collections.Generic;
  25: using System.Linq;
  26: using System.Text;
  27: using Utilities.Events.EventArgs;
  28: using Utilities.Events;
  29: #endregion
  30:  
  31: namespace Utilities.DataTypes
  32: {
  33:     /// <summary>
  34:     /// Vector class
  35:     /// </summary>
  36:     /// <typeparam name="T">The type of item the vector should hold</typeparam>
  37:     public class Vector<T> : IList<T>
  38:     {
  39:         #region Constructor
  40:  
  41:         /// <summary>
  42:         /// Constructor
  43:         /// </summary>
  44:         public Vector()
  45:         {
  46:             Items = new T[DefaultSize];
  47:         }
  48:  
  49:         /// <summary>
  50:         /// Constructor
  51:         /// </summary>
  52:         /// <param name="InitialSize">Initial size of the vector</param>
  53:         public Vector(int InitialSize)
  54:         {
  55:             if (InitialSize < 1) throw new ArgumentOutOfRangeException("InitialSize");
  56:  
  57:             Items = new T[InitialSize];
  58:             DefaultSize = InitialSize;
  59:         }
  60:  
  61:         #endregion
  62:  
  63:         #region IList<T> Members
  64:  
  65:         public int IndexOf(T item)
  66:         {
  67:             return Array.IndexOf<T>(this.Items, item, 0, this.NumberItems);
  68:         }
  69:  
  70:         public void Insert(int index, T item)
  71:         {
  72:             if (index > this.NumberItems || index < 0) throw new ArgumentOutOfRangeException("index");
  73:  
  74:             try
  75:             {
  76:                 if (this.NumberItems == this.Items.Length)
  77:                 {
  78:                     Array.Resize<T>(ref this.Items, this.Items.Length * 2);
  79:                 }
  80:                 if (index < this.NumberItems)
  81:                 {
  82:                     Array.Copy(this.Items, index, this.Items, index + 1, this.NumberItems - index);
  83:                 }
  84:                 this.Items[index] = item;
  85:                 ++this.NumberItems;
  86:                 EventHelper.Raise<ChangedEventArgs>(Changed, this, new ChangedEventArgs());
  87:             }
  88:             catch (Exception a) { throw a; }
  89:         }
  90:  
  91:         public void RemoveAt(int index)
  92:         {
  93:             if (index > this.NumberItems || index < 0) throw new ArgumentOutOfRangeException("index");
  94:  
  95:             try
  96:             {
  97:                 if (index < this.NumberItems)
  98:                 {
  99:                     Array.Copy(this.Items, index + 1, this.Items, index, this.NumberItems - (index + 1));
 100:                 }
 101:                 this.Items[this.NumberItems - 1] = default(T);
 102:                 --this.NumberItems;
 103:                 EventHelper.Raise<ChangedEventArgs>(Changed, this, new ChangedEventArgs());
 104:             }
 105:             catch (Exception a) { throw a; }
 106:         }
 107:  
 108:         public T this[int index]
 109:         {
 110:             get
 111:             {
 112:                 if (index > this.NumberItems || index < 0) throw new ArgumentOutOfRangeException("index");
 113:                 return this.Items[index];
 114:             }
 115:             set
 116:             {
 117:                 if (index > this.NumberItems || index < 0) throw new ArgumentOutOfRangeException("index");
 118:                 this.Items[index] = value;
 119:                 EventHelper.Raise<ChangedEventArgs>(Changed, this, new ChangedEventArgs());
 120:             }
 121:         }
 122:  
 123:         #endregion
 124:  
 125:         #region ICollection<T> Members
 126:  
 127:         public void Add(T item)
 128:         {
 129:             try
 130:             {
 131:                 Insert(this.NumberItems, item);
 132:             }
 133:             catch (Exception a) { throw a; }
 134:         }
 135:  
 136:         public void Clear()
 137:         {
 138:             try
 139:             {
 140:                 Array.Clear(this.Items, 0, this.Items.Length);
 141:                 this.NumberItems = 0;
 142:                 EventHelper.Raise<ChangedEventArgs>(Changed, this, new ChangedEventArgs());
 143:             }
 144:             catch (Exception a) { throw a; }
 145:         }
 146:  
 147:         public bool Contains(T item)
 148:         {
 149:             return (this.IndexOf(item) >= 0);
 150:         }
 151:  
 152:         public void CopyTo(T[] array, int arrayIndex)
 153:         {
 154:             Array.Copy(this.Items, 0, array, arrayIndex, this.NumberItems);
 155:         }
 156:  
 157:         public int Count
 158:         {
 159:             get { return this.NumberItems; }
 160:         }
 161:  
 162:         public bool IsReadOnly
 163:         {
 164:             get { return false; }
 165:         }
 166:  
 167:         public bool Remove(T item)
 168:         {
 169:             int Index = this.IndexOf(item);
 170:             if (Index > 0)
 171:             {
 172:                 this.RemoveAt(Index);
 173:                 return true;
 174:             }
 175:             return false;
 176:         }
 177:  
 178:         #endregion
 179:  
 180:         #region IEnumerable<T> Members
 181:  
 182:         public IEnumerator<T> GetEnumerator()
 183:         {
 184:             for (int x = 0; x < this.NumberItems; ++x)
 185:             {
 186:                 yield return this.Items[x];
 187:             }
 188:         }
 189:  
 190:         #endregion
 191:  
 192:         #region IEnumerable Members
 193:  
 194:         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 195:         {
 196:             for (int x = 0; x < this.NumberItems; ++x)
 197:             {
 198:                 yield return this.Items[x];
 199:             }
 200:         }
 201:  
 202:         #endregion
 203:  
 204:         #region Protected Variables/Properties
 205:  
 206:         protected int DefaultSize = 2;
 207:         protected T[] Items = null;
 208:         protected int NumberItems { get; set; }
 209:  
 210:         #endregion
 211:  
 212:         #region Events
 213:         public EventHandler<ChangedEventArgs> Changed;
 214:         #endregion
 215:     }
 216: }

Most of the class above is pretty straight forward. We have an array of elements and each time we insert an item and need to increase the size of the array, we double its size. If you wanted you could switch it from multiplying by two to a fixed size (a simple change really). The only portion which may not jump out at you though is the fact that there is an EventHandler class in there. An EventHandler is basically a generic class that holds a method that handles an event. It's not mine, just a .Net class. But it's defined with a ChangedEventArgs class which is mine. The ChangedEventArgs class is a simple EventArgs class that looks like this:

   1: public class BaseEventArgs : System.EventArgs
   2: {
   3:     public bool Stop
   4:     {
   5:         get { return _Stop; }
   6:         set { _Stop = value; }
   7:     }
   8:     private bool _Stop = false;
   9:  
  10:     public object Content
  11:     {
  12:         get { return _Content; }
  13:         set { _Content = value; }
  14:     }
  15:     private object _Content = null;
  16: } 
  17:  
  18: public class ChangedEventArgs : BaseEventArgs
  19: {
  20: }

Just a slightly modified EventArgs class. The other thing that you may notice about the vector class is it uses an EventHelper class to call the EventHandler. Once again, it's something that I created. Basically I was tired of writing the basic event raising code for every instance so I created a generic way of doing it.

   1: public static class EventHelper
   2: {
   3:     /// <summary>
   4:     /// Raises an event
   5:     /// </summary>
   6:     /// <typeparam name="T">The type of the event args</typeparam>
   7:     /// <param name="Delegate">The delegate</param>
   8:     /// <param name="Sender">The sender</param>
   9:     /// <param name="EventArgs">The event args</param>
  10:     public static void Raise<T>(EventHandler<T> Delegate, object Sender, T EventArg) where T : System.EventArgs
  11:     {
  12:         try
  13:         {
  14:             if (Delegate != null)
  15:             {
  16:                 Delegate(Sender, EventArg);
  17:             }
  18:         }
  19:         catch { throw; }
  20:     }
  21: }

It's pretty simple. Just takes in an EventHandler, the sender, and the eventargs. Even if all it does is saves me a couple lines of code in a couple other classes, that's all I really need. Anyway, that's it. With those classes we have our Vector class that can notify us when things are changed, we control how it grows, etc. along with a couple other functions/classes that you might find useful. So try it out, leave feedback, and happy coding.



Comments