Reputation: 41
Does anybody know if this is possible? I have a custom Attribute class that defines a Type that implements IComparer for a property. I would like to access that Type via reflection and instantiate it for use in IEnumerable.OrderBy():
[System.AttributeUsage(System.AttributeTargets.Property)]
public class SortComparer : System.Attribute
{
public Type ComparerType;
public SortComparer(Type ComparerType)
{
this.ComparerType = ComparerType;
}
}
var property = typeof(PerTurbineResultViewModel).GetProperty(SortColumn);
var sortComparer = property.GetCustomAttributes(typeof(SortComparer), true).FirstOrDefault() as SortComparer;
if (sortComparer != null)
{
var insta = Activator.CreateInstance(sortComparer.ComparerType);
this.Results = lstResults.Select(r => new ResultViewModel(r)).
OrderBy(p => property.GetValue(p, null), insta));
}
The above does not compile since OrderBy<TSource, TResult>
requires the second argument to be of type IComparer<TResult>
(which is unknown at compile time).
Is there a way to instantiate the 'insta' variable and cast it as IComparer<TResult>
using the type information found in 'property'?
EDIT: The first option got me very close:
Func<ResultViewModel, PropertyInfo> sel = t => property;
this.Results = infoGeneric.Invoke(Results, new object[] { vals, sel, insta }) as IEnumerable<ResultViewModel>;
Except I get a runtime exception for the property selector:
// Object of type 'System.Func`2[ResultViewModel,System.Reflection.PropertyInfo]' cannot be converted to type 'System.Func`2[ResultViewModel,System.Reflection.RuntimePropertyInfo]'.
RuntimePropertyInfo seems to be internal... is there another way to pass in the property selector?
Upvotes: 4
Views: 812
Reputation: 6502
Sometimes it is better to use functional approach by instantiating helper classes by factory functions.
A generic class that implements IComparer
interface [1]
internal class GenericEqualityComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _comparer;
public GenericEqualityComparer(Func<T, T, bool> comparer) {
_comparer = comparer;
}
public bool Equals(T x, T y)
{
return _comparer(x, y);
}
public int GetHashCode(T obj)
{
return base.GetHashCode();
}
}
Use a factory method to instantiate generic comparer class [2] and pass Func<T, T, int> compare
delegate as parameter to this class
IComparer<Point> comparer = comparator<Point>((a, b) =>
{
return (a.X + a.Y) - (b.X + b.Y);
});
comparer.Compare(new Point(0, 0), new Point(0, 0)); // should return zero
comparer.Compare(new Point(100, 0), new Point(0, 0)); // should return a value greater than zero
comparer.Compare(new Point(0, 0), new Point(100, 0)); // should return a value less than zero
Upvotes: 0
Reputation: 1502286
Basically you've got two options:
OrderBy
using reflection as well: get the generic method definition, then call MethodInfo.MakeGenericMethod
to get the constructed version, then invoke it.dynamic
in C# 4 to get the built-in mini-compiler to do the heavy lifting for youEDIT: As property.GetValue()
only returns object
, you'll almost certainly have to go via the reflection route. Either that, or you could use a third, somewhere horrible but really easy option...
... make all your comparers implement IComparer<object>
, and cast within them. Then your TResult
would be object
, and you can just cast:
object tmp = Activator.CreateInstance(sortComparer.ComparerType);
IComparer<object> comparer = (IComparer<object>) tmp;
this.Results = lstResults.Select(r => new ResultViewModel(r))
.OrderBy(p => property.GetValue(p, null), comparer);
Upvotes: 3