public class SingletonThe above implementation creates a new static instance during the class initialization. The singleton instance is retrieved by a static GetInstance method. An alternative is expose the singleton object by read-only property without a setter. The code needs a little bit change if a lazy creation is required:
{
private static Singleton instance = new Singleton();
private Singleton() { }
public static Singleton GetInstance()
{
return instance;
}
}
public class SingletonSyncLock in above code is for thread-safe consideration. In addition, the singleton class could be sealed if you don't like people to inherit from it and break the singleton principle.
{
private static volatile Singleton instance;
private static object syncObj = new Object();
private Singleton() {}
public static Singleton GetInstance()
{
if (instance == null)
{
lock (syncObj)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
Singleton pattern is very useful in some use cases. Here's a real life example. I needed a file logger for a simple web service application used in a small company. Enterprise library, logging application block, and even log4Net are just too heavy for such small application. In addition, the client is rejective to third-party tools.
I decided to write a file logger by myself. A stream writer was used, but I don't want multiple instances of stream writers being created and used at the same time. The IO is expensive and multiple accesses to a single file is not safe. Singleton came to play and only one instance of logger and one stream writer is created:
using System;
using System.IO;
using System.Configuration;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
Logger.WriteLog("Jus a test.");
Logger.WriteLog(LogSeverity.Info, "Another test.");
Console.Read();
}
}
// A helper static class to get access the logger
public static class Logger
{
private static readonly FileLogger _logger = FileLogger.GetInstance();
public static void WriteLog(String log)
{
_logger.Write(log);
}
public static void WriteLog(LogSeverity logLevel, String log)
{
_logger.Write(logLevel, log);
}
}
// Enumeration for log severity
public enum LogSeverity { Error, Warning, Info, Debug };
// FileLogger class with Singleton pattern
internal sealed class FileLogger : IDisposable
{
private static FileLogger _instance = null; // Singleton object
private static object _syncLock = new object(); // Sync lock
internal StreamWriter _writer; // Stream writer writting to the log file
internal string _path = "C:\\Logs"; // Default folder
internal string _absolutePath; // C:\Logs\MachineName.log
internal string _fileName; // MachineName.log
internal int _logFileSize = 2; // New log file when its size bigger than 2 MB
internal LogSeverity _defaultSeverity = LogSeverity.Error; // Default severity
// Private constructor
private FileLogger()
{
GetConfigSettings();
_fileName = string.IsNullOrEmpty(Environment.MachineName) ?
"LogFile.log" : Environment.MachineName + ".log";
_absolutePath = _path + "\\" + _fileName;
if (!Directory.Exists(_path))
{
Directory.CreateDirectory(_path);
}
_writer = new StreamWriter(_absolutePath, true);
}
// Public static method to get Singleton object
public static FileLogger GetInstance()
{
if (_instance == null)
{
lock (_syncLock)
{
if (_instance == null)
{
_instance = new FileLogger();
}
}
}
return _instance;
}
// Check if the log file exceeds the maximum size
private void CheckFileSize()
{
if (_writer.BaseStream.Length >= (_logFileSize * 1024 * 1024))
{
this.Dispose();
string backupFile = string.Format("{0}\\{1}-{2}.log",
_path, Environment.MachineName, DateTime.Now.ToString("yyyyMMdd-HHmmsss"));
try
{
while (File.Exists(backupFile))
{
backupFile = backupFile.Replace(".log", "_1.log");
}
File.Move(_absolutePath, backupFile);
this.Dispose();
_writer = new StreamWriter(_absolutePath, true);
}
catch (Exception ex)
{
WriteToEventLog(ex.Message);
}
}
}
// Get default setting from configuraiton file
private void GetConfigSettings()
{
string appSetting = ConfigurationManager.AppSettings["FileLogger_Path"];
if (!string.IsNullOrEmpty(appSetting))
{
_path = appSetting;
}
int maxFileSize;
appSetting = ConfigurationManager.AppSettings["FileLogger_MaxFileSizeInMB"];
if (!string.IsNullOrEmpty(appSetting) && int.TryParse(appSetting, out maxFileSize))
{
_logFileSize = maxFileSize;
}
appSetting = ConfigurationManager.AppSettings["FileLogger_DefaultSeverity"];
if (!string.IsNullOrEmpty(appSetting))
{
try
{
_defaultSeverity = (LogSeverity)Enum.Parse(typeof(LogSeverity), appSetting);
}
catch (Exception ex)
{
WriteToEventLog(ex.Message);
}
}
}
// Clean up the stream writer
public void Dispose()
{
lock (_syncLock)
{
if (_writer != null)
{
_writer.Dispose();
}
}
}
// Write with default log severity
public void Write(string msg)
{
Write(_defaultSeverity, msg);
}
// Write message to log file
public void Write(LogSeverity logSeverity, string msg)
{
lock (_syncLock)
{
try
{
if (_writer.BaseStream == null)
{
_writer = new StreamWriter(_absolutePath, true);
}
CheckFileSize();
_writer.WriteLine("[{0}] at {1} -- {2}",
logSeverity.ToString(), DateTime.Now.ToString(), msg);
_writer.Flush();
}
catch (Exception ex)
{
WriteToEventLog(ex.Message);
}
}
}
private void WriteToEventLog(string message)
{
string source = "Logger";
string log = "Application";
try
{
if (!EventLog.SourceExists(source))
EventLog.CreateEventSource(source, log);
EventLog.WriteEntry(source, message, EventLogEntryType.Error);
}
catch { }
}
}