Last active
January 21, 2018 19:00
-
-
Save edongashi/e6f55efa63f5b1cc7a2b8171090bcb41 to your computer and use it in GitHub Desktop.
Tracks the content of a single file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Tracks the content of a single file. | |
/// </summary> | |
public sealed class FileWatcher : IDisposable, INotifyPropertyChanged | |
{ | |
private class Watcher : IDisposable | |
{ | |
private readonly List<FileWatcher> listeners; | |
private readonly FileSystemWatcher fileSystemWatcher; | |
private readonly string filePath; | |
private bool isLatestValue; | |
private string value; | |
public Watcher(FileWatcher initialListener) | |
{ | |
filePath = initialListener.filePath; | |
listeners = new List<FileWatcher> { initialListener }; | |
fileSystemWatcher = new FileSystemWatcher | |
{ | |
Path = Path.GetDirectoryName(filePath), | |
Filter = Path.GetFileName(filePath), | |
EnableRaisingEvents = true | |
}; | |
fileSystemWatcher.Created += (s, e) => Update(); | |
fileSystemWatcher.Changed += (s, e) => Update(); | |
fileSystemWatcher.Deleted += (s, e) => Update(); | |
fileSystemWatcher.Renamed += (s, e) => Update(); | |
fileSystemWatcher.Error += (s, e) => Update(); | |
} | |
public string Value | |
{ | |
get | |
{ | |
lock (this) | |
{ | |
if (!isLatestValue) | |
{ | |
value = TryReadFile(); | |
isLatestValue = true; | |
} | |
return value; | |
} | |
} | |
private set => this.value = value; | |
} | |
public int Count => listeners.Count; | |
public void AddListener(FileWatcher listener) | |
{ | |
listeners.Add(listener); | |
} | |
public void RemoveListener(FileWatcher listener) | |
{ | |
listeners.Remove(listener); | |
} | |
public void Dispose() | |
{ | |
fileSystemWatcher.Dispose(); | |
} | |
private void Update() | |
{ | |
try | |
{ | |
fileSystemWatcher.EnableRaisingEvents = false; | |
isLatestValue = false; | |
foreach (var listener in listeners) | |
{ | |
listener.NotifyChanged(); | |
} | |
} | |
finally | |
{ | |
fileSystemWatcher.EnableRaisingEvents = true; | |
} | |
} | |
private string TryReadFile() | |
{ | |
try | |
{ | |
using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) | |
{ | |
using (var reader = new StreamReader(stream)) | |
{ | |
return reader.ReadToEnd(); | |
} | |
} | |
} | |
catch | |
{ | |
return ""; | |
} | |
} | |
} | |
private static readonly Dictionary<string, Watcher> Watchers | |
= new Dictionary<string, Watcher>(StringComparer.OrdinalIgnoreCase); | |
private readonly string filePath; | |
private bool disposed; | |
public FileWatcher(string filePath) | |
{ | |
filePath = Path.GetFullPath(filePath ?? throw new ArgumentNullException(nameof(filePath))); | |
this.filePath = filePath; | |
lock (Watchers) | |
{ | |
if (Watchers.ContainsKey(filePath)) | |
{ | |
Watchers[filePath].AddListener(this); | |
} | |
else | |
{ | |
Watchers[filePath] = new Watcher(this); | |
} | |
} | |
} | |
~FileWatcher() | |
{ | |
Dispose(false); | |
} | |
public string Content | |
{ | |
get | |
{ | |
lock (Watchers) | |
{ | |
if (disposed) | |
{ | |
throw new ObjectDisposedException(nameof(FileWatcher)); | |
} | |
return Watchers[filePath].Value; | |
} | |
} | |
} | |
public event EventHandler ContentChanged; | |
public event PropertyChangedEventHandler PropertyChanged; | |
private void NotifyChanged() | |
{ | |
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Content))); | |
ContentChanged?.Invoke(this, EventArgs.Empty); | |
} | |
public void Dispose() | |
{ | |
Dispose(true); | |
GC.SuppressFinalize(this); | |
} | |
private void Dispose(bool disposing) | |
{ | |
lock (Watchers) | |
{ | |
if (disposed) | |
{ | |
return; | |
} | |
disposed = true; | |
var watcher = Watchers[filePath]; | |
watcher.RemoveListener(this); | |
if (watcher.Count == 0) | |
{ | |
watcher.Dispose(); | |
Watchers.Remove(filePath); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment