在属性(Property)内执行初始化是一种好的实践吗?

3

我有一个名为PluginProvider的类,它使用PluginLoader组件从文件系统中加载插件(管理/本地)。在PluginProvider类中,当前定义了一个名为“PluginTypes”的属性,该属性在get()时调用“InitializePlugins”实例方法。

class PluginProvider
 {
   IEnumerable<IPluginType> PluginTypes
   {
     get
     {
        //isInitialized is set inside InitializePlugins method
        if(!isInitialized)
        {
           InitializePlugins(); //contains thread safe code
        }
        //_pluginTypes is set within InitializePlugins method
        return _pluginTypes;
     }
   }
 }

我正准备重构这段代码。我想知道在属性中进行这种初始化是否可行。我知道不应在属性中执行繁重的操作。但是,当我查看此链接:http://msdn.microsoft.com/en-us/library/vstudio/ms229054.aspx,发现“特别是访问网络或文件系统的操作(除了初始化外)应该是方法,而不是属性。” 现在我有点困惑,请帮忙。

4个回答

2
  • 如果您想尽可能地延迟初始化,并且不知道何时调用您的属性(或属性),那么您所做的是可以的。
  • 如果您想延迟初始化并且可以控制何时第一次调用属性,则可以将方法 InitializePlugins() 设为公共,并在访问属性之前显式调用它。这个选项还可以打开异步初始化的可能性。例如,您可以有一个返回 TaskInitializePluginsAsync()
  • 如果延迟初始化不是很重要,那么就在构造函数中执行初始化即可。

感谢您的回复。对我来说,延迟初始化并不是一个问题。同时,我也不想在构造函数中添加昂贵的操作。这是在应用程序启动之前发生的事情,在属性被访问之前。因此,我认为我可以选择第二个选项。 - Nacharya
@Naveen 我也会选择第二个选项,并实现异步模式(因为你提到操作非常昂贵)。否则,我不会太担心使用构造函数或在构造函数中抛出异常。这些都很好,在 .Net Framework 中有例子(例如,请参见 FileStream 类)。 - Eren Ersönmez

1
你正在进行的操作被称为惰性初始化。你推迟了一个可能代价高昂的操作,直到它的输出被需要的那一刻。
现在,这不是绝对的规则。如果你的 InitializePlugins 方法需要很长时间才能完成,并且可能会影响用户体验,那么你可以考虑将其移入公共方法,甚至将其异步化并在属性外部调用:在应用程序启动时或在找到一个好的时机调用长时间运行的操作。
否则,如果它是一个短暂的一次性事情,它可以留在那里。正如我所说,这不是绝对的规则。通常这些只是适用于特定情况的一些指导方针。

1

任何初始化代码都应该在构造函数中完成,这样你就知道它只会被调用一次。

public class PluginProvider
{
   IEnumerable<IPluginType> PluginTypes
   {
      get
      {
        return _pluginTypes;
      }
   }

   public PluginProvider()
   {
      InitializePlugins();
   }
}

感谢您的回复。这里有两个问题:1. 加载插件是一个耗时的操作,我希望构造函数能够快速返回。2. 异常处理,正如 @Yuval Itzchakov 在其中的一个回答中提到的。 - Nacharya

1
这当然是个看法问题。但我会根据你想要执行的操作长度来决定做什么。如果加载插件需要时间,我会创建一个公共方法,在使用类之前任何用户都需要调用它。另一种方法是将该方法放在构造函数中,但我认为构造函数应该尽快返回并包含字段/属性初始化。
class PluginProvider
{
    private bool _isInitialized;
    IEnumerable<IPluginType> PluginTypes { get; set;}

    public void Initialize()
    {
        if (_isInitialized)
        {
             return;
        }      

        InitializePlugins();
        _isInitialized = true;  
    }
}

请注意,这种方法的缺点是,在使用任何操作之前,您必须确保已调用Initialize方法。
另一件事情突然想到支持这种方法是异常处理。我相信您不希望构造函数在无法从文件系统加载类型时抛出任何类型的IOException。

1
是的,我有与以下相同的担忧: 1)插件加载是一项昂贵的操作,可能会有数百个插件; 2)构造函数可能会抛出异常的风险。由于插件加载发生在应用程序启动时,因此我可以将InitializePlugins()设置为公共方法,并在访问类中的任何属性之前调用它。 - Nacharya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接