【问题标题】:Get an element from List<T> based on ID根据 ID 从 List<T> 中获取一个元素
【发布时间】:2010-11-08 21:32:34
【问题描述】:

这个问题和这个问题有关: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&lt;typeof(T)&gt;(); 调用的格式不正确。

问题 1a:考虑到所有将调用 GetItemByID() 的类都将 UID 作为其中的元素,我如何修复第二个函数以正确返回唯一元素?如果可能的话,我希望能够做到public static &lt;T&gt; GetItemByID&lt;T&gt;(int UID)

问题 1b:同样的问题,但假设我不能修改 GetItemByID 的函数原型?

【问题讨论】:

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


    【解决方案1】:

    1a。确保所有 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;}
    }
    

    【讨论】:

    • 有趣 - 看起来更干净。但是,当我尝试编译它时,出现错误 "Error 12 Cannot convert lambda expression to delegate type 'System.Func&lt;T,bool&gt;' because some of the return types in the block are not implicitly convertible to the delegate return type"
    • 我在 lambda 中使用了赋值而不是相等。固定。
    【解决方案2】:

    鉴于所有将调用 GetItemByID() 的类都将 UID 作为其中的元素,我如何修复第二个函数以正确返回唯一元素?

    将 GetItemByID 方法修改为自身为泛型:public static T GetItemByID&lt;T&gt;(int UID)

    同样的问题,但假设我无法修改 GetItemByID 的函数原型

    GetAllItems&lt;typeof(T)&gt;() 不起作用,正如您所指出的,因为调用泛型方法需要在编译时了解类型 T。而是使用MethodInfo.MakeGenericMethod() 为特定的T 的Type 方法创建一个封闭的表单引用。

    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);
    }
    

    【讨论】:

      【解决方案3】:

      如果您不介意它的成本,这里有一些反射:

      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);
      
      }
      

      它基本上使用反射来检索名为UID 的字段的值。

      仅当您无法更改ArticleUser 时才使用此选项,因为它们应该从一个接口继承以允许访问UID 字段而无需反映它们。

      【讨论】:

        【解决方案4】:

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

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

        或者可能从基类代码中对自定义解决方案有所了解。

        【讨论】:

          【解决方案5】:

          针对您的 1A,通常的解决方案是将 UID 属性提取到一个

          interface IHasUID { public int UID { get; } }
          

          ArticleUser 和其他任何实现;然后添加一个约束

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

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

          【讨论】:

            【解决方案6】:

            我这样做的方式是使用 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();
            ...
            

            当然是简化的例子,但认为这对提问者或其他人可能会派上用场。

            【讨论】:

              【解决方案7】:

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

              那么我会这样做: if (T == typeof(Article)) item = ((List)mainList).Find(vr => vr.UID == UID);

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-11-09
                • 1970-01-01
                • 2014-09-17
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多