我有一些实体集合,我需要根据它们之间的依赖关系进行排序。这是一个例子,因为它很难解释:
public class A : I {
private B objB;
public B propB { get{ return objB; } }
// Some other fields and properties.
}
public class B : I { /* Some fields and properties. */ }
public class C : I {
private A objA;
public A propA { get{ return objA; } }
// Some other fields and properties.
}
public interface I {}
问题是我需要将数据导入到这些类型的集合中,但我需要按特定顺序导入,因为如果我先导入A
个对象,我将无法链接相应的{{1因为它还不存在。
所以我想要的是以一种方式对我的集合进行排序,以正确的顺序导入所有依赖项(没有循环依赖关系)。我无法想出一个能做到这一点的linq声明。
B
我在考虑可能会在lists.OrderBy(l => l. ??? );
中获取typesList
的所有类型T
的列表List<T>
,并以某种方式使用反射计算{{1}中的字段数在} lists
中,但似乎......效率不高?
编辑:意识到我的结构的措辞有点模糊
基本上T
是typesList
。这是一个结果示例:
lists
答案 0 :(得分:1)
我知道这样做的最简单方法是构建一个新的集合,当你构建它时,看看你是否在类中看到任何已知的类型。如果你确实看到一个已知的类型,就在第一次发现之前将其置于最后,或者如果没有找到已知的类型则将它放在最后。一旦你有了这个集合,就会以相反的顺序枚举集合,并且它将以反向依赖顺序排列。
以下示例与
类似var sortedLists = lists.OrderByTypeDependency();
如何实现LINQ样式扩展方法:
static class ExtensionMethods
{
public static IEnumerable<T> OrderByTypeDependency<T>(this IEnumerable<T> items)
{
LinkedList<T> knownItems = new LinkedList<T>();
foreach (var item in items)
{
var itemType = item.GetType();
var itemPropertyTypes =
itemType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.PropertyType);
var itemFieldTypes =
itemType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.FieldType);
//Create a set of all types internal to type we are checking on.
HashSet<Type> itemChildTypes = new HashSet<Type>(itemPropertyTypes.Concat(itemFieldTypes));
bool found = false;
for (var knownItemNode = knownItems.First; knownItemNode != null; knownItemNode = knownItemNode.Next)
{
var knownItemType = knownItemNode.Value.GetType();
if (itemType == knownItemType || itemChildTypes.Contains(knownItemType))
{
knownItems.AddBefore(knownItemNode, item);
found = true;
break;
}
}
if (!found)
{
knownItems.AddLast(item);
}
}
//output the result in reverse order.
for (var knownItemNode = knownItems.Last; knownItemNode != null; knownItemNode = knownItemNode.Previous)
{
yield return knownItemNode.Value;
}
}
}
编辑:目前尚不清楚你是否传递了类型列表或对象列表。如果您传递的是类型列表,则只需对代码进行一些小调整,只需删除两个.GetType()
来电,然后从泛型转为仅接受IEnumerable<Type>
static class ExtensionMethods
{
public static IEnumerable<Type> OrderByTypeDependency(this IEnumerable<Type> items)
{
LinkedList<Type> knownItems = new LinkedList<Type>();
foreach (var item in items)
{
var itemType = item;
var itemPropertyTypes =
itemType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.PropertyType);
var itemFieldTypes =
itemType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.FieldType);
//Create a set of all types internal to type we are checking on.
HashSet<Type> itemChildTypes = new HashSet<Type>(itemPropertyTypes.Concat(itemFieldTypes));
bool found = false;
for (var knownItemNode = knownItems.First; knownItemNode != null; knownItemNode = knownItemNode.Next)
{
var knownItemType = knownItemNode.Value;
if (itemType == knownItemType || itemChildTypes.Contains(knownItemType))
{
knownItems.AddBefore(knownItemNode, item);
found = true;
break;
}
}
if (!found)
{
knownItems.AddLast(item);
}
}
for (var knownItemNode = knownItems.Last; knownItemNode != null; knownItemNode = knownItemNode.Previous)
{
yield return knownItemNode.Value;
}
}
}
<强>更新强>
根据你对问题的更新,只要内部列表在列表中保存相同类型的对象,这样我们就可以检查列表中的第一项以找到它的类型,原始代码的这个修改将进行排序你需要。
static class ExtensionMethods
{
public static IEnumerable<T> OrderByTypeDependency<T>(this IEnumerable<T> outerList)
where T: IList
{
LinkedList<T> knownItems = new LinkedList<T>();
foreach (var innerList in outerList)
{
if(innerList.Count == 0)
continue;
var itemType = innerList[0].GetType();
var itemPropertyTypes = itemType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.PropertyType);
var itemFieldTypes = itemType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Select(x => x.FieldType);
//Create a set of all types internal to type we are checking on.
HashSet<Type> itemChildTypes = new HashSet<Type>(itemPropertyTypes.Concat(itemFieldTypes));
bool found = false;
for (var knownItemNode = knownItems.First; knownItemNode != null; knownItemNode = knownItemNode.Next)
{
var knownItemType = knownItemNode.Value[0].GetType();
if (itemType == knownItemType || itemChildTypes.Contains(knownItemType))
{
knownItems.AddBefore(knownItemNode, innerList);
found = true;
break;
}
}
if (!found)
{
knownItems.AddLast(innerList);
}
}
for (var knownItemNode = knownItems.Last; knownItemNode != null; knownItemNode = knownItemNode.Previous)
{
yield return knownItemNode.Value;
}
}
}