C# 对象A引用了对象B,而对象B将对象A作为公共财产-糟糕的设计?
我有一个上下文对象,它有几个有用的属性和方法。其他对象在其构造函数中接受此上下文对象。但是,上下文对象可以将这些其他对象作为公共属性。见下文:C# 对象A引用了对象B,而对象B将对象A作为公共财产-糟糕的设计?,c#,oop,C#,Oop,我有一个上下文对象,它有几个有用的属性和方法。其他对象在其构造函数中接受此上下文对象。但是,上下文对象可以将这些其他对象作为公共属性。见下文: public class Foo { private IContext Context { get; private set; } public Foo( IContext context ) { Context = context; } } public class SomeContext : IContex
public class Foo {
private IContext Context { get; private set; }
public Foo( IContext context ) {
Context = context;
}
}
public class SomeContext : IContext {
public Foo SomeProperty { get; set; }
/*
Other useful properties and methods as defined in IContext
*/
public SomeContext () {
SomeProperty = new Foo( this );
}
}
现在我可以在Foo上的方法中做一些有用的事情:
public void FooIt() {
IUseful bar = this.Context.GetUsefulInterface();
bar.DoUsefulThing();
}
然而,这可能会导致一些非常奇怪的事情。在Fo: 中考虑这些方法
public void DoSomething() {
/* useful stuff */
}
public void DoSomethingElse() {
this.Context.SomeProperty.DoSomething(); // could just call this.DoSomething();
this.Context.SomeProperty.DoSomethingElse(); // stack overflow!
}
这被认为是一种糟糕的设计/代码味道吗?上下文对象的原因在某种程度上涉及其中,我想将问题更多地放在循环引用的框架中。如果认为不好,有哪些方法可以打破这种循环关系?不错,但绝对不理想。我的看法是,您不应该在构造函数中为对象创建上下文,而上下文对象将接受正在创建的对象。上下文应该是您正在创建的对象的外部内容 但是,使用上下文对象创建一个“无限”循环排序是没有问题的。ASP.NET MVC ControllerContext在某些方面做得非常相似;你通常不会这样称呼它:
ControllerContext.Controller.ControllerContext.Controller // etc
循环关系在非托管环境中是不好的,因为您需要弱引用来打破循环关系(否则您将不可避免地泄漏循环中涉及的对象)。不过,我想不出一个具体的理由在管理环境中给它们贴上“坏”的标签
事实上,有很多很好的理由可以使用循环引用。例如,在UI树中,父UI元素知道它们的子元素,子元素也知道它们的父元素。通常,循环引用是糟糕设计的味道。然而,在一些情况下,循环引用是一种很好的方法(在Winforms中:Control.Controls vs.Control.Parent)
如果有循环引用,则应检查正确的值。如果您有父子关系,并且childr已从父项中删除,则子项不应引用父项,父项不应引用子项。如果没有领域知识,在尝试设计循环依赖项时很难知道从何处开始,因此我将进行概括 当您不表达层次关系时,循环引用是不好的,在这种关系中,返回父级是必需的或有用的。这是因为它促进了循环中类型之间的强耦合,很难正确构造/删除,并且在遍历时容易出现错误 但是,当您有一个层次关系,并且需要/有用地遍历备份层次结构时,使用循环引用是非常有意义的 您可能希望避免的一件事是试图一次调用过深的属性。这会增加耦合,并很容易导致出现诸如堆栈溢出异常之类的错误
// Reaches too far. Makes this depend on the interface of SomeProperty
this.Context.SomeProperty.DoSomething();
// ...
// Not reaching too far. Only depends on Context.
// This might forward to SomeProperty.DoSomething()
this.Context.DoSomething();
根据修复方式的不同,这也可能有助于打破堆栈溢出
请参阅:,因为循环引用比“单向”引用更难维护。我经常看到在另一种模式以同样的效率解决问题的场景中使用循环引用buggy实现和使用。