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ステートメントに渡す方法がありますか?
お時間をいただきありがとうございます。
選択を構築する必要があります
.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
によって最終フォームをキャッシュするためにできることは何でも良いことに注意してください。
可能な場合は、リフレクションやハードコードされた文字列から離れます...
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");
あなたの例から、あなたが望むのはこれだと思います:
_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
を使用します。
GetValue
はobject
を返します。これを実際の型にキャストする必要があります。
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();
}
_
あなたはリフレクションでそれを行うことができるはずです。似たようなものを使っています。
これを試してみてください:
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に基づいています
これも使えます。私のために働く。
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();
}