【发布时间】:2017-05-18 10:05:00
【问题描述】:
我正在使用反射来检索可用类的列表,并且我想将输入对象调整为匹配的最特定类;即我想做类似的事情:
RawData input = new RawData() { ... };
Type targetAdapter = AdapterSelector.SelectAdapterFor(input);
AdapterSelector.TryGetAdaptMethod(targetAdapter, out Func<RawData, Adapter> fAdapt);
Adapter adapted = fAdapt(input);
每个适用的类都有以下签名:
public class Adapter
{
public static Adapter Adapt(RawData input) { ... }
public static bool Matches(RawData input) { ... }
}
可以使用反射来检索具有所需方法的类列表,如下所示1:
public partial class AdapterSelector
{
public static IEnumerable<Type> GetApplicableAdapterTypes()
{
IEnumerable<Type> allTypes = typeof(AdapterSelector).Assembly.GetTypes();
IEnumerable<Type> applicableTypes = from t in allTypes
where TryGetAdaptMethod(t, _) &&
TryGetMatchMethod(t, _)
select t;
return applicableTypes;
}
private static bool TryGetAdaptMethod(Type type, out Func<RawData, Adapter> adapt)
{
MethodInfo adaptMethod = type.GetMethod("Adapt", BindingFlags.Static|BindingFlags.Public, null, new Type[] { typeof(RawData) }, null);
if (adaptMethod != null)
{
adapt = (Func<RawData, Adapter>)Delegate.CreateDelegate(typeof(Func<RawData, Adapter>), null, adapterMethod);
return true;
}
else
{
adapt = null;
return false;
}
}
private static bool TryGetMatchMethod(Type type, out Func<RawData, bool> match)
{
MethodInfo matchMethod = type.GetMethod("Match", BindingFlags.Static|BindingFlags.Public, null, new Type[] { typeof(RawData) }, null);
if (matchMethod != null)
{
match = (Func<RawData, bool>)Delegate.CreateDelegate(typeof(Func<RawData, bool>), null, matchMethod);
return true;
}
else
{
match = null;
return false;
}
}
}
我假设我可以使用以下IComparer<Type> 来订购此适配器列表:
public class TypeHierarchyComparer: IComparer<Type>
{
public int Compare(Type lhs, Type rhs)
{
if (rhs.IsSubclassOf(lhs))
{
return -1;
}
else if (lhs.IsSubclassOf(rhs))
{
return +1;
}
else
{
// Return arbitrary but fixed value for siblings, cousins, etc.
return lhs.FullName.CompareTo(rhs.FullName);
}
}
}
请注意,这是一种部分排序:如果一种类型是另一种类型的子类,则该类型应在 2 后者之后排序。如果两个类型都不是另一个类型的子类,则顺序未定义。
通过这种排序,我希望以下适配器层次结构能够正确排序3:
-
AdapterNFHI-
KEGLMQR
OBCJ-
PAAABDST
但是,结果:
AdapterSelector.GetApplicableAdapterTypes().OrderBy((x) => x, new TypeHierarchyComparer());
是:
Adapter, AA, AB, B, C, D, N, F, H, I, @98654364@, @98654363@, @9865436@ , P, E, G, L, M, O, P, Q, R, S, @9876
正确的顺序是O和P应该在AA、AB、D、S和T之前排序,O应该在B之前987654386@ 和 J。似乎子类是在其父母之一之后排序的,但不一定在其所有父母之后。
如何按特异性对类进行排序? (即在所有父类之前/之后对子类进行排序)
1)见Assembly.GetTypes()、Type.GetMethod(string, BindingFlags, Binder, Type[], ParameterModifier[])、Can you get a Func<T> (or similar) from a MethodInfo object?和Delegate.CreateDelegate(Type, object, methodInfo)
2)我正在使用Enumerable.OrderByDescending<TSource, TKey>(this IEnumerable<TSource>, Func<TSource, TKey>, IComparer<TKey>) 让匹配器首先应用在列表的前面。
3)Visual Studio 的解决方案资源管理器显示的类层次结构。我不能透露适配器的名称,因此我使用AA 到T,我保留了名称的字母顺序。
【问题讨论】:
-
你得到的错误是当你比较 E 和 F 时。E 是 1 级,而 F 是 2 级。我建议在类中添加一个级别编号,这样你就只比较名称同一级别。
标签: c# linq sorting reflection