Created
September 5, 2013 13:03
-
-
Save cerebrate/6449820 to your computer and use it in GitHub Desktop.
PostSharp: Doing Singletons Right
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
namespace ArkaneSystems.Arkane.Aspects | |
{ | |
/// <summary> | |
/// A constraint which verifies that the class to which it is applied obeys the Singleton pattern | |
/// we use. | |
/// </summary> | |
/// <remarks> | |
/// The singleton pattern is as follows: | |
/// private static readonly Lazy{$CLASS$} lazy = new Lazy{$CLASS$} (() => new $CLASS$()); | |
/// public static $CLASS$ Instance { get { return lazy.Value; } } | |
/// private $CLASS$ () | |
/// {} | |
/// </remarks> | |
[MulticastAttributeUsage (MulticastTargets.Class, Inheritance = MulticastInheritance.None)] | |
public sealed class SingletonConstraint : ScalarConstraint | |
{ | |
/// <summary> | |
/// Validates the element of code to which the constraint is applied. | |
/// </summary> | |
/// <param name="target"> | |
/// Element of code to which the constraint is applied (<see cref="T:System.Reflection.Assembly" />, | |
/// <see cref="T:System.Type" />, | |
/// <see cref="T:System.Reflection.MethodInfo" />, <see cref="T:System.Reflection.ConstructorInfo" />, | |
/// <see cref="T:System.Reflection.PropertyInfo" />, | |
/// <see cref="T:System.Reflection.EventInfo" />, <see cref="T:System.Reflection.FieldInfo" />, | |
/// <see cref="T:System.Reflection.ParameterInfo" />). | |
/// </param> | |
public override void ValidateCode (object target) | |
{ | |
var targetType = (Type) target; | |
// Check for private static readonly field, named lazy, typed Lazy<T>. | |
// Make required generic type. | |
Type generic = (typeof (Lazy<>)).MakeGenericType (targetType); | |
var field = targetType.GetField ("Lazy", BindingFlags.NonPublic | BindingFlags.Static); | |
if (field == null || field.IsPrivate != true || field.FieldType != generic || field.IsInitOnly != true) | |
{ | |
Message.Write (targetType, | |
SeverityType.Error, | |
"2001", | |
"The {0} type does not have 'private static readonly Lazy<{0}> lazy = new Lazy<{0}> (() => new {0} ());'.", | |
targetType.Name); | |
} | |
// Check for public static property, read-only, named Instance, returning T. | |
var property = targetType.GetProperty ("Instance", | |
BindingFlags.Public | BindingFlags.Static, | |
null, | |
targetType, | |
new Type[0], | |
null); | |
if (property == null || property.CanRead != true || property.CanWrite) | |
{ | |
Message.Write (targetType, | |
SeverityType.Error, | |
"2002", | |
"The {0} type does not have 'public static {0} Instance {{ get {{ return lazy.Value; }} }}'.", | |
targetType.Name); | |
} | |
// Check for private static parameterless constructor and absence of other constructors. | |
var constructors = | |
targetType.GetConstructors (BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); | |
bool ppc = false; | |
bool other = false; | |
foreach (ConstructorInfo c in constructors) | |
{ | |
if (c.IsPrivate && (c.GetParameters ().Length == 0)) | |
ppc = true; | |
else | |
other = true; | |
} | |
if (!ppc || other) | |
{ | |
Message.Write (targetType, | |
SeverityType.Error, | |
"2003", | |
"The {0} type does not have a single, parameterless private constructor.", | |
targetType.Name); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
(Checks to make sure all [SingletonConstraint] flagged singletons follow the same appropriate pattern.)