Я хочу реализовать коллекцию, предметы которой нужно проверить на пустоту.
В случае ссылочного типа, можно проверить на нулевое значение. Для типов значений необходимо реализовать пустое тестирование и, возможно, выбрать конкретное значение, представляющее пустоту.
Моя общая коллекция T должна быть пригодна для значений значений и ссылочных типов (это означает, что оба значения Coll<MyCalss>
и Coll<int>
должны быть возможны). Но я должен по-разному проверять ссылочные типы и типы значений.
Не было бы неплохо иметь интерфейс, который реализует метод IsEmpty (), чтобы исключить эту логику из моего универсального типа? Но, конечно, этот метод IsEmpty () не может быть функцией-членом: его нельзя вызывать для пустых объектов.
Один из обходных путей, который я нашел, состоит в том, чтобы элементы коллекции сохранялись как объекты, а не как T-s, но это доставляет мне головную боль (вокруг бокса и жесткой типизации). В старом добром C ++ это не было проблемой: -)
Код ниже демонстрирует, чего я хотел бы достичь:
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.
}
}
}