У кого-нибудь есть альтернатива использованию статических методов в интерфейсе C #? - PullRequest
0 голосов
/ 28 ноября 2009

Я хочу реализовать коллекцию, предметы которой нужно проверить на пустоту. В случае ссылочного типа, можно проверить на нулевое значение. Для типов значений необходимо реализовать пустое тестирование и, возможно, выбрать конкретное значение, представляющее пустоту.

Моя общая коллекция 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.
    }
  }
}

Ответы [ 5 ]

7 голосов
/ 28 ноября 2009

Вы можете создать метод расширения для вашего интерфейса с именем IsEmpty. Затем вы могли бы сначала проверить, является ли параметр this в этом методе расширения нулевым.

Таким образом, вы можете вызывать метод IsEmpty для любой ссылки на тип, реализующий ваш интерфейс, независимо от того, может ли он быть нулевым или нет.

3 голосов
/ 28 ноября 2009

Не существует такого понятия, как "1002" * "пусто" int, поэтому поддержка int будет непростой задачей, если вы не сохраните битовую карту, для которой определены, - но если вы просто используете коллекцию из int? (то есть Nullable<int>) это сделано для вас . Без дополнительной работы и без бокса:

List<int?> list1 = ...; // data
List<string> list2 = ...; // data

Console.WriteLine(list1[3]; == null); // actually maps to `item.HasValue`
Console.WriteLine(list2[3]; == null); // reference test

Чтобы получить int из int?, любой из:

int i = (int)value; // throws exception if empty
int i = i.Value; // throws exception if empty
int i = i.GetValueOrDefault(); // returns 0 if empty

А когда вам не нужно проверять на пустоту, просто List<int> (нет ?). Нет лишнего кода. Нет специального класса коллекции. И это работает для большинства всего.

3 голосов
/ 28 ноября 2009

Для этого можно использовать ключевое слово «по умолчанию», например:

this.Items[Index] = default(T);
0 голосов
/ 28 ноября 2009

Как насчет отправки двух функций в конструкторе?

public Coll(Func<T> createEmpty, Func<T, bool> isEmpty)
{
    this.createEmpty = createEmpty;
    this.isEmpty = isEmpty;
}

Вы можете использовать эти функции позже:

if (!isEmpty(item))

Items[index] = createEmpty();
0 голосов
/ 28 ноября 2009

Вместо использования массива в вашем классе для представления коллекции используйте стек или список. Тогда у вас нет пустых индексов, их просто нет, если они были удалены.

...