3

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

我有另一个类为长时间运行的方法提供异步包装器(可能是 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尝试访问该MetaData.Data2属性,他们将得到响应。但是如果他们在返回后调用 if ,它将包含正确的信息。由于确实没有办法通知用户此方法已完成,我担心这会变成糟糕的用户体验,因为他们不必等待构造函数,而是必须等待所有属性被设置。GetData2Async()Data2Receivednull

4

3 回答 3

2

首先,您说没有办法通知用户Data2完成。这不是真的,您可以使用多种方式通知用户,例如事件或Task.

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

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

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

于 2013-01-26T03:48:56.217 回答
1

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

于 2013-01-26T02:08:07.560 回答
0

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

IMO,您不应该在构造函数中调用任何操作,例如您现在正在做的事情。你的构造函数中的所有东西MetaData都不应该从那里开始。

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

再看看这个问题: 一个构造函数应该放多少代码?

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

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

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

于 2013-01-26T03:49:17.217 回答