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..
The performance can be improved by adding implicit (I prefer) or explicit casting operator instead of relying on generic == operator.
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 insideAnyInternal<T>
. Neither I could find a way to have methodT 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;
}
}
}