Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 域模型中避免循环引用_C#_.net_Design Patterns_Domain Driven Design - Fatal编程技术网

C# 域模型中避免循环引用

C# 域模型中避免循环引用,c#,.net,design-patterns,domain-driven-design,C#,.net,Design Patterns,Domain Driven Design,这一定是一个非常常见的场景,已经有很多关于它的文章,希望甚至是一个非常好的模式。我有一个域模型,其中自定义容器包含实体。例如(为简洁起见,不包括属性和接口): 类实体 { 公共int Id; 公共实体容器ParentContainer; } 类实体容器 { 公共int Id; 公共IList实体=新列表(); 公共无效附加值(实体) { entity.ParentContainer=这个; 实体。添加(实体); } } 班长 { 公用干管() { 实体entity1=新实体(); 实体entit

这一定是一个非常常见的场景,已经有很多关于它的文章,希望甚至是一个非常好的模式。我有一个域模型,其中自定义容器包含实体。例如(为简洁起见,不包括属性和接口):

类实体
{
公共int Id;
公共实体容器ParentContainer;
}
类实体容器
{
公共int Id;
公共IList实体=新列表();
公共无效附加值(实体)
{
entity.ParentContainer=这个;
实体。添加(实体);
}
}
班长
{
公用干管()
{
实体entity1=新实体();
实体entity2=新实体();
EntityContainer EntityContainer=新的EntityContainer();
entityContainer.AddEntity(entity1);
entityContainer.AddEntity(entity2);
//现在可以轻松地遍历图形,例如。
Console.WriteLine(“entity1的父容器ID=“+entity1.ParentContainer.ID”);
Console.WriteLine(“容器至少包含此实体ID:+entityContainer.Entities[0].ID”);
}
}
我现在可以很容易地双向遍历对象图,但已经创建了一个循环引用。您会创建第三种类型来分离依赖项吗


提前感谢

容器需要知道内容物的类型吗?如果没有,泛型可以避免这种情况,即使用
容器
,而您恰好使用了
容器
。除此之外,;将必要的详细信息推送到程序集中两者都可以引用的接口(或基类)中是一种常见的方法

就我个人而言,我会尽量避免让孩子了解父母

还有,;请注意,如果您选择了抽象(接口等)路线;如果使用(例如)xml序列化,这可能会有很大的影响


(重新编辑评论)
好啊第一:循环引用(在程序集中)导致了什么问题;如果没有,就别管它。如果有问题,那么你需要一个额外的类型;可能有些接口代表具体类型,例如,
Entity:IEntity
EntityContainer
只知道
IEntity
(或者vv和
IEntityContainer
,或者两者都知道),

循环引用本身没有错,它们在.NET框架中被广泛使用,例如XmlNode.OwnerDocument、Control.Parent

如果需要向上遍历树,则可以使用反向引用


在COM中,循环引用是很棘手的,因为如果您将容器及其所有子对象设置为“无”,则对象将无法正确清理,因为子对象仍然保留对父对象的引用。但是.NET垃圾收集的实现方式没有问题。

因此,我认为您的类模型没有问题,但您可以轻松地进行清理。使Entity实现接口IEntity并使EntityContainer持有IList,除非您有使用IList的非常具体的原因,否则您应该考虑IEnumerable,这将使您使用的EntityClass的使用者更容易使用它。因为在我的书中,如果您在设计这些对象时使用某种延迟加载模式,那么使用循环引用对象来传递任何IEntity数组或linq表达式选择IEntity都是可能的

e、 g

您要访问:公司。员工 在另一个场景中:雇员。公司

这是一个循环引用,即公司、员工、公司、员工等


如果这些属性不是延迟加载的,例如,一个公司对象总是加载其员工属性,而员工对象总是加载其公司属性,那么当您引入数据源时,它不会工作得太好

您所在的模型不允许多个父容器具有反向关系,因此,如果您在多个容器中有一个实体,则很可能不会以相同的方式工作。您能否澄清这是如何循环的?据我所知,您正在创建一个树结构,所以我不知道循环性来自何处。Entity引用EntityContainer,EntityContainer引用Entity。这是一个很好的反向关系。我需要更新,但是这个例子对于这个问题来说可能还可以。关于相反的关系,如果我将它添加到上面,那么我可能会用一个“Entity container”列表替换“Entity”类中的父容器字段.由于性能原因,孩子需要了解其父母。Generica不适合我正在建模的实际问题域。这些是特定的类型和关系。感谢更新-我将添加接口。我认为循环引用实际上没有错这一普遍共识是这个问题的主要补充。在列表上调用Enumerable.ToList()绝对不是免费的,因为它总是生成一个新的列表,所以它必须复制一个,即O(N)@Pavel nope它经过优化,只要在列表中调用list对象,就可以返回该对象。你完全错了。用ILSpy打开System.Core.dll,并查看
Enumerable.ToList
的实现。它实际上就像
returnnewlist()
加上一个参数null检查一样简单。这是有意为之的——LINQ的设计者不希望库客户端能够将对象向下转换为库作者不希望的对象,并可能改变内部数据。因此,所有
To…()
方法都将创建一个副本,即使原始副本已经是该类型的副本。相反,
As…()
返回原始对象。@Pavel你是对的,我记错了优化,它确实总是返回一个新列表。循环引用遇到的一个问题是序列化。一般来说,如果对象A有B,对象B有A,
class Entity
{
    public int Id;
    public EntityContainer ParentContainer;
}


class EntityContainer
{
    public int Id;
    public IList<Entity> Entities = new List<Entity>();

    public void AddEntity(Entity entity)
    {
        entity.ParentContainer = this;
        Entities.Add(entity);
    }
}


class Main
{
    public Main()
    {
        Entity entity1 = new Entity();
        Entity entity2 = new Entity();
        EntityContainer entityContainer = new EntityContainer();
        entityContainer.AddEntity(entity1);
        entityContainer.AddEntity(entity2);

        // Can now traverse graph easily, e.g.
        Console.WriteLine("entity1's parent container ID = " + entity1.ParentContainer.Id);
        Console.WriteLine("Container contains at least this entity ID: " + entityContainer.Entities[0].Id);

    }
}