我使用反射来检索可用类的列表,并且我希望将输入对象调整为最匹配的 特定的 类;即我想做的事情:
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 :
Adapter
N
F
H
I
K
E
G
L
M
Q
R
O
B
C
J
P
AA
AB
D
S
T
然而,结果:
AdapterSelector.GetApplicableAdapterTypes()。OrderBy((x)=&gt; x,new TypeHierarchyComparer());
是:
Adapter
,AA
,AB
,B
,C
,D
,N
,F
,H
,I
,J
,K
,P
,E
,G
,L
,{ {1}},M
,O
,P
,Q
,R
,S
是否应在T
,O
,P
,AA
和{{1}之前订购正确的订单AB
和D
{}}和S
应该在T
,O
和B
之前。似乎子类是在他们的父母之后订购的,但不一定是在他们的父母之后。
如何按特异性对类进行排序? (即,在所有父母之前/之后对子类进行排序)
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的解决方案资源管理器所示的类层次结构。我无法透露适配器的名称,因此我使用了C
到J
,我保留了名称的字母顺序。
答案 0 :(得分:1)
问题在于你的任意排序规则过于随意。
而不是对类型名称进行排序(因此,比较G
和B
,会说G
应该在B
之后),您应该找到最接近的公共名称两个不相关类型的祖先。然后,你应该根据两种类型下降的共同祖先的直接后代进行排序。
因此,为了比较G
和B
,您会找到最近的共同祖先(Adapter
),然后找到这两种类型的下降类型({{1}然后在那些类型上执行任意排序规则。
像这样(没有关于代码质量的承诺: - )):
K
我认为这段代码不会在列表末尾徘徊,因为如果其中一个列表用尽,则意味着其中一个类型 实际上是另一个的祖先以前的子类化规则应该已经解决了比较。
另外,您可能希望考虑&#34; memoizing&#34;祖先使用B
进行查找。
答案 1 :(得分:1)
Linq OrderBy
使用的排序算法(快速排序)需要完整的订单关系。您已使用未按部分顺序关系排序的对的任意赋值扩展了部分顺序,但结果不会满足完整排序的公理,特别是它不会是可传递的(例如在您的玩具示例中{ {1}}和P<AA
,但AA<E
当传递性需要E<P
时),依赖于传递性的排序算法将会出现任意严重的行为。这就是你观察到的。不需要在内部使用快速排序的P<E
,而是需要拓扑排序算法。