c# – 如何设计一个类来防止循环依赖在构造之前调用派生成员?

前端之家收集整理的这篇文章主要介绍了c# – 如何设计一个类来防止循环依赖在构造之前调用派生成员?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
(我将它标记为C#和 Java,因为它在两种语言中都是相同的问题.)

说我有这些课程

interface IKernel
{
    // Useful members,e.g. AvailableMemory,TotalMemory,etc.
}

class Kernel : IKernel
{
    private /*readonly*/ FileManager fileManager;  // Every kernel has 1 file manager
    public Kernel() { this.fileManager = new FileManager(this); /* etc. */ }

    // implements the interface; members are overridable
}

class FileManager
{
    private /*readonly*/ IKernel kernel;  // Every file manager belongs to 1 kernel
    public FileManager(IKernel kernel) { this.kernel = kernel; /* etc. */ }
}

这种设计的问题是,只要FileManager尝试在其构造函数内部执行任何内容(它可能合理地需要),它就会在潜在的子类实例上调用一个虚方法,该实例的构造函数尚未被调用.

在你可以定义真正的构造函数(而不是初始化程序,如C#/ Java)的语言中不会出现这个问题,因为在它们的构造函数调用之前,子类甚至都不存在……但是在这里,会出现这个问题.

那么什么是最佳/适当的设计/实践,以确保不会发生这种情况?

编辑:

我不一定说我需要循环引用,但事实是内核和FileManager都相互依赖.如果您有关于如何在不使用循环引用的情况下缓解此问题的建议,那么这也很棒!

解决方法

就个人而言,我不喜欢循环引用.但是如果你决定离开它们,你可能会增加一些懒惰:
interface IKernel
{
    // Useful members,etc.
}

class Kernel : IKernel
{
    private readonly Lazy<FileManager> fileManager;  // Every kernel has 1 file manager
    public Kernel() { this.fileManager = new Lazy<FileManager>(() => new FileManager(this)); /* etc. */ }

    // implements the interface; members are overridable
}

class FileManager
{
    private /*readonly*/ IKernel kernel;  // Every file manager belongs to 1 kernel
    public FileManager(IKernel kernel) { this.kernel = kernel; /* etc. */ }
}

这里的懒惰让我们确保在查询FileManager实例时将完全初始化IKernel实现.

猜你在找的C#相关文章