Общие классы с методами, которые работают только для некоторых параметров типа - PullRequest
0 голосов
/ 29 августа 2009

Допустим, вы пишете библиотеку для отображения вещей на экране, поэтому вы создаете интерфейс IDisplayable. Этот интерфейс имеет один метод для создания элемента управления из объекта: displayable.GetControl().

Вы хотите создать свой собственный тип списка, который может отображаться: MyList<T>. Теперь этот список может отображаться только в том случае, если T является IDisplayable, поэтому вы можете спросить в классе MyList, что T должен реализовывать IDisplayable. Но вы также хотите использовать этот тип списка в некоторых местах, когда T не IDisplayable (и в результате этот список не будет отображаться). Так можно ли сказать, что MyList реализует IDisplayable, если T реализует IDisplayable? Я также был бы рад, если бы MyList<T> всегда реализовывал IDisplayable, но выдавал исключение во время выполнения, если вы пытаетесь вызвать GetControl(), если T не IDisplayable, но я хотел бы знать, есть ли статически безопасный для этого способ сделать это , Можно ли это сделать? Или я смотрю на неправильное решение?

Edit:

Я согласен с предложениями, что MyList может иметь слишком много обязанностей. Моей первоначальной идеей было создать MyDisplayableList<T> : MyList<T> (where T : IDisplayable).

Проблема этого подхода заключается в том, что у меня есть много методов, которые берут MyList и возвращают MyList (например, такие методы, как Select in Linq). Так что, если я использую select в MyDisplayableList, я получаю MyList, и я не могу отобразить его, даже если это MyList ... Есть ли безопасный тип решения этой проблемы в C #?

Ответы [ 5 ]

8 голосов
/ 29 августа 2009

Это невозможно, как вы это описали. Вы должны создать список двух типов:

public class MyList<T> : IList<T>
{
    ...
}

public class MyDisplayableList<T> : MyList<T> where T : IDisplayable
{
    ...
}
5 голосов
/ 29 августа 2009

Simple. Проверьте, является ли тип IDisplayable. Если это не так, бросьте InvalidOperationException:

if (!typeof(IDisplayable).IsAssignableFrom(typeof(T))) 
    throw new InvalidOperationException();

Или, если у вас есть экземпляр T, просто проверьте это:

IDisplayable disp = instanceOfT as IDisplayable;
if (disp == null)
    throw new InvalidOperationException();
// do stuff with `disp`.

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

1 голос
/ 21 июня 2010

Я думаю, что лучшее решение этой проблемы - абстрагироваться от списка и подумать о составном из IDisplayable с. Именно так моделируются элементы управления в ASP.NET или Windows Forms, например.

public class CompositeDisplayable : IDisplayable {

  public void Add(IDisplayable displayable) {
    // TODO: check for null, cycles, etc...
    _list.Add(displayable);
  }

  public void Remove(IDisplayable displayable) {
    // TODO: check for null
    _list.Remove(displayable);
  }

  public Control GetControl() {
    var control = new Control(); 
    // this assumes Control is also a composite type
    _list.ForEach(displayable => control.Add(displayable.GetControl()));
    return control;
  }

  private List<IDisplayable> _list = new List<IDisplayable>();

}

Затем, чтобы соединить вашу коллекцию «чего-то» с композитом, вы можете создать метод расширения, подобный этому:

public static class DisplayableExtensions {

  public static IDisplayable ToDisplayable(this IEnumerable source) {
    if (source == null) throw new NullReferenceException(); // extension method should behave like instance methods
    var result = new CompositeDisplayable();
    source.
      OfType<IDisplayable>().
      ToList().
      ForEach(displayable => result.Add(displayable));
    return result;
  }

}
1 голос
/ 29 августа 2009

Я думаю, что причина, по которой вы хотите, чтобы MyList<T> мог работать как с IDisplayable, так и с не IDisplayable, заключается в том, что есть некоторая дублируемая функция.

Я бы предложил использовать базовую реализацию как MyListBase<T>, которая реализует базовые функции, которые выполняет оба списка. Затем MyDisplayableList наследует MyList (MyDisplayableList<T> : MyList<T> where T : IDisplayable), который выполняет функции, относящиеся только к IDisplayable.

Если есть какая-либо функция, относящаяся к non-IDisplayble, добавьте NonDisplayableList<T> : MyListBase<T> для выполнения этих функций.

1 голос
/ 29 августа 2009

Тебе вообще нужны дженерики?

Я думаю, у вас есть несколько классов, реализующих IDisplayable, и вы хотите поместить все их экземпляры в один и тот же список. Так что вам просто нужно

public class MyList : Collection<IDisplayable>, IDisplayable
{
    public void GetControl()
    {
        foreach (IDisplayable displayable in this)
        {
            displayable.GetControl();
        }
    }
}

Если вы действительно хотите добавить в этот список также не-1007 * экземпляры, найдите общий базовый класс и определите

public class MyList2 : Collection<object>, IDisplayable
{
    public void GetControl()
    {
        foreach (IDisplayable displayable in this.OfType<IDisplayable>())
        {
            displayable.GetControl();
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...