web-dev-qa-db-ja.com

名前によるLINQ選択プロパティ

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

GetListOf ...メソッドを含むDRYコードがあります。

私はこのようなことができるはずだと感じています

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

しかし、それは有効なコードではありません。機能の作成にはキーが関連していると思います

それが答えである場合、どうすればよいですか?

これはリフレクションを使用した2番目の試みですが、これも実行できません。

        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かもしれないと思うが、とにかく私の作品を見せようと思った。

注サンプルコードは実際には簡略化されています datalist を介してAJAXでオートコンプリートエクスペリエンスを作成するために使用されます。そのオブジェクトには20以上のプロパティがあり、 20以上のメソッドですが、DRYこれを完了する方法があるはずです。また、これを1つのメソッドにすると、コントローラーのアクションもたくさんクリーンアップされます。

質問:

コードの最初のセクションを考えると、これらの同様のメソッドを単一のメソッドに抽象化して、いくつかのオブジェクトをselectステートメントに渡す方法がありますか?

お時間をいただきありがとうございます。

10
WizardHammer

選択を構築する必要があります

.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によって最終フォームをキャッシュするためにできることは何でも良いことに注意してください。

5
Marc Gravell

可能な場合は、リフレクションやハードコードされた文字列から離れます...

Tの関数セレクターを受け入れる拡張メソッドを定義して、文字列プロパティ以外の他の型を処理できるようにする方法

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

すでに公開しているもののほかに、int型のidプロパティを持つpersonクラスがあるとします。

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

タイプセーフなラムダセレクターで結果をフェッチするだけです

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
Dan Dohotaru

あなたの例から、あなたが望むのはこれだと思います:

_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メソッドがあります。オブジェクトは、プロパティ値を取得する型のインスタンスです。 staticプロパティをターゲットにする場合は、そのパラメーターにnullを使用します。

GetValueobjectを返します。これを実際の型にキャストする必要があります。

person => (string)prop.GetValue(person)は、次のようなシグネチャを持つランバ式です。

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();
}
_
3
JamesFaix

あなたはリフレクションでそれを行うことができるはずです。似たようなものを使っています。

これを試してみてください:

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に基づいています

2

これも使えます。私のために働く。

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();
    }
1
Allard Vos