Общий интерфейс

Я разрабатываю приложение, в котором одинаковые объекты находятся в двух местах с разными типами коллекций, как показано ниже.

Модель:

class PersonModel {
  public string Name { get;set;}
  public List<Address> Addresses { get;}
  public List<OtherType> OtherTypes { get;}
}

Аналогичная модель просмотра:

class PersonViewModel {
   public string Name { get;set;}
   public ObservableCollection<Address> Addresses { get; }
   public ObservableCollection<OtherType> OtherTypes { get; }
}

Чтобы сделать обе сущности согласованными, я решил использовать общий интерфейс, который гарантирует, что обе сущности реализуют все свойства, поэтому я создал что-то вроде этого:

public interface IPerson<T> where T: ICollection<T> {
   string Name { get;set;}
   T<Address> Addresses { get;}
   T<OtherType> OtherTypes [ get; } 
}

и занятия будут

class PersonModel<List> {}
class personViewModel<ObservableCollection> {}

но компилятор не готов скомпилировать мой интерфейс. :( Говорит, что параметр типа "T" не может использоваться с аргументом типа.

Причина, по которой я хочу этого, я хотел свести к минимуму преобразование типов из/в модель и модель представления.

Моя модель просмотра будет такой,

class PersonViewModel<T> : IPerson<T> {
   public PersonViewModel(IPerson model){
      this.Model = model;
   }
   internal PersonModel Entity {
      get; set;
   }
   public string Name { 
      get{ return model.Name;} 
      set {model.Name = value;}
   }
   public T<Address> Addresses {
      get { return model.Addresses.Cast<T>(); }
   }
}

Предложите мне лучший способ синхронизации Model и ViewModel.


person hungryMind    schedule 02.02.2012    source источник
comment
Возможно, вам стоит просто использовать Automapper.   -  person CodesInChaos    schedule 02.02.2012
comment
Я уже использовал его, но члену команды не нравятся большие и вложенные объекты.   -  person hungryMind    schedule 02.02.2012


Ответы (4)


Вы не можете использовать дженерики таким образом. Вы можете попробовать что-то вроде этого

public interface IPerson
{
    string Name { get; set; }
    ICollection<Address> Addresses { get; }
    ICollection<OtherType> OtherTypes { get; }
}

public class OtherType { }
public class Address { }

А потом

class PersonModel : IPerson
{
    public PersonModel()
    {
        Addresses = new List<Address>();
        OtherTypes = new List<OtherType>();
    }

    public string Name { get; set; }
    public ICollection<Address> Addresses { get; private set; }
    public ICollection<OtherType> OtherTypes { get; private set; }
}

class PersonViewModel : IPerson
{
    public PersonViewModel()
    {
        Addresses = new ObservableCollection<Address>();
        OtherTypes = new ObservableCollection<OtherType>();
    }

    public string Name { get; set; }
    public ICollection<Address> Addresses { get; private set; }
    public ICollection<OtherType> OtherTypes { get; private set; }
}
person oleksii    schedule 02.02.2012
comment
но это гарантирует, что ViewModel - person hungryMind; 02.02.2012
comment
@Lukazoid, да, я напортачил и исправил код. Теперь это работает. - person oleksii; 02.02.2012
comment
Это подход, который я бы выбрал, если бы делал то, что требовал спрашивающий. - person Lukazoid; 02.02.2012

ViewModel существует для предоставления данных для представления. Это означает, что он должен быть смоделирован в соответствии с требованиями представления. Обычно эти требования не совпадают с требованиями к Модели. Это означает, что обычно ваша модель и ваша модель представления не будут синхронизированы, они будут отличаться. В вашем подходе ViewModel не добавляет никакой ценности и может быть удален.

Для сопоставления между ViewModel и Model вы можете использовать AutoMapper.

person Daniel Hilgarth    schedule 02.02.2012

Ваши реализации должны выглядеть следующим образом:

class PersonModel : IPerson<List> {}
class PersonViewModel : IPerson<ObservableCollection> {}

Вам действительно нужен универсальный класс? ObservableCollection<T> и List<T> оба реализуют ICollection<T>, поэтому вы можете объявить Addresses и OtherTypes в своем интерфейсе как ICollection<Address> и ICollection<OtherType> соответственно.

(что такое AddressView?)

person vc 74    schedule 02.02.2012
comment
Проблема не в классе, а в интерфейсе, мой интерфейс компилируется. Изменен AddressView на адрес - person hungryMind; 02.02.2012

Ваше общее ограничение на IPerson требует, чтобы T реализовывал ICollection из T? Это бесконечно рекурсивно, что недопустимо.

Вы также не можете указать универсальные параметры для универсальных типов, поэтому T<Address> не разрешено, потому что неизвестно, является ли T универсальным типом или нет.

Вы можете изменить свой интерфейс следующим образом:

public interface IPerson<TAddressCol, TOtherCol> 
   where TAddressCol: ICollection<Address>
   where TOtherCol : ICollection<OtherType> 
{
   string Name { get;set;}
   TAddressCol Addresses { get;}
   TAddressCol OtherTypes [ get; } 
}

Затем используйте его так:

class PersonModel<List<Address>, List<OtherType>> {}
class personViewModel<ObservableCollection<Address>, ObservableCollection<OtherType>> {}

Я думаю, что это единственный реальный способ получить подход, который вы хотите. Но я бы предположил, что ваш интерфейс просто возвращает ICollection<Address> и ICollection<OtherType>. Затем ваша модель/модель представления должна будет выставлять коллекции через интерфейс, однако ничто не мешает вам иметь вспомогательные реализации как List или ObservableCollection соответственно.

person Lukazoid    schedule 02.02.2012
comment
Нет, это просто пример. Моя сущность содержит список многих типов. Не рекомендуется указывать все типы - person hungryMind; 02.02.2012
comment
Почему ваш интерфейс не может просто вернуть ICollections? А ваши реализации возвращают ICollections? Тогда каждая реализация может возвращать разные реализации ICollection? - person Lukazoid; 02.02.2012
comment
см. ответ @oleksii о том, что я имею в виду, это то, на что я бы пошел. - person Lukazoid; 02.02.2012