C# 对列表排序<;T>;使用查询表达式

C# 对列表排序<;T>;使用查询表达式,c#,linq,sorting,expression,C#,Linq,Sorting,Expression,我在使用Linq订购这样的结构时遇到问题: public class Person { public int ID { get; set; } public List<PersonAttribute> Attributes { get; set; } } public class PersonAttribute { public int ID { get; set; } public string Name { get; set; } pub

我在使用Linq订购这样的结构时遇到问题:

public class Person
{
    public int ID { get; set; }
    public List<PersonAttribute> Attributes { get; set; }
}

public class PersonAttribute
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
}
我想使用LINQ投影对按我选择的person属性升序的人员列表进行排序,例如,按年龄排序,或按名字排序

我正在尝试类似的东西

string mySortAttribute = "Age"
PersonList.OrderBy(p => p.PersonAttribute.Find(s => s.Name == mySortAttribute).Value);

但是语法让我失望了。有什么线索吗?

为什么不使用键值字典而不是列表?我想这会更合适,而且会让其他事情变得更容易

更新-如下所示:

PersonAttribute Age = new PersonAttribute { ID = 8, Name = "Age", Value = "32" };
PersonAttribute FirstName = new PersonAttribute { ID = 9, Name = "FirstName", Value = "Rebecca" };
PersonAttribute LastName = new PersonAttribute { ID = 10, Name = "LastName", Value = "Johnson" };
PersonAttribute Gender = new PersonAttribute { ID = 11, Name = "Gender", Value = "Female" };
public class Person
{
  public Dictionary<string, string> Attributes = new Dictionary<string,string>();
}

List<Person> people = new List<Person>();

Person rebecca = new Person();
rebecca.Attributes["Age"] = "32";
rebecca.Attributes["FirstName"] = "Rebecca";
rebecca.Attributes["LastName"] = "Johnson";
rebecca.Attributes["Gender"] = "Female";
people.Add(rebecca);

var PeopleInAgeOrder = people.OrderBy(p => p.Attributes["Age"]);
公共类人物
{
公共字典属性=新字典();
}
列表人员=新列表();
人物丽贝卡=新人();
丽贝卡。属性[“年龄”]=“32”;
rebecca.Attributes[“FirstName”]=“rebecca”;
属性[“LastName”]=“Johnson”;
丽贝卡。属性[“性别”]=“女性”;
人物。添加(丽贝卡);
var PeopleInAgeOrder=people.OrderBy(p=>p.Attributes[“Age”]);

可能是您的语法错误吗?您的属性称为Attributes,但您是否在代码中使用了ObjectSettings?或者这是一个打字错误

如果是,那么代码看起来很好,除非不是所有Person实例都具有您试图按其排序的属性,在这种情况下,您会得到一个异常

编辑: 另外,不要使用Find,而是先尝试使用

PersonList.OrderBy(p => p.Attributes.First(a => a.Name == "Age").Value)

我想你会得到一个例外,其中一个项目没有年龄属性。 我尝试了下面的代码,效果很好——我猜你的数据有点不对劲,正如其他海报所指出的。无论如何,下面的工作很好

    List<Person> personList = new List<Person>();
    Random rand = new Random();

    //generate 50 random persons
    for (int i = 0; i < 50; i++)
    {
        Person p = new Person();
        p.Attributes = new List<PersonAttribute>();
        p.Attributes.Add(new PersonAttribute() { ID = 8, Name = "Age", Value = rand.Next(0, 100).ToString() });
        p.Attributes.Add(new PersonAttribute() { ID = 10, Name = "Name", Value = rand.Next(0, 100).ToString() });
        personList.Add(p);
    }

    var finalList = personList.OrderBy(c => c.Attributes.Find(a => a.Name == "Age").Value).ToList();
List personList=new List();
Random rand=新的Random();
//随机产生50个人
对于(int i=0;i<50;i++)
{
人员p=新人员();
p、 属性=新列表();
p、 Add(newpersonattribute(){ID=8,Name=“Age”,Value=rand.Next(0100.ToString()});
p、 Add(newpersonattribute(){ID=10,Name=“Name”,Value=rand.Next(0100.ToString()});
添加(p);
}
var finalList=personList.OrderBy(c=>c.Attributes.Find(a=>a.Name==“Age”).Value).ToList();

这是假设属性类实现IComparable或具有一个很好的ToString函数(我希望如此)

否则,语法会变得更加复杂:

var list = personList
            .OrderBy(p => 
                     p.Attributes.FirstOrDefault(a => a.Name == "Age") == null ?
                     "" : p.Attributes.First(a => a.Name == "Age").Value
            );

我还假设每个键都有一个值,否则需要更智能的代码…;-)

OrderBy
是产生新序列的LINQ扩展。要对现有序列进行排序,您需要添加一两个扩展方法。。。然后您可以使用:

PersonList.Sort(p => p.Attributes.Find(
  s => s.Name == mySortAttribute).Value);

public static class ListExtensions {
  public static void Sort<TSource, TValue>(
    this List<TSource> source,
    Func<TSource, TValue> selector)
  {
    var comparer = Comparer<TValue>.Default;
    source.Sort((x, y) => comparer.Compare(selector(x), selector(y)));
  }
  public  static void SortDescending<TSource, TValue>(
    this List<TSource> source,
    Func<TSource, TValue> selector)
  {
    var comparer = Comparer<TValue>.Default;
    source.Sort((x, y) => comparer.Compare(selector(y), selector(x)));
  }
}
PersonList.Sort(p=>p.Attributes.Find(
s=>s.Name==myortAttribute.Value);
公共静态类ListExtensions{
公共静态无效排序(
此列表来源:,
Func选择器)
{
var comparer=comparer.Default;
Sort((x,y)=>comparer.Compare(选择器(x),选择器(y));
}
公共静态无效排序(
此列表来源:,
Func选择器)
{
var comparer=comparer.Default;
Sort((x,y)=>comparer.Compare(选择器(y),选择器(x));
}
}

我知道这是一篇老文章,但我想我应该发布一个我刚才找到的比较器,以防其他人需要它

public class GenericComparer<T> : IComparer<T>
 {
     public string SortExpression { get; set; }
     public int SortDirection { get; set; } // 0:Ascending, 1:Descending

     public GenericComparer(string sortExpression, int sortDirection)
     {
         this.SortExpression = sortExpression;
         this.SortDirection = sortDirection;
     }
     public GenericComparer() { }

     #region IComparer<T> Members
     public int Compare(T x, T y)
     {
         PropertyInfo propertyInfo = typeof(T).GetProperty(SortExpression);
         IComparable obj1 = (IComparable)propertyInfo.GetValue(x, null);
         IComparable obj2 = (IComparable)propertyInfo.GetValue(y, null);

         if (SortDirection == 0)
         {
             return obj1.CompareTo(obj2);
         }
         else return obj2.CompareTo(obj1);
     }
     #endregion
 }
公共类GenericComparer:IComparer
{
公共字符串排序表达式{get;set;}
public int SortDirection{get;set;}//0:升序,1:降序
公共泛型比较器(字符串排序表达式,int-sortDirection)
{
this.SortExpression=SortExpression;
this.SortDirection=SortDirection;
}
公共泛型比较器(){}
#地区I比较成员
公共整数比较(TX,TY)
{
PropertyInfo PropertyInfo=typeof(T).GetProperty(SortExpression);
IComparable obj1=(IComparable)propertyInfo.GetValue(x,null);
IComparable obj2=(IComparable)propertyInfo.GetValue(y,null);
if(SortDirection==0)
{
返回obj1.CompareTo(obj2);
}
否则返回obj2.CompareTo(obj1);
}
#端区
}
用法

List<MyObject> objectList = GetObjects(); /* from your repository or whatever */
objectList.Sort(new GenericComparer<MyObject>("ObjectPropertyName", (int)SortDirection.Descending));
dropdown.DataSource = objectList;
dropdown.DataBind();
List objectList=GetObjects();/*从您的存储库或其他任何地方*/
排序(新的GenericComparer(“ObjectPropertyName”,(int)SortDirection.Descending));
dropdown.DataSource=objectList;
dropdown.DataBind();

您可以重载构造函数以接受SortDirection枚举。我没有这样做,因为该类位于库中,没有对System.Web的引用。

某些情况下,您需要考虑:

  • 由于您的属性在字符串中,因此年龄“30”和“3”将在年龄“4”之前排序
  • 该属性可能不存在
如果创建此扩展方法类:

public static class ListExtenstions
{
    public static List<Person> OrderList(this List<Person> list, string attributeName, PersonAttribute defaultAttribute)
    {
        return OrderList(list, attributeName, defaultAttribute, x => x);
    }

    public static List<Person> OrderList<T>(this List<Person> list, string attributeName, PersonAttribute defaultAttribute, Func<string, T> convertion)
    {
        return list.OrderBy(x => convertion((x.Attributes.FirstOrDefault(y => y.Name == attributeName) ?? defaultAttribute).Value)).ToList();

        // Query Syntax
        //return
        //    (from p in list
        //     let attribute = p.Attributes.FirstOrDefault(a => a.Name == attributeName) ?? defaultAttribute
        //     orderby attribute.Value
        //     select p).ToList();
    }
}

我已经修复了打字错误,发现我的问题是IQueryable和List,这需要一个演员阵容。谢谢你的帮助。这并不能回答他的问题。他没有问如何重组数据,而是问如何对(可能是现有的,也可能是不可更改的遗留)数据结构进行排序。
public static class ListExtenstions
{
    public static List<Person> OrderList(this List<Person> list, string attributeName, PersonAttribute defaultAttribute)
    {
        return OrderList(list, attributeName, defaultAttribute, x => x);
    }

    public static List<Person> OrderList<T>(this List<Person> list, string attributeName, PersonAttribute defaultAttribute, Func<string, T> convertion)
    {
        return list.OrderBy(x => convertion((x.Attributes.FirstOrDefault(y => y.Name == attributeName) ?? defaultAttribute).Value)).ToList();

        // Query Syntax
        //return
        //    (from p in list
        //     let attribute = p.Attributes.FirstOrDefault(a => a.Name == attributeName) ?? defaultAttribute
        //     orderby attribute.Value
        //     select p).ToList();
    }
}
List<Person> persons = ...
...
PersonAttribute defaultAttribute = new PersonAttribute() { Value = "0" };
var ordered = persons.OrderList("Age", defaultAttribute, x => Convert.ToInt32(x));
List<Person> persons = ...
...
PersonAttribute defaultAttribute = new PersonAttribute() { Value = String.Empty };
var ordered persons.OrderList("Name", defaultAttribute);