设计具有长时间运行的构造函数的对象

本文关键字:构造函数 对象 运行 长时间 | 更新日期: 2023-09-27 17:56:10

>我有一个类,旨在为某些特定文件提供对某些元数据的快速访问,这些文件在我的应用程序中多次使用。 不幸的是,某些元数据只能通过长时间运行的方法调用来提取。

我还有另一个类为长时间运行的方法提供异步包装器(可能是 5 分钟或更长时间,具体取决于文件的大小),但我正在尝试弄清楚如何调用这个异步方法以及是否适合将其放入构造函数中,或者是否有更好的设计模式适用于这种情况。

这里有一些伪代码来尝试说明我的问题:

public class MetaData
{
    public string Data1 { get; private set; }
    public string Data2 { get; private set; }
    public MetaData(String filepath)
    {
        var extractor = new ExtractMetaData(filepath);  //create instance of class that fetches the metadata
        this.Data1 = extractor.GetData1(); // short running method
        extractor.Data2Received += Data2Received;  
        extractor.GetData2Async();  // long running method, called with via async method
    }        
    private void Data2Received(object sender, MetaDataEventArgs args)
    {
        this.Data2 = args.Data;  // finally set Data2 property
    }
}
class ExtractMetaData
{
    public event Data2ReceivedEventHandler Data2Received;
    public ExtractMetaData (string filePath) { }
    public String GetData1();  // very fast method to get Data1
    public void GetData2Async();  // very slow method to get Data2
}

我想弄清楚的是是否有更好的方法来实现这一目标?

现在使用我的代码,几乎无需等待构造MetaData,但如果有人尝试在 GetData2Async() 方法返回并触发 Data2Received 事件之前访问 MetaData.Data2 属性,他们将得到null响应。 但是,如果他们在返回后调用if,它将包含正确的信息。 由于确实无法通知用户此方法已完成,我担心这会变成糟糕的用户体验,因为他们不需要等待构造函数,而是必须等待所有属性设置。

设计具有长时间运行的构造函数的对象

首先,您说无法通知用户完成Data2。事实并非如此,您可以使用多种方式来通知用户,例如事件或Task

但我认为你实际上应该重组你的班级。你说获得Data2需要很长时间,这很可能意味着它使用了大量资源。正因为如此,我认为除非必须,否则您甚至不应该尝试初始化Data2。你怎么知道的?用户必须告诉您。理想情况下,如果用户不想要 Data2 ,他甚至不应该能够访问它,这意味着MetaData分为两类:类似于 BasicMetaDataExtendedMetaData ,它继承自BasicMetaData .

ExtendedMetaData 中,您可以有某种方式通知用户初始化已完成(很可能使用事件),或者您可以让构造函数等到初始化完成(您可以使用 Monitor.Wait()Monitor.Pulse() 来执行此操作)。

就个人而言,我认为最好的选择是如果你有一个可以返回Task<ExtendedMetaData>的静态工厂方法。这样,用户可以同步(使用 Result)或异步(在可用的情况下使用 ContinueWith()await)等待结果。这在 .Net 4.5 中特别有用(因为 await ),但在 .Net 4.0 上也很有用。不幸的是,您问题上的标签表明您使用的是 .Net 3.5,它没有 Task .如果可能的话,我建议您升级。

我认为您需要将注意力花在以下模式上:延迟加载(仅在您真正需要时才调用"long"方法)和代理(如果需要实现缓存层,隐藏内部实现,可能在对象底部有多种不同的模式)。如果您决定使用多个对象来确保整个功能 - 那么外观也可能是合理的选择。

对于这个问题,你会得到几个不同的答案。这是我的看法。

IMO,您不应该在构造函数中调用任何操作,例如您现在正在执行的操作。MetaData构造函数中的所有内容都不应该一开始就存在。

在实例化对象时,它可能会引发异常,这很好,但不会构造对象。一些最佳实践。构造函数应该是短期运行的,并且应该确保在构造函数之后创建对象图。

也看看这个问题:应该在构造函数中放入多少代码?

或者,应注入依赖项并创建方法来填充数据。

如果您能多描述一下您的问题,那将更有帮助。

您确实需要简化流程和设计。