Где () на абстрактном уровне, чтобы вернуть себя - PullRequest
3 голосов
/ 18 сентября 2010

У меня есть этот код, и я хочу, чтобы он был элегантным.
Я застрял в этой проблеме наследования, и мне пришлось бы испортить код, если я это сделаю.
Помоги мне сохранить элегантность Я не против вносить изменения где-либо вверх и вниз по иерархии; смело меняйте ядро.

У меня есть эти абстрактные классы (я пропустил несвязанную реализацию, чтобы содержание вопроса было коротким).

public abstract class Entity : IComparable 
{ 
    protected int _ID;
    public abstract int ID { get; } 
}    
public abstract class SortedEntities<T> : IEnumerable<T> where T : Entity 
{ 
    Dictionary<int,T> _Entities;
}

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

public class Contact : Entity { }
public class Contacts : SortedEntities<Contact> { }

И у меня также есть нечто большее, чем просто Contact и Contacts, которые наследуются от Entity и SortedEntities, которые действуют одинаково.

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

Contacts LoadedContacts = new Contacts();  // load and fill somewhere else
// !!!NOT IMPLEMENTED YET!!!
Contacts SelectedContacts = LoadedContacts.GetFromIDList("1,4,7");

Где он возвращает новый Contacts объект, заполненный этими Contact объектами предоставленного идентификатора.
Итак, чтобы разрешить этот код для всех классов, унаследованных от SortedEntities, я подумал добавить этот образный код в реферат:

public abstract class SortedEntities<T> : IEnumerable<T> where T : Entity 
{
    // !!!NOT REAL CODE YET!!!
    public abstract this<T> GetFromIDList(string IDCSV)
    {
        List<string> idlist = IDCSV.Split(',').ToList<string>();
        return this.Where(entity => idlist.Contains(entity.ID.ToString()));
    }
}

Но, очевидно, this<T> недопустимо.
То, что я пытаюсь сказать компилятору, это сделать тип возвращаемого значения этого метода типом наследуемого класса вниз по иерархии.
Таким образом, если кто-то вызовет LoadedContacts.GetFromIDList("1,4,7"), он вернет Contacts без необходимости приводить его к SortedEntities<T>, если я сделаю его типом возврата, что также потребует от меня переопределения метода в каждом наследующем классе, чтобы скрыть абстрактный метод ,

Я забыл что-то, что уже знаю?
Или это совершенно невозможно, и мне нужно переопределить и скрыть метод в иерархии для всех наследующих классов?

1 Ответ

1 голос
/ 18 сентября 2010

Распространенным решением является добавление другого параметра универсального типа, который ссылается на «текущий» тип (например, this относится к «текущему» объекту):

public abstract class Entity : IComparable
{
    protected int _ID;
    public abstract int ID { get; }
}

public abstract class SortedEntities<TEntities, TEntity> : IEnumerable<TEntity>
    where TEntities : SortedEntities<TEntities, TEntity>, new()
    where TEntity : Entity
{
    Dictionary<int, TEntity> _Entities;

    public TEntities GetFromIDList(string IDCSV)
    {
        List<string> ids = IDCSV.Split(',').ToList<string>();
        return new TEntities
        {
            _Entities = this.Where(entity => ids.Contains(entity.ID.ToString()))
                            .ToDictionary(e => e.ID)
        };
    }
}

Использование:

public class Contact : Entity
{
}

public class Contacts : SortedEntities<Contacts, Contact>
{
}

Обратите внимание, что TEntities ограничено SortedEntities<TEntities, TEntity>.Это на самом деле не означает, что TEntities может ссылаться только на текущий класс, но если вы следуете шаблону, позволяющему классу X наследовать от SortedEntities<X, Y>, он должен работать.

new() требуется ограничение, чтобы вы могли создать новый экземпляр «текущего» класса.

Эрик Липперт указал где-то еще, что ему не нравится этот шаблон.Действуйте на свой страх и риск!

...