LINQ按名称选择属性

时间:2017-12-12 21:01:36

标签: c# linq expression

我正在尝试在LINQ select语句中使用变量。

这是我现在正在做的一个例子。

using System;
using System.Collections.Generic;
using System.Linq;
using Faker;

namespace ConsoleTesting
{
internal class Program
{
    private static void Main(string[] args)
    {
        List<Person> listOfPersons = new List<Person>
        {
            new Person(),
            new Person(),
            new Person(),
            new Person(),
            new Person(),
            new Person(),
            new Person(),
            new Person(),
            new Person(),
            new Person(),
            new Person()
        };

        var firstNames = Person.GetListOfAFirstNames(listOfPersons);

        foreach (var item in listOfPersons)
        {
            Console.WriteLine(item);
        }

        Console.WriteLine();
        Console.ReadKey();
    }


    public class Person
    {
        public string City { get; set; }
        public string CountryName { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public Person()
        {
            FirstName = NameFaker.Name();
            LastName = NameFaker.LastName();
            City = LocationFaker.City();
            CountryName = LocationFaker.Country();
        }

        public static List<string> GetListOfAFirstNames(IEnumerable<Person> listOfPersons)
        {
            return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
        }

        public static List<string> GetListOfCities(IEnumerable<Person> listOfPersons)
        {
            return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
        }

        public static List<string> GetListOfCountries(IEnumerable<Person> listOfPersons)
        {
            return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
        }

        public static List<string> GetListOfLastNames(IEnumerable<Person> listOfPersons)
        {
            return listOfPersons.Select(x => x.FirstName).Distinct().OrderBy(x => x).ToList();
        }
    }
}
}

我有一些非常不带DRID代码的GetListOf ...方法

我觉得我应该可以做这样的事情

public static List<string> GetListOfProperty(
IEnumerable<Person> listOfPersons, string property)
        {
            return listOfPersons.Select(x =>x.property).Distinct().OrderBy(x=> x).ToList();
        }

但这不是虚拟代码。我认为关键是要创建一个功能

如果这就是答案我该怎么做?

这是使用refelection的第二次尝试但是这也是不行的。

        public static List<string> GetListOfProperty(IEnumerable<Person> 
listOfPersons, string property)
        {
            Person person = new Person();
            Type t = person.GetType();
            PropertyInfo prop = t.GetProperty(property);
            return listOfPersons.Select(prop).Distinct().OrderBy(x => 
x).ToList();
}

我认为反思可能是一个DeadEnd / red herring,但我认为无论如何我都会展示我的作品。

注意实际上简化了示例代码,这用于通过AJAX填充datalist以创建自动完成体验。该对象具有20多个属性,我可以通过编写20多种方法来完成,但我觉得应该有一种干燥的方法来完成它。同样制作这个方法也会清理我的控制器动作。

问题:

鉴于代码的第一部分是有一种方法将这些类似的方法抽象为单个方法,请将一些对象传递给select语句???

感谢您的时间。

6 个答案:

答案 0 :(得分:4)

您必须构建选择

.Select(x =>x.property).

手工。幸运的是,它并不是一个棘手的问题,因为你希望它总是相同的类型(string),所以:

var x = Expression.Parameter(typeof(Person), "x");
var body = Expression.PropertyOrField(x, property);
var lambda = Expression.Lambda<Func<Person,string>>(body, x);

然后上面的Select成为:

.Select(lambda).

(对于基于IQueryable<T>的LINQ)或

.Select(lambda.Compile()).

(对于基于IEnumerable<T>的LINQ)。

请注意,您可以通过property缓存最终表单的任何操作都可以。

答案 1 :(得分:3)

从您的示例中,我认为您想要的是:

public static List<string> GetListOfProperty(IEnumerable<Person> 
    listOfPersons, string property)
{
    Type t = typeof(Person);         
    PropertyInfo prop = t.GetProperty(property);
    return listOfPersons
        .Select(person => (string)prop.GetValue(person))
        .Distinct()
        .OrderBy(x => x)
        .ToList();

}

typeof是C#中的内置运算符,您可以将类型的名称“传递”到它,它将返回Type的相应实例。它在编译时工作,而不是运行时,因此它不像普通函数那样工作。

PropertyInfo有一个GetValue方法,它接受一个object参数。该对象是从中获取属性值的类型的实例。如果您要尝试定位static属性,请对该参数使用null

GetValue会返回object,您必须将其转换为实际类型。

person => (string)prop.GetValue(person)是一个lamba表达式,其签名如下:

string Foo(Person person) { ... }

如果您希望此功能适用于任何类型的属性,请将其设为通用而非硬编码string

public static List<T> GetListOfProperty<T>(IEnumerable<Person> 
    listOfPersons, string property)
{
    Type t = typeof(Person);         
    PropertyInfo prop = t.GetProperty(property);
    return listOfPersons
        .Select(person => (T)prop.GetValue(person))
        .Distinct()
        .OrderBy(x => x)
        .ToList();
}

答案 2 :(得分:3)

在可能的情况下,我会远离反射和硬编码字符串......

如何定义接受T的函数选择器的扩展方法,以便您可以处理字符串属性旁边的其他类型

public static List<T> Query<T>(this IEnumerable<Person> instance, Func<Person, T> selector)
{
    return instance
        .Select(selector)
        .Distinct()
        .OrderBy(x => x)
        .ToList();
}

并且假设您有一个person类,除了您已经公开的

之外,其id属性类型为int
public class Person
{
    public int Id { get; set; }
    public string City { get; set; }
    public string CountryName { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

您需要做的就是使用类型安全的lambda选择器

获取结果
var ids = listOfPersons.Query(p => p.Id);
var firstNames = listOfPersons.Query(p => p.FirstName);
var lastNames = listOfPersons.Query(p => p.LastName);
var cityNames = listOfPersons.Query(p => p.City);
var countryNames = listOfPersons.Query(p => p.CountryName);

修改

因为看起来你真的需要硬编码字符串作为属性输入,如何省略一些动态并使用一些决定论

public static List<string> Query(this IEnumerable<Person> instance, string property)
{
    switch (property)
    {
        case "ids": return instance.Query(p => p.Id.ToString());
        case "firstName": return instance.Query(p => p.FirstName);
        case "lastName": return instance.Query(p => p.LastName);
        case "countryName": return instance.Query(p => p.CountryName);
        case "cityName": return instance.Query(p => p.City);
        default: throw new Exception($"{property} is not supported");
    }
}

并以此方式访问所需的结果

var cityNames = listOfPersons.Query("cityName");

答案 3 :(得分:2)

你应该能够用Reflection做到这一点。我用它类似的东西。

只需改变你的反思尝试:

public static List<string> GetListOfValues(IEnumerable<Person> listOfPersons, string propertyName)
{
    var ret = new List<string>();

    PropertyInfo prop = typeof(Person).GetProperty(propertyName);
    if (prop != null)
        ret = listOfPersons.Select(p => prop.GetValue(p).ToString()).Distinct().OrderBy(x => x).ToList();

    return ret;
}

我希望它有所帮助。

  

它基于C#6

答案 4 :(得分:0)

您也可以使用它。为我工作。

public static class ObjectReflectionExtensions
{
    public static  object GetValueByName<T>(this T thisObject,  string propertyName)
    {
        PropertyInfo prop = typeof(T).GetProperty(propertyName);
        return prop.GetValue(thisObject);

    }
}

像这样打电话。

public static List<string> GetListOfProperty(IEnumerable<Person> listOfPersons, string propertyName)
    {
        return listOfPersons.Select(x =>(string)x.GetValueByName(propertyName)).Distinct().OrderBy(x=> x).ToList();
    }

答案 5 :(得分:-1)

如果要选择所有值:

object[] foos = objects.Select(o => o.GetType().GetProperty("PropertyName").GetValue(o)).ToArray();