Получить элемент из списка <T>на основе идентификатора - PullRequest
3 голосов
/ 09 ноября 2010

Этот вопрос связан с этим вопросом: Данный System.Type T, список десериализации

Учитывая эту функцию, чтобы получить список всех элементов ...

 public static List<T> GetAllItems<T>()
 {
    XmlSerializer deSerializer = new XmlSerializer(typeof(List<T>));
    TextReader tr = new StreamReader(GetPathBasedOnType(typeof(T)));
    List<T> items = (List<T>)deSerializer.Deserialize(tr);
    tr.Close();
 }

... Я хочу создать функцию для извлечения только одного элемента из тех, для которых необходим UID (уникальный идентификатор):

public static System.Object GetItemByID(System.Type T, int UID)
{

    IList mainList = GetAllItems<typeof(T)>();
    System.Object item = null;

    if (T == typeof(Article))
        item = ((List<Article>)mainList).Find(
            delegate(Article vr) { return vr.UID == UID; });
    else if (T == typeof(User))
        item = ((List<User>)mainList).Find(
            delegate(User ur) { return ur.UID == UID; });

    return item;
}

Однако это не работает, поскольку GetAllItems<typeof(T)>(); вызов сформирован неправильно.

Вопрос 1a : Как я могу исправить вторую функцию для правильного возврата уникального элемента, учитывая, что все классы, которые будут вызывать GetItemByID (), имеют UID в качестве элемента в них? Я хотел бы иметь возможность сделать public static <T> GetItemByID<T>(int UID), если это возможно.

Вопрос 1b : тот же вопрос, но предположим, что я не могу изменить прототип функции GetItemByID?

Ответы [ 7 ]

3 голосов
/ 09 ноября 2010

1а.Убедитесь, что все T реализуют интерфейс IUniqueIdentity, который определяет свойство UID, а затем ограничивают универсальный метод для приема только IUniqueIdentity типов.Итак:

public static T GetItemById<T>(int UID) where T:IUniqueIdentity
{
    IList<T> mainList = GetAllItems<T>();

    //assuming there is 1 or 0 occurrences, otherwise FirstOrDefault
    return mainList.SingleOrDefault(item=>item.UID==UID); 

}

public interface IUniqueIdentity
{
    int UID{get;}
}
1 голос
/ 09 ноября 2010

Как я могу исправить вторую функцию, чтобы она правильно возвращала уникальный элемент, если все классы, которые будут вызывать GetItemByID (), имеют UID в качестве элемента в них?

Измените метод GetItemByID, чтобы он сам был универсальным: public static T GetItemByID<T>(int UID)

Тот же вопрос, но предположим, что я не могу изменить прототип функции GetItemByID

GetAllItems<typeof(T)>() не работает, как вы заметили, поскольку вызов универсальных методов требует знания типа T во время компиляции. Вместо этого используйте MethodInfo.MakeGenericMethod(), чтобы создать ссылку на закрытую форму для метода для конкретного Type из T.

public static System.Object GetItemByID(System.Type type, int UID) 
{
    Type ex = typeof(YourClass);
    MethodInfo mi = ex.GetMethod("GetItemByID");

    MethodInfo miConstructed = mi.MakeGenericMethod(type);

    // Invoke the method.
    object[] args = new[] {UID};
    return miConstructed.Invoke(null, args);
}
0 голосов
/ 29 апреля 2014

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

...
List<TodoModel> todos = new List<TodoModel>() {
    new TodoModel() {
        id = 0,
        name = "Laundry!",
        desc = "Whites and darks."
    },
    new TodoModel() {
        id = 1,
        name = "Dishes",
        desc = "Oh, first buy dishes."
    },
    new TodoModel() {
        id = 2,
        name = "Twitter Bootstrap",
        desc = "Check out Grids."
    }
};

ViewBag.Item = todos.Find(o => ((TodoModel)o).id == id);

return View();
...

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

0 голосов
/ 09 ноября 2010

Не уверен, что он вписывается в вашу систему как есть, но задумывались ли вы о реализации KeyedCollection вместо простого списка?в пользовательское решение из кода базового класса.

0 голосов
/ 09 ноября 2010

Вот что-то в Reflection, если вы не возражаете против его стоимости:

public static System.Object GetItemByID(System.Type T, int UID)
{

    IList mainList = GetAllItems().OfType<typeof(T)>();

    return mainList.Find(i => (int)(i.GetType().GetFields().Where(f => f.Name == "UID").FirstOrDefault().GetValue(i)) == UID);

}

В основном он использует Reflection для получения значения поля с именем UID.

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

0 голосов
/ 09 ноября 2010

System.Type уже является типом, нет необходимости делать typeof.

, тогда я бы сделал: if (T == typeof (Article)) item = ((List) mainList) .Find (vr=> vr.UID == UID);

0 голосов
/ 09 ноября 2010

В ответ на ваш 1A, обычное решение состоит в том, чтобы извлечь свойство UID в

interface IHasUID { public int UID { get; } }

, которое оба Article, User и все остальное реализуют;а затем добавьте ограничение

public static T GetItemByID<T>(int UID) where T : IHasUID

Если у вас нет контроля над типами Article, User и др., и вы не можете заставить их реализовать этот интерфейс, тогда выв основном ** и вы не сможете сделать это безопасным способом.

...