C# 在动态层次结构中按类型查找项目
我的一位同事问了一个有趣的问题。想象一下下面的对象结构 项目-->短跑-->故事-->任务 我的同事只是对这些任务感兴趣。没有别的了。每个任务都有自己的id,他希望选择项目的所有任务,而不知道类型项目的确切层次结构。当某人更改层次结构中的某些内容(添加或删除一个级别)时,他现在还必须修复查询以获取所有任务 有没有一个好的方法来实现这一点?也许是这样的:C# 在动态层次结构中按类型查找项目,c#,.net,linq,C#,.net,Linq,我的一位同事问了一个有趣的问题。想象一下下面的对象结构 项目-->短跑-->故事-->任务 我的同事只是对这些任务感兴趣。没有别的了。每个任务都有自己的id,他希望选择项目的所有任务,而不知道类型项目的确切层次结构。当某人更改层次结构中的某些内容(添加或删除一个级别)时,他现在还必须修复查询以获取所有任务 有没有一个好的方法来实现这一点?也许是这样的: IEnumerable<Project> projects = GetProjectBy(...); IEnumerable<
IEnumerable<Project> projects = GetProjectBy(...);
IEnumerable<Task> = projects.ItemsOfTypeInHierarchy<Task>();
IEnumerable projects=GetProjectBy(…);
IEnumerable=projects.ItemsOfTypeInHierarchy();
不经思考就可以吗
(我知道它是如何与
一起工作的。选择many
并知道确切的结构)模型的每个部分是否通过一些公共接口或基类公开其子级?如果他们实现了像
public interface IHierarchicalParent
{
IEnumerable<IHierarchicalParent> Children { get; }
}
公共接口IHierarchicalParent
{
IEnumerable子项{get;}
}
可以递归地搜索树而不进行反射
如果不能修改每个类,那么可以为每个类型编写一个适配器——尽管之后必须编写一系列包装器。根据您的用例灵活地选择要制作的包装涉及一些类型检查(是
,作为
,巧妙地使用动态
,等等),但不会反映到成员中
您还可以将适配器“展平”为一个方法,该方法接受基本实体对象并返回相同类型的可枚举项
但是,如果可以在编译时确定特定的层次结构,则可以完全避免对类型进行反射。您可以通过使ProjectAdapter
为其项目中的每个Sprint
返回一个可枚举的SprintAdapter
作为其子项
。这当然会使它不灵活,但根本没有类型检查!之后,只需将所有对象视为IHierarchicalParent
,并以相同的方式执行递归检查
(从我的评论中删除了这个答案,并对其进行了扩展。)模型的每个部分是否通过一些公共接口或基类公开其子级?如果他们实现了像
public interface IHierarchicalParent
{
IEnumerable<IHierarchicalParent> Children { get; }
}
公共接口IHierarchicalParent
{
IEnumerable子项{get;}
}
可以递归地搜索树而不进行反射
如果不能修改每个类,那么可以为每个类型编写一个适配器——尽管之后必须编写一系列包装器。根据您的用例灵活地选择要制作的包装涉及一些类型检查(是
,作为
,巧妙地使用动态
,等等),但不会反映到成员中
您还可以将适配器“展平”为一个方法,该方法接受基本实体对象并返回相同类型的可枚举项
但是,如果可以在编译时确定特定的层次结构,则可以完全避免对类型进行反射。您可以通过使ProjectAdapter
为其项目中的每个Sprint
返回一个可枚举的SprintAdapter
作为其子项
。这当然会使它不灵活,但根本没有类型检查!之后,只需将所有对象视为IHierarchicalParent
,并以相同的方式执行递归检查
(从我的评论中删除了这个答案,并对其进行了扩展。)这个答案的灵感来源于米格尔·卡斯特罗(pluralsight的作者),它确实使用了反射,但我使用了它,它表现得很好。我想,我会分享它的多功能性。你可以做的不仅仅是获取对象,你可以用它们做任何你想做的事情
创建类似于ObjectBase的东西,需要在层次结构中找到的任何类都继承自该基类。我从我的源代码管理中复制了这一点,并在这篇文章中做了一些调整,但是这个ObjectBase的目标是通过“遍历”树来获取树中的所有“脏”对象。您可以传递任何您想要的匿名方法,该匿名方法就是您实际想要传递给该对象的方法(将其添加到集合、执行方法、更改属性等)
公共抽象类ObjectBase
{
//IsDirty属性
受保护的财产;
公共图书馆
{
获取{return\u isDirty;}
设置{u isDirty=value;}
}
///
///使用此项作为根,遍历树以获取所有脏对象
///
///数不清
公共IEnumerable GetDirtyObjects()
{
var dirtyObjects=新列表();
步行树(
o=>
{
如果(o.IsDirty)
dirtyObjects.Add(o);
return false;//可以返回true以停止在第一个对象上行走
});
返回脏物体;
}
///将树中的所有DirtyObjects设置为“清理”
公共树()
{
步行树(
o=>
{
o、 IsDirty=false;
return false;//可以返回true以停止在第一个对象上行走
});
}
//接受要执行的函数以及可能要排除的任何属性
//在本例中,我们将使用的函数是获取脏对象(上图)
public void WalkTree(Func walkSnippet,IEnumerable exemptProperties=null)
{
//创建已访问对象的列表,以防止查看同一对象两次
访问列表=新列表();
//通过财产名称考虑任何豁免
列表豁免=新列表();
if(exemptProperties!=null)
豁免=适当的豁免
public static class ReflectionHelper
{
public static IEnumerable<PropertyInfo> GetTreeProperties<T>(T obj)
{
var type = obj.GetType();
var properties = type.GetProperties()
.Where(x => x.PropertyType.IsSubclassOf(typeof(T)) || x.GetValue(obj) as IEnumerable<T> != null);
return properties;
}
}