LINQ select property by name

You would have to build the select

.Select(x =>x.property).

by hand. Fortunately, it isn't a tricky one since you expect it to always be the same type (string), so:

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

Then the Select above becomes:

.Select(lambda).

(for LINQ based on IQueryable<T>) or

.Select(lambda.Compile()).

(for LINQ based on IEnumerable<T>).

Note that anything you can do to cache the final form by property would be good.


From your examples, I think what you want is this:

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 is a built-in operator in C# that you can "pass" the name of a type to and it will return the corresponding instance of Type. It works at compile-time, not runtime, so it doesn't work like normal functions.

PropertyInfo has a GetValue method that takes an object parameter. The object is which instance of the type to get the property value from. If you are trying to target a static property, use null for that parameter.

GetValue returns an object, which you must cast to the actual type.

person => (string)prop.GetValue(person) is a lamba expression that has a signature like this:

string Foo(Person person) { ... }

If you want this to work with any type of property, make it generic instead of hardcoding 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();
}

I would stay away from reflection and hard coded strings where possible...

How about defining an extension method that accepts a function selector of T, so that you can handle other types beside string properties

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

and imagine that you have a person class that has an id property of type int besides those you already expose

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

all you need to do is fetch the results with type safe lambda selectors

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

Edit

As it seems you really need hardcoded strings as the property inputs, how about leaving out some dynamism and use a bit of determinism

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

and access the desired results as such

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