I want to implement a collection, whose items need to be tested for emptiness. In case of a reference type, one would test for being null. For value types, one has to implement empty testing, and probably choose a specific value that represents emptyness.
My generic collection of T should be usable for both value and reference type values (meaning that Coll<MyCalss>
and Coll<int>
should both be possible). But I have to test reference and value types differently.
Wouldn't it be nice to have an interface that implements an IsEmpty() method, to exclude this logic from my generic type? But of course, this IsEmpty() method cannot be a member function: it could not be called on empty objects.
One workaround I found is to have collection items stored as objects, rather then T-s, but it gives me a headache (around boxing and being strongly typed). In good old C++ it was no problem :-)
The code below demonstrates what I'd like to achieve:
using System;
using System.Collections.Generic;
namespace StaticMethodInInterfaceDemo
{
public interface IEmpty<T>
{
static T GetEmpty(); // or static T Empty;
static bool IsEmpty(T ItemToTest);
}
public class Coll<T> where T : IEmpty<T>
{
protected T[] Items;
protected int Count;
public Coll(int Capacity)
{
this.Items = new T[Capacity];
this.Count = 0;
}
public void Remove(T ItemToRemove)
{
int Index = Find(ItemToRemove);
// Problem spot 1: This throws a compiler error: "Cannot convert null to type parameter 'T'
// because it could be a non-nullable value type. Consider using 'default(T)' instead."
this.Items[Index] = null;
// To overcome, I'd like to write this:
this.Items[Index] = T.Empty; // or T.GetEmpty(), whatever.
this.Count--;
}
public T[] ToArray()
{
T[] ret = new T[this.Count];
int TargetIndex = 0;
for(int Index = 0; Index < this.Items.GetLength(0); Index++)
{
T Item = this.Items[Index];
// Problem spot 2: This test is not correct for value types.
if (Item != null)
ret[TargetIndex++] = Item;
// I'd like to do this:
if (!T.IsEmpty(Item))
ret[TargetIndex++] = Item;
}
return ret;
}
protected int Find(T ItemToFind)
{
return 1; // Not implemented in the sample.
}
}
}
Instead of using an array in your class to represent the collection, use a stack or list. Then you don't have empty indexes, they simply aren't there if they where removed.
How about just sending in two functions in the constructor?
You could then use those functions later:
There is no such thing as an "empty"
int
, so supportingint
will be tricky unless you store a bit-map of which are defined - but if you simply use a collection ofint?
(i.e.Nullable<int>
) it is done for you. No extra work, and no boxing:To get an
int
from anint?
, any of:And when you don't need to be able to test for empty, just a
List<int>
(no?
). No extra code. No special collection class. And it works for most anything.You can use the 'default' keyword for this, as in:
You can create an extension method for your Interface called IsEmpty. Then you could first test if the 'this' parameter in this extension method is null.
So you could call the IsEmpty method on any reference to a type that is implementing your interface, not regarding if this could be null or not.