Объявление, что структура возвращается из метода интерфейса

Я пишу метод в C# (2.0), который должен возвратить набор простых объектов. Обычно я сделал бы что-то вроде этого:

class MyWidget
{
    struct LittleThing
    {
        int foo;
        DateTime bar;
    }

    public IList<LittleThing> LookupThings()
    {
        // etc.
    }
}

Однако я должен объявить этот метод в интерфейсе. Вызывающая сторона не добирается для наблюдения MyWidget, только IWidget интерфейс. Вышеупомянутая установка не работает в той ситуации, потому что C# не позволяет определять типы в интерфейсе. Что надлежащий или лучший способ состоит в том, чтобы сделать такое объявление?

Простая вещь, о которой я думал, состоит в том, чтобы просто объявить LittleThing за пределами интерфейса. Это не кажется большим по нескольким причинам. Один: это только когда-либо используется тем отдельным методом в том едином классе, таким образом, это не кажется этим LittleThing должен быть независимый тип, просто плавающий вокруг отдельно. Два: если похожие методы завершат то, чтобы быть записанным для других классов, то они будут возвращать различные виды данных (по хорошим причинам дизайна), и я не хочу создавать помехи namepace тонной подобно названных структур, которые отличаются незначительно друг от друга.

Если бы мы могли бы обновить нашу версию .NET, я просто возвратил бы a Tuple<>, но это в течение некоторого времени еще не будет опцией.

[Отредактированный для добавления: маленький объект действительно должен содержать больше чем два поля, таким образом, KeyValuePair<K,V> не вполне сократит его.]

[Отредактированный для добавления далее: IWidget реализован только одним классом, Widget. Я думаю, что это странный имеет интерфейс только для одного класса, но это было сделано для удовлетворения старой политики кодирования, которая потребовала, чтобы контракт всегда был в отдельном блоке от реализации. Упомянутая политика теперь ушла, но у нас нет ресурсов, чтобы осуществить рефакторинг целое приложение и удалить все ненужные интерфейсы.]

Какова лучшая практика?

6
задан Auraseer 8 January 2010 в 19:39
поделиться

7 ответов

Если бы мы могли обновить нашу версию .Net, я бы просто вернул Tuple<>, но это еще не будет вариантом в течение некоторого времени.

Зачем ждать? Не похоже, что кортеж - это сложная вещь. Вот код для 3-ступенчатого кортежа.

public struct Tuple<TItem1, TItem2, TItem3>
{
    public Tuple(TItem1 item1, TItem2 item2, TItem3 item3)
    {
        this = new Tuple<TItem1, TItem2, TItem3>();
        Item1 = item1;
        Item2 = item2;
        Item3 = item3;
    }

    public static bool operator !=(Tuple<TItem1, TItem2, TItem3> left, Tuple<TItem1, TItem2, TItem3> right)
    { return left.Equals(right); }

    public static bool operator ==(Tuple<TItem1, TItem2, TItem3> left, Tuple<TItem1, TItem2, TItem3> right)
    { return !left.Equals(right); }

    public TItem1 Item1 { get; private set; }
    public TItem2 Item2 { get; private set; }
    public TItem3 Item3 { get; private set; }

    public override bool Equals(object obj)
    {
        if (obj is Tuple<TItem1, TItem2, TItem3>)
        {
            var other = (Tuple<TItem1, TItem2, TItem3>)obj;
            return Object.Equals(Item1, other.Item1)
                && Object.Equals(Item2, other.Item2)
                && Object.Equals(Item3, other.Item3);
        }
        return false;
    }

    public override int GetHashCode()
    {
        return ((this.Item1 != null) ? this.Item1.GetHashCode() : 0)
             ^ ((this.Item2 != null) ? this.Item2.GetHashCode() : 0)
             ^ ((this.Item3 != null) ? this.Item3.GetHashCode() : 0);
    }
}

Как видите, ничего страшного. Что я сделал в своем текущем проекте, так это реализовал 2, 3 и 4-ступени, а также статический Tuple класс с Create методами, которые в точности повторяют типы кортежей .NET 4. Если вы действительно параноик, вы можете использовать рефлектор, чтобы посмотреть на разобранный исходный код кортежа .NET 4, и скопировать его дословно

Когда мы в конце концов обновим до .NET 4, мы просто удалим классы, или #ifdef их из

.
1
ответ дан 10 December 2019 в 00:39
поделиться
  1. Зачем использовать структуры? Почему бы не использовать вместо этого классы?
  2. Объявляем класс отдельно, как публичный класс.
2
ответ дан 10 December 2019 в 00:39
поделиться

Игнорируя здесь значение названия структуры, можно использовать общую версию KeyValuePair.

.
0
ответ дан 10 December 2019 в 00:39
поделиться

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

Тем не менее, одним из паттернов, часто используемых для обхода этого ограничения, является определение класса с тем же именем (исключая префикс 'I') для предоставления любых связанных определений, таких как:

public interface IWidget
{
    IList<Widget.LittleThing> LookupThings();
}

// For definitions used by IWidget
public class Widget
{
    public struct LittleThing
    {
        int foo;
        DateTime bar;
    }
}

Примеры этого паттерна можно найти в BCL, в частности, в генериках и методах-расширениях, но также и со значениями по умолчанию (e. например, EqualityComparer.Default) и даже реализации по умолчанию (например, IList и List). Выше приведен лишь еще один случай.

).
0
ответ дан 10 December 2019 в 00:39
поделиться

Если "LittleThing" имеет только два значения, вы можете вернуть KeyValuePair.

Если их больше двух, вы всегда можете сделать свой собственный класс Tuple, и заменить его на .NET 4, когда вы, наконец, перейдете на .NET 4.

Иначе я бы просто определил структуру с интерфейсом, и включил бы ее как часть вашего API. Пространства имен заботятся об именовании...

6
ответ дан 10 December 2019 в 00:39
поделиться

Вы можете объявить структуру вне интерфейса , но внутри вложенного пространства имен .

1
ответ дан 10 December 2019 в 00:39
поделиться

Итак, прочитав все, что вы прокомментировали, я вот что думаю:

  1. Если все типы, реализующие IWidget, возвращают LittleThing:

    Тогда я считаю, что лучшая практика для LittleThing должна быть на том же уровне пространства имен чем IWidget, предпочтительно в пространстве имен Widgets. Однако вам действительно стоит подумать о том, чтобы сделать LittleThing классом. Судя по вашему описанию проблемы, это может быть не так уж и мало, поскольку каждый раз класс, реализующий IWidget, неправильно использует одни поля, но не другие.

  2. Если все типы, реализующие IWidget, должны возвращать немного разные значения, но с аналогичным поведением:

    Рассмотрите возможность создания интерфейса IThing. Тогда каждая реализация IThing будет полностью зависеть от класса, который ее возвращает, поэтому вы можете объявить структуру или класс, реализующий IThing внутри класса, реализующего IWidget, следующим образом:

     interface IWidget
     {
      IList  LookupThings ()
      {
      …
      }
     }
    
    интерфейс IThing
     {
      …
     }
    
    класс MyWidget: IWidget
     {
      IList  IWidget.LookupThings ()
      {
      …
      }
    
      частный класс MyWidgetThings: IThing
      {
      …
      }
     }
     
0
ответ дан 10 December 2019 в 00:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: