从List <t>获取基于ID </t>的元素

时间:2010-11-08 21:32:34

标签: c# asp.net xml-serialization xml-deserialization generic-list

此问题与此问题有关:Given System.Type T, Deserialize List<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(唯一ID)的项目中的一项:

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 个答案:

答案 0 :(得分:3)

1a上。确保定义属性IUniqueIdentity的所有T实现接口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 :(得分:1)

  

如果所有调用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);
}

答案 2 :(得分:0)

作为对1A的回应,通常的解决方案是将UID属性提取到

interface IHasUID { public int UID { get; } }

ArticleUser以及其他任何实现的内容;然后添加约束

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

如果您无法控制ArticleUser等类型,并且无法让它们实现此界面,那么您基本上是 * *并且您将无法以类型安全的方式执行此操作。

答案 3 :(得分:0)

如果您不介意其成本,请参阅反思中的内容:

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的字段的值。

仅在您无法更改ArticleUser时使用此选项,因为这些应该从一个界面继承,以允许访问UID字段而无需反映他们。

答案 4 :(得分:0)

不确定它是否适合您的系统,但是您是否考虑过实施KeyedCollection而不仅仅是通用列表?

http://msdn.microsoft.com/en-us/library/ms132438%28VS.95%29.aspx

或者可以从基类代码中了解自定义解决方案。

答案 5 :(得分:0)

我这样做的方法是使用lambda。也许我过度简化了你真正想要完成的事情,但是我在MVC5应用程序中通过它的Id从列表中获取一个对象:

...
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();
...

当然,简化示例,但认为这对于提问者或其他人来说可能会派上用场。

答案 6 :(得分:-1)

System.Type已经是一个类型,不需要做typeof。

然后我会这样做: if(T == typeof(Article))     item =((List)mainList).Find(vr =&gt; vr.UID == UID);