【发布时间】:2015-03-23 15:26:40
【问题描述】:
我有一个简单的 FeatureToggle 类,它使用 FilesSystemWatcher 来监视 app.config 文件。如果 .config 文件发生更改,它将从 appSettings 部分重新加载设置。我让它实现了 IDisposable,但是我不能使用 using {} 语句,因为我希望在程序运行时监视 .config 文件,但是我确实希望在进程结束时调用 Dispose(),我可以' t 将 finally() 块添加到主程序。如何调用 Dispose()。我会在这里使用终结器吗?
功能切换:
public sealed class FeatureToggle : IDisposable
{
private static readonly FeatureToggle instance = new FeatureToggle();
private static FeatureWatcher featureWatcher = new FeatureWatcher();
private static ConcurrentDictionary<string, bool> features = new ConcurrentDictionary<string, bool>();
private bool disposed;
static FeatureToggle()
{
}
private FeatureToggle()
{
}
public static FeatureToggle Instance
{
get
{
return instance;
}
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
public bool Enabled(string featureName)
{
string path = this.GetAssemblyPath();
if (featureWatcher.GetWatcher(path) == null)
{
FileSystemWatcher watcher = featureWatcher.AddWatcher(path);
if (watcher != null)
{
watcher.Changed += this.OnChanged;
this.Refresh(path);
}
}
return this.Get(featureName);
}
private void Add(string key, bool value)
{
features.AddOrUpdate(key, value, (k, v) => value);
}
private void Dispose(bool disposing)
{
if (this.disposed)
{
return;
}
if (disposing)
{
featureWatcher.Dispose();
featureWatcher = null;
features = null;
}
}
private bool Get(string key)
{
bool value;
if (features.TryGetValue(key, out value))
{
return value;
}
return false;
}
private string GetAssemblyPath()
{
return AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
}
private IEnumerable<KeyValuePair<string, bool>> LoadConfig(string path)
{
ExeConfigurationFileMap configMap = new ExeConfigurationFileMap { ExeConfigFilename = path };
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(
configMap,
ConfigurationUserLevel.None);
var settings =
config.AppSettings.Settings.Cast<KeyValueConfigurationElement>()
.Where(x => x.Key.StartsWith("FeatureToggle."))
.ToDictionary(o => o.Key.ToString(CultureInfo.InvariantCulture), o => Convert.ToBoolean(o.Value));
return settings;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
// app.config changed - run update
this.Refresh(e.FullPath);
}
private void Refresh(string path)
{
foreach (var kv in this.LoadConfig(path))
{
this.Add(kv.Key, kv.Value);
}
}
}
FileWatcher:
public class FeatureWatcher : IDisposable
{
private static ConcurrentDictionary<string, FileSystemWatcher> watchers;
private bool disposed;
public FeatureWatcher()
{
watchers = new ConcurrentDictionary<string, FileSystemWatcher>();
}
public FileSystemWatcher AddWatcher(string path)
{
if (this.GetWatcher(path) == null)
{
var parentPath = Path.GetDirectoryName(path);
var watcher = new FileSystemWatcher
{
Path = parentPath,
NotifyFilter = NotifyFilters.LastWrite,
Filter = "*.config"
};
watchers.TryAdd(path, watcher);
watcher.EnableRaisingEvents = true;
return watcher;
}
return null;
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
public FileSystemWatcher GetWatcher(string path)
{
FileSystemWatcher watcher;
if (!watchers.TryGetValue(path, out watcher))
{
return null;
}
return watcher;
}
protected virtual void Dispose(bool disposing)
{
if (this.disposed)
{
return;
}
if (disposing)
{
// Clean up managed resources
if (watchers != null)
{
foreach (var watcher in watchers.Values)
{
watcher.EnableRaisingEvents = false;
watcher.Dispose();
}
watchers = null;
}
this.disposed = true;
}
}
}
【问题讨论】:
-
为什么要在进程终止时处理掉它?资源会自动释放,没有清理优势
-
但是 FileSystemWatcher 实现了 IDisposable,因此我需要在使用它的类中实现 IDisposable。这不正确吗?
-
这是个好主意,但由于在进程退出之前您不会释放它,因此调用 dispose 是没有意义的
-
如果我这样放着,不调用 Dispose() 程序结束后 GC 会释放内存吗?
-
所有内存在进程终止时被释放