You can pass a property accessor to the method.
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;
}
You would call it like this:
var result = SortBy(toSort, x => x.maxSpeed);
But you could go one step further and write your own extension method.
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;
}
}
Now you can sort like this
List<Class1> sorted = toSort.OrderByAsListOrNull(x => x.maxSpeed);
but also
Person[] people = ...;
List<Person> sortedPeople = people.OrderByAsListOrNull(p => p.LastName);
Note that I declared the first parameter as because it must fulfill two conditions:ICollection<T>
Count propertyIEnumerable<T> in order to be able to apply the LINQ method OrderBy. is an List<Class1> but also an array ICollection<T> as many other collections.Person[]
So far, I have shown how you can read a property. If the method needs to set a property, you need to pass it a setter delegate as well
void ReadAndWriteProperty(Func<Class1, T> getProp, Action<Class1, T> setProp)
Where is the type of the property.T
To get a property, you could probably use a :Func<Item, T>
// I don't know what this method returns so I used "void".
public void ParseData<T>(List<Item> items, Func<Item, T> propertySelector) {
// as an example, here's how to get the property of the first item in the list
var firstItemsProperty = propertySelector(items.First());
...
}
You can call this method by passing a lambda expression:
ParseData(itemList, x => x.Property1) // "Property1" is a property declared in "Item"
You almost have the right solution already - the only missing piece is how you use the property to get the desired value from the MeritFunctionLine.property.CalculationOutput
In your loop, simply replace the calculation line withforeach
m += Math.Abs(item.property(values) - item.value);
Edit:
Adding Genericity
To address Obsidian Phoenix's comment, you can use this with different classes by making both and MeritFunction generic, so:MeritFunctionLine
public class MeritFunctionLine<TCalcOutput>
{
public Func<TCalcOutput, double> property { get; set; }
public double value { get; set; }
public ComparisonTypes ComparisonType { get; set; }
}
public class MeritFunction<TCalcOutput>
{
public List<MeritFunctionLine<TCalcOutput>> Lines { get; set; }
public double Calculate(TCalcOutput values)
{
double m = 0;
foreach (var item in Lines)
{
m += Math.Abs(item.property(values) - item.value);
}
return m;
}
}
The rewritten usage example would be
MeritFunction<CalculationOutput> mf = new MeritFunction<CalculationOutput>();
mf.Lines.Add(new MeritFunctionLine<CalculationOutput>() { property = x => x.Property1, value = 90, comparisonType = ComparisonTypes.GreaterThan });
mf.Lines.Add(new MeritFunctionLine<CalculationOutput>() { property = x => x.Property3, value = 50, comparisonType = ComparisonTypes.Equals });
CalculationOutput c1 = new CalculationOutput() { property1 = 1, property2 = 20, property3 = 150, property4 = 500 };
CalculationOutput c2 = new CalculationOutput() { property1 = 15, property2 = 32, property3 = 15, property4 = 45 };
double value1 = mf.Calculate(c1);
double value2 = mf.Calculate(c2);
Some extra convenience
If you have many s to add, the syntax above can be a bit tedious. So as a bonus, let's change MeritFunctionLine so that it can be initialized with the list initialization syntax. To do that, we need to make it MeritFunction and give it an IEnumerable function:Add
public class MeritFunction<TCalcOutput> : IEnumerable<MeritFunctionLine<TCalcOutput>>
{
public List<MeritFunctionLine<TCalcOutput>> Lines { get; set; }
public MeritFunction()
{
Lines = new List<MeritFunctionLine<TCalcOutput>>();
}
public void Add(Func<TCalcOutput, double> property, ComparisonTypes ComparisonType, double value)
{
Lines.Add(new MeritFunctionLine<CalculationOutput>
{
property = property,
value = value,
comparisonType = ComparisonType
});
}
public double Calculate(TCalcOutput values)
{
double m = 0;
foreach (var item in Lines)
{
m += Math.Abs(item.property(values) - item.value);
}
return m;
}
public IEnumerator<MeritFunctionLine<TCalcOutput>> GetEnumerator()
{
return List.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Note that the method receives the parameters in a different order - you'll understand why when you look at the usage. Quite a bit of extra code, but now creating our Add is a bit nicer:MeritFunction
MeritFunction<CalculationOutput> mf = new MeritFunction<CalculationOutput>
{
{ x => x.Property1, ComparisonTypes.GreaterThan, 90 },
{ x => x.Property3, ComparisonTypes.Equals, 50 }
};
Note, all code untested. Use at own risk :)
I'd suggest:
public static class AttributeExtensions
{
public static string GetMyAttributeNameOfProperty<TClass>(string propertyName)
{
var propertyInfo = typeof(TClass).GetProperty(propertyName);
return propertyInfo?.GetCustomAttribute<MyAttribute>()?.Name ?? default;
}
}
Usage:
var awesome = AttributeExtensions.GetMyAttributeNameOfProperty<Matrix>(nameof(Matrix.AwesomeName));
Console.WriteLine(awesome);
Note: The above isn't an extension method. The below code is.
public static class AttributeExtensions
{
public static string GetMyAttributeNameOfProperty<TType>(this TType me, string propertyName) where TType : Type
{
var propertyInfo = me.GetProperty(propertyName);
return propertyInfo?.GetCustomAttribute<MyAttribute>()?.Name ?? default;
}
}
Usage:
var awesome = typeof(Matrix).GetMyAttributeNameOfProperty(nameof(Matrix.AwesomeName));
Console.WriteLine(awesome);
Both output:
awesome_name
Benefits:
Expression or even the namespace
System.Linq.Expressions null if property or attribute doesn't exist)nameof makes refactoring safe in the event you rename class propertiesFunc<TClass, object> or similarMatrix (or any other type since it's generic) Either way works and is really a matter of preference. There are pros and cons to using extension methods.