プロパティ自体を関数に渡すメソッドを探しています。プロパティの値ではありません。関数は、ソートに使用されるプロパティを事前に知りません。この例の最も簡単な方法は、異なるパラメータータイプで4つの上書きを作成することです。他の方法は、typeof()
を関数内で使用することです。 Class1に数百のプロパティがある場合、これらの方法はどちらも受け入れられません。これまでのところ、私は次の方法を見つけました:
class Class1
{
string vehName;
int maxSpeed;
int fuelCapacity;
bool isFlying;
}
class Processor
{
List<Class1> vehicles = null;
Processor(List<Class1> input)
{
vehicles = input;
}
List<Class1> sortBy(List<Class1> toSort, string propName)
{
if (toSort != null && toSort.Count > 0)
{
return toSort.OrderBy(x => typeof(Class1).GetProperty(propName).GetValue(x, null)).ToList();
}
else return null;
}
}
class OuterUser
{
List<Class1> vehicles = new List<Class1>();
// ... fill the list
Processor pr = new Processor(vehicles);
List<Class1> sorted = pr.sortBy("maxSpeed");
}
文字列を処理関数に渡す際の「人為的エラー」のリスクがあるため、この方法は好きではありません。文字列がコードの他の部分によって生成されると、これはさらにevenくなります。 Class1プロパティをさらに処理するために関数に渡すことを実装するよりエレガントな方法を提案してください。 IMHOを使用するための最適なオプションは次のとおりです(またはこのようなもの)。
vehicles = sortBy(vehicles, Class1.maxSpeed);
プロパティアクセサをメソッドに渡すことができます。
List<Class1> SortBy(List<Class1> toSort, Func<Class1, IComparable> getProp)
{
if (toSort != null && toSort.Count > 0) {
return toSort
.OrderBy(x => getProp(x))
.ToList();
}
return null;
}
次のように呼び出します。
var result = SortBy(toSort, x => x.maxSpeed);
しかし、さらに一歩進んで、独自の拡張メソッドを作成することもできます。
public static class CollectionExtensions
{
public static List<TSource> OrderByAsListOrNull<TSource, TKey>(
this ICollection<TSource> collection, Func<TSource,TKey> keySelector)
if (collection != null && collection.Count > 0) {
return collection
.OrderBy(x => keySelector(x))
.ToList();
}
return null;
}
}
これで、このようにソートできます
List<Class1> sorted = toSort.OrderByAsListOrNull(x => x.maxSpeed);
だけでなく
Person[] people = ...;
List<Person> sortedPeople = people.OrderByAsListOrNull(p => p.LastName);
最初のパラメーターをICollection<T>
として宣言したことに注意してください。これは、次の2つの条件を満たす必要があるためです。
Count
プロパティが必要ですOrderBy
を適用するには、IEnumerable<T>
である必要があります。List<Class1>
はICollection<T>
ですが、他の多くのコレクションと同様に配列Person[]
でもあります。
これまで、プロパティの読み取り方法を示してきました。メソッドでプロパティを設定する必要がある場合は、同様にセッターデリゲートを渡す必要があります
void ReadAndWriteProperty(Func<Class1, T> getProp, Action<Class1, T> setProp)
ここで、T
はプロパティのタイプです。
ラムダ式を使用して、プロパティ情報を渡すことができます。
void DoSomething<T>(Expression<Func<T>> property)
{
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if (propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}
}
使用法:
DoSomething(() => this.MyProperty);
@MatthiasGの答えから欠落しているのは、名前だけでなくプロパティ値を取得する方法です。
public static string Meth<T>(Expression<Func<T>> expression)
{
var name = ((MemberExpression)expression.Body).Member.Name;
var value = expression.Compile()();
return string.Format("{0} - {1}", name, value);
}
つかいます:
Meth(() => YourObject.Property);
ここで素晴らしいソリューション...
void GetString<T>(string input, T target, Expression<Func<T, string>> outExpr)
{
if (!string.IsNullOrEmpty(input))
{
var expr = (MemberExpression) outExpr.Body;
var prop = (PropertyInfo) expr.Member;
prop.SetValue(target, input, null);
}
}
void Main()
{
var person = new Person();
GetString("test", person, x => x.Name);
Debug.Assert(person.Name == "test");
}
これにLinqを使用しないのはなぜですか?のような:
vehicles.OrderBy(v => v.maxSpeed).ToList();
上記の回答から追加するだけです。また、注文の方向に簡単なフラグを立てることもできます。
public class Processor
{
public List<SortableItem> SortableItems { get; set; }
public Processor()
{
SortableItems = new List<SortableItem>();
SortableItems.Add(new SortableItem { PropA = "b" });
SortableItems.Add(new SortableItem { PropA = "a" });
SortableItems.Add(new SortableItem { PropA = "c" });
}
public void SortItems(Func<SortableItem, IComparable> keySelector, bool isAscending)
{
if(isAscending)
SortableItems = SortableItems.OrderBy(keySelector).ToList();
else
SortableItems = SortableItems.OrderByDescending(keySelector).ToList();
}
}