Интерфейсы C # с родовыми типами, ссылающимися друг на друга - PullRequest
0 голосов
/ 10 мая 2018

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

Однако я подумал, что было бы удобнее иметь классы, которые наследуют List и имеют там необходимые методы и больше не делают их статичными, поэтому они применяются к любому списку объектов, которые вы используете.

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

Пример:

public class Student 
{
    public int Id {get;set;}
    public string Name {get;set;}
}
public class Students : List<Student>
{
    public Student GetTopStudent()
    {
        return this.OrderByDescending(s => s.Grade).FirstOrDefault();
    }

    public Students GetPassingStudents()
    {
        return this.Where(s => s.Grade > 0.7).ToCollection();
    }

    public Students ToCollection(IEnumerable<Student> studentsList)
    {
        var students = new Students();
        foreach(var s in studentsList)
        {
            students.Add(s);
        }
        return students();
    ]
}

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

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

Класс коллекции и интерфейсы

public abstract class Collection<T> : List<T>
{
    // Needed tie in the extension method with the List.Add() Method
    public void Add(object item) => base.Add((T)item);
}

public interface IObjectWithId<T> 
    where T : IObjectWithId<T>
{
    int Id {get;set;}
}

public ICollectionItem<C> 
    where C : Collection<IcollectionItem<C>>
{
}

public ICollectionItemWithId<C,T> 
    where C : Collection<ICollectionItemWithId<C,T>>
    where T : IObjectWithId<T>
{
}

Расширения

public static List<T> Get<T>(this IEnumerable<IobjectWithId<T>> list, List<int> ids)
    where T : IObjectWithId<T>
{
    return list.Where(i => ids.Contains(i.Id))
        .Cast<T>();
        .ToList();
}

public static C Get<C, T>(this IEnumerable<IcollectionItemWithId<C, T>> list, List<int> ids)
    where C : Collection<ICollectionItemWithId<C, T>>, new()
    where T : IObjectWithId<T>
{
    return list.Where(i => ids.Contains(i.Id)).ToCollection();
}

public static C ToCollection<C, T>(this IEnumerable<ICollectionItemWithId<C, T>> list)
    where C : Collection<ICollectionItemWithId<C, T>>, new()
    where T : IObjectWithId<T>
{
    var collection = new C();
    foreach(var item in list)
    {
        collection.Add(item);
    }
    return collection;
}

public static C ToCollection<C>(this IEnumerable<ICollectionItem<C>> list)
where C : Collection<ICollectionItem<C>>, new()
{
    var collection = new C();
    foreach(var item in list)
    {
        collection.Add(item);
    }
    return collection;
}

Мне не удалось заставить этот код работать. Обычно я получаю ошибки после его создания, либо нет неявного преобразования ссылок из класса ученика в список учеников, либо ошибка, связанная с боксом.

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

1 Ответ

0 голосов
/ 10 мая 2018

Вы слишком усложняете вещи.Коренная проблема - ваша любопытная ссылка на себя public interface IObjectWithId<T>.Поскольку вы нигде не используете T в самом интерфейсе, это не обязательно должно быть универсальным.

Просто напишите методы расширения, которые работают на IEnumerable<T>, и верните IEnumerable<T> с некоторыми простыми ограничениями на T, например

// This does not need to be generic in any way
public interface IObjectWithId
{
    int Id {get;set;}
}

public static IEnumerable<T> Get<T>(this IEnumerable<T> list, List<int> ids)
    where T : IObjectWithId
{
    return list.Where(i => ids.Contains(i.Id));
}

Пусть звонящие на цепочку .ToList() на это, если захотят.Иногда им может понадобиться только один (Single() или First()), или они могут выполнить другую фильтрацию или групповую операцию после этого, поэтому принудительное возвращение его к List<T> неэффективно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...