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.
}
}
}