0

I have a requirement where: 1. I need to store objects of any type in list 2. Avoid casting calls as much as possible

To that end I tried to come up with something. No matter what I tried I could not get rid of boxing\unboxing. I wanted to know whether any of you have come across something that will achieve it.

The class I have created is mostly useless unless you are dealing with small collections because in terms of memory and performance it takes 1.5 times ArrayList. I am trying to find ways to improve at least one of them as well (preferably performance).

Any feedback is appreciated.

    public class Castable 
    {
        Object _o;

        public override bool Equals(object obj) { return base.Equals(obj); }

        public override int GetHashCode() { return base.GetHashCode(); }

        public bool Equals<T>(T obj)
        {
            T v1 = (T)this._o;
            //T v2 = obj;
            //var v2 = obj; // Convert.ChangeType(obj, obj.GetType());

            // This doesn't work.. (Cannot convert T to Castable
            //var v2 = Convert.ChangeType(this.GetType() == obj.GetType() ?  
            //((Castable)obj)._o.GetType(), obj.GetType());

            //if (((T)this._o) != obj) //<== why this doesn't work?
            //if (v1 == obj) //<== "Operator '==' cannot be applied to operands of type 'T' and 'T'"
            if(v1.Equals(obj))
            {
                return true;
            }

            return false;
        }

        public bool Equals(Castable obj)
        {
            var v = Convert.ChangeType(obj._o, obj._o.GetType());
            return Equals(v);
        }


        public static bool operator ==(Castable a, Castable b)
        {
            return a.Equals(b);
        }

        public static bool operator !=(Castable a, Castable b)
        {
            return !a.Equals(b);
        }

        #region HOW CAN WE USE GENRIC TYPE FOR == and != OPERATOR?
        public static bool operator ==(Castable a, object b)
        {
            return a.Equals(b);
        }

        public static bool operator !=(Castable a, object b)
        {
            return !a.Equals(b);
        }
        #endregion

        public void Set<T>(T t)  { _o = t; }

        public T Get<T>() { return (T)_o; }

        public static long TestLookup(IList list, int elements, int lookups)
        {
            object value;
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (long index = 0; index < lookups; ++index)
            {
                value = list[random.Next(0, elements - 1)];
            }
            watch.Stop();

            return watch.ElapsedMilliseconds;
        }

        public static long TestCompare(IList list, int elements, int lookups)
        {
            //object value;
            bool match;
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (long index = 0; index < lookups; ++index)
            {
                match = random.Next() == (int)list[random.Next(0, elements - 1)];
            }
            watch.Stop();

            return watch.ElapsedMilliseconds;
        }

        public static long TestCompareCastable(IList<Castable> list, int elements, int lookups)
        {
            //object value;
            bool match;
            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (long index = 0; index < lookups; ++index)
            {
                match = list[random.Next(0, elements - 1)] == random.Next(); //most of the times 1.4 times
                //match = list[random.Next(0, elements - 1)].Equals(random.Next()); // may be 1.3 times ArrayList
            }
            watch.Stop();

            return watch.ElapsedMilliseconds;
        }


        public static void Test(int elements, int lookups, int times)
        {
            List<int> intList = new List<int>();
            List<Castable> castableList = new List<Castable>();
            ArrayList intArrayList = new ArrayList();

            if (Stopwatch.IsHighResolution)
                Console.WriteLine("We have a high resolution timer available");

            long frequency = Stopwatch.Frequency;
            Console.WriteLine(" Timer frequency in ticks per second = {0}", frequency);

            for (int index = 0; index < elements; ++index)
            {
                intList.Add(random.Next());
                intArrayList.Add(random.Next());
                Castable c = new Castable();
                c.Set(random.Next());
                castableList.Add(c);
            }

            long ms = 0;


            string result = "";
            string ratios = "";
            for (int time = 0; time < times; ++time)
            {
                ms = TestLookup(intList, elements, lookups);
                result += "intList Lookup Time " + ms.ToString() + " MS\n";
                ms = TestLookup(castableList, elements, lookups);
                result += "intArrayList Lookup Time " + ms.ToString() + " MS\n";
                ms = TestLookup(intArrayList, elements, lookups);
                result += "castableList Lookup Time " + ms.ToString() + " MS\n";

                ms = TestCompare(intList, elements, lookups);
                result += "intList Compare Time " + ms.ToString() + " MS\n";
                long msarraylist = ms = TestCompare(intArrayList, elements, lookups);
                result += "intArrayList Compare Time " + ms.ToString() + " MS\n";
                ms = TestCompareCastable(castableList, elements, lookups);
                result += "castableList Compare Time " + ms.ToString() + " MS\n";
                ratios += String.Format("round: {0}, ratio: {1}\n", time, (float)ms / msarraylist);
            }

            //MessageBox.Show(result);
            MessageBox.Show(ratios);


            int i = 10;
            Castable o1 = new Castable();
            o1.Set(i);
            int j = 10;
            Castable o2 = new Castable();
            o2.Set(j);
            if (!o1.Equals(10))
            {
                Console.WriteLine("unequal");
            }

            if (!o1.Equals(o2))
            {
                Console.WriteLine("unequal");
            }

            if (o1 != j)
            {
                Console.WriteLine("unequal");
            }

            int x = o1.Get<int>();

        }

    }

EDIT

In short I am trying to achieve:

@winSharp93: yes, in short: List GenericGenericCollection = new List ();
GenericGenericCollection.Add(new string("a sonnet");
GenericGenericCollection.Add(42);
GenericGenericCollection.Add(new MyOwnCustomType);

EDIT AGAIN

There are two ways I found: 1. In .NET 4, a new 'dynamic' keyword is introduced. If you replace the line Object _o; with dynamic _o; you can use the code as it is. The problem is although dynamic supposed to be dynamic type, performance is just like boxing..

  1. The performance can be improved by adding implicit (I prefer) or explicit casting operator instead of relying on generic == operator.

  2. Based on http://igoro.com/archive/fun-with-c-generics-down-casting-to-a-generic-type/ I added following class. This takes care of boxing and performance - with following class performance is little better than ArrayList of int or Castable. Of course it has long way to go when List<int> compared. The only problem, from my point of view is, once object is assigned to plain Any object to get concrete type embedded inside AnyInternal<T>. Neither I could find a way to have method T Get(). Even keyword dynamic fails at runtime at statment:

Any.AnyInternal<dynamic> any = (Any.AnyInternal<dynamic>)anyInstanceContainingAnyInternalForInt;

//too bad I can't seal Any after AnyInternal<T> has derived from it.
public abstract class Any
{
    public static implicit operator int(Any any)
    {
        return Any.ToType<int>(any).Data;
    }

    public static AnyInternal<T> ToType<T>(Any any)
    {
        return ((AnyInternal<T>)any);
    }

    public class AnyInternal<T> : Any
    {
        private T _data;
        public T Data { get { return _data; } }
        public AnyInternal(T data)
        {
            _data = data;
        }
    }
}
neel roy
  • 147
  • 1
  • 13
  • Why didn't you declare your type as Castable and have _o declared as the type T? – Jeow Li Huan Feb 23 '12 at 11:56
  • Because that binds Castable to T and I want to have a list where Castable is NOT bound to type. – neel roy Feb 23 '12 at 12:03
  • I would take a step back and look at the problem at hand. If boxing in a collection is a problem for you i would consider implementing my own collection which internally holds multiple inner collections. One for the known value types and one for objects. Create overloads of the Add method would allow you to place the right items in the right inner collections – Polity Feb 23 '12 at 12:09
  • @Polity very sound advice; probably will end up taking it but want to explore every avenue before.. – neel roy Feb 24 '12 at 11:52

1 Answers1

1

Use the generic List<T> (inside System.Collections.Generic) instead of ArrayList.
There won't happen any boxing / unboxing for value types.

Matthias
  • 12,053
  • 4
  • 49
  • 91
  • same answer as to Jeow Li Huan above – neel roy Feb 23 '12 at 12:04
  • So you want to store multiple value types (but no classes) in one List without boxing? – Matthias Feb 23 '12 at 12:05
  • yes, in short: List GenericGenericCollection = new List (); GenericGenericCollection.Add(new string("a sonnet"); GenericGenericCollection.Add(42); GenericGenericCollection.Add(new MyOwnCustomType); – neel roy Feb 23 '12 at 12:09
  • @neelroy - you simply can't without resorting to boxing. C# has a different memory model than C++, cooperate with it, don't try and fight it. Again, read my comment on your question if you want to look for an alternative workaround – Polity Feb 23 '12 at 12:22