|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Table of contents
IntroductionThe CLR and other .NET code contains many recurring patterns. As these patterns appear in code, they may also appear in code generated by CodeDom graphs, yet generating these patterns requires lot of work, which is quite repetitive. This library contains many pattern implementations for use in your own CodeDom generator, which could help you in decreasing the amount of code you write by thousands of lines. As of October 23, 2006, the project is hosted on CodePlex, with this article serving as an introduction to its abilities. Argument Assertion patternsThe Argument Assertion patterns are recurring patterns from the CLR in which arguments are checked and exceptions such as In order to include this pattern in your code, your code should look like this: myMethod.Statements.Add(
new CodePatternArgumentAssertNotNullStatement("myArgument"));
myMethod.Statements.Add(
new CodePatternArgumentAssertInRangeStatement("myArgument",
new CodeFieldReferenceExpression(
new CodeTypeReferenceExpression("MyType"), "MinValue"),
new CodeFieldReferenceExpression(
new CodeTypeReferenceExpression("MyType"), "MaxValue")));
myMethod.Statements.Add(
new CodePatternArgumentAssertIsInstanceOfStatement(
"myArgument", typeof(int)));
The code generated by above will be: if ((myArgument == null))
{
throw new System.ArgumentNullException("myArgument");
}
if (((myArgument > MyType.MaxValue)
|| (myArgument < MyType.MinValue)))
{
throw new System.ArgumentOutOfRangeException("myArgument");
}
if ((myArgument.GetType().IsInstanceOfType(typeof(int)) == false))
{
throw new System.ArgumentException(string.Format(
"The argument myArgument must be of type {0}.",
typeof(int).FullName), "myArgument");
}
The Assembly Information patternAdded automatically by Visual Studio, an assembly is identifiable by a set of attributes. This pattern simplifies access to these attributes as properties of a class deriving from In order to include this pattern in your code, your code should look like this: CodePatternCompileUnit unit = new CodePatternCompileUnit();
unit.AssemblyVersion = new Version(1, 0);
unit.AssemblyTitle = "My assembly";
unit.CLSCompliant = true;
The code generated by above will be: [assembly: System.Reflection.AssemblyVersionAttribute("1.0")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0")]
[assembly: System.Reflection.AssemblyTitleAttribute("My assembly")]
[assembly: System.CLSCompliantAttribute(true)]
The Asynchronous Operation patternA recurring pattern from the CLR, asynchronous invocation of methods is required by many systems and components. The implementation presented here is the simplest one, which uses delegates. Documentation on the members generated can be controlled using the In order to include this pattern in your code, your code should look like this: type.Members.AddRange(new CodePatternAsyncOperation(myMethod));
The code generated by above will be: /// <summary>
/// Represents the delegate instance
/// for asynchronous calls to MyMethod.
/// </summary>
private MyMethodAsyncCallback m_MyMethodCallback;
/// <summary>
/// Executes the MyMethod method asynchronously with a callback.
/// </summary>
/// <param name="foo">See original method, MyMethod,
/// for more information about this parameter.</param>
/// <param name="callback">A method to be called when
/// the asynchronous action completes.</param>
/// <returns>An <see cref="System.IAsyncResult" />
/// object detailing the asynchronous action.</returns>
public System.IAsyncResult BeginMyMethod(int foo,
System.AsyncCallback callback)
{
if ((this.m_MyMethodCallback == null))
{
this.m_MyMethodCallback =
new MyMethodAsyncCallback(this.MyMethod);
}
return this.m_MyMethodCallback.BeginInvoke(foo, callback, null);
}
/// <summary>
/// Executes the MyMethod method asynchronously.
/// </summary>
/// <param name="foo">See original method, MyMethod,
/// for more information about this parameter.</param>
/// <returns>An <see cref="System.IAsyncResult" />
/// object detailing the asynchronous action.</returns>
public System.IAsyncResult BeginMyMethod(int foo)
{
return this.BeginMyMethod(foo, null);
}
/// <summary>
/// Synchronously completes an asynchronous call to MyMethod.
/// </summary>
/// <param name="asyncResult">The <see cref="System.IAsyncResult" />
/// retrieved from the call to <see cref="BeginMyMethod" />.</param>
/// <exception cref="System.InvalidOperationException">Thrown
/// when the method is called before the
/// <see cref="BeginMyMethod" /> method.</exception>
public void EndMyMethod(System.IAsyncResult asyncResult)
{
if ((this.m_MyMethodCallback == null))
{
throw new System.InvalidOperationException("End of asynchronous" +
" operation attempted when one has not yet begun.");
}
this.m_MyMethodCallback.EndInvoke(asyncResult);
}
/// <summary>
/// Represents the delegate for asynchronous calls to MyMethod.
/// </summary>
public delegate void MyMethodAsyncCallback(int foo);
The BeginProcess/EndProcess patternA recurring pattern from In order to include this pattern in your code, your code should look like this: type.Members.AddRange(new CodePatternBeginEndProcess("Init"));
The code generated by above will be: /// <summary>
/// See <see cref="IsInInit" />
/// for information about this field.
/// </summary>
private int m_IsInInit;
/// <summary>
/// Begins the Init process.
/// </summary>
public virtual void BeginInit()
{
this.m_IsInInit = (this.m_IsInInit + 1);
}
/// <summary>
/// Ends the Init process.
/// </summary>
public virtual void EndInit()
{
if ((this.m_IsInInit != 0))
{
this.m_IsInInit = (this.m_IsInInit - 1);
}
}
/// <summary>
/// Gets whether the Init process has begun.
/// </summary>
/// <value>Whether the init process has begun.</value>
protected bool IsInInit()
{
return (this.m_IsInInit != 0);
}
Binary Operator patternsMost binary operators are built into CodeDom, but some aren't. This pattern extends the normal In order to include this pattern in your code, your code should look like this: method.Statements.Add(
new CodeConditionStatement(
new CodePatternBinaryOperatorExpression(
new CodeVariableReferenceExpression("bool1"),
CodePatternBinaryOperatorType.BooleanExclusiveOr,
new CodeVariableReferenceExpression("bool2"))
/* , Contained statements */));
The code generated by above will be: if (((bool1 == true) && (bool2 == false)) ||
((bool1 == false) && (bool2 == true)))
{
// Contained statements...
}
Code Access Security Decorator patternsThe model of declarative Code Access Security is widespread in the CLR and also very important in many differing situations. In order to include this pattern in your code, your code should look like this: UI ui = new DotNetZen.CodeDom.Patterns.Permissions.UI();
ui.Clipboard =
System.Security.Permissions.UIPermissionClipboard.AllClipboard;
myMethod.CustomAttributes.Add(new CodePatternCasAttribute(ui));
The code generated by above will be: [System.Security.Permissions.UIPermissionAttribute(
System.Security.Permissions.SecurityAction.Demand,
Clipboard=System.Security.Permissions.
UIPermissionClipboard.AllClipboard)]
private void MyMethod()
{
}Currently all of the Framework's declarative CAS attributes are supported and custom attribute abstractions can be created simply by inheriting from DotNetZen.CodeDom.Patterns.Permissions.Permission.
Compound Assignment patternsNo compound assignment operators are built into CodeDom. This pattern extends the normal In order to include this pattern in your code, your code should look like this: method.Statements.Add(
new CodePatternCompoundAssignStatement(
new CodeVariableReferenceExpression("foo"),
CodePatternCompoundAssignmentOperatorType.Add,
new CodeVariableReferenceExpression("bar")));
The code generated by above will be: foo = (foo + bar);
The Cursor Lock patternA recurring pattern from In order to include this pattern in your code, your code should look like this: method.Statements.AddRange(
new CodePatternCursorLock(/* Contained statements */));
The code generated by above will be: System.Windows.Forms.Cursor cursor0 = this.Cursor;
try
{
this.Cursor = System.Windows.Forms.Cursors.WaitCursor;
// More code here...
}
finally
{
this.Cursor = cursor0;
}
The Custom Attribute patternMost custom attributes derive from the same template. As a result, creating custom attributes can be a menial task. Documentation on the members generated can be controlled using the In order to include this pattern in your code, your code should look like this: CodePatternCustomAttributeDeclaration attrib =
new CodePatternCustomAttributeDeclaration(
"CoolMetaData",
AttributeTargets.Struct |
AttributeTargets.Class | AttributeTargets.Enum,
false, true,
new CodeParameterDeclarationExpression(typeof(int),
"MetaData"));
attrib.SetComment("MetaData", "The metadata for the attribute");
The code generated by above will be: [System.AttributeUsageAttribute(((System.AttributeTargets.Enum |
System.AttributeTargets.Struct)
| System.AttributeTargets.Class),
AllowMultiple=false, Inherited=true)]
public sealed class CoolMetaDataAttribute : System.Attribute
{
/// <summary>
/// Value for the property <see cref="MetaData" />.
/// </summary>
private int m_MetaData;
/// <summary>
/// Initializes a new instance of the
/// <see cref="CoolMetaDataAttribute" /> class.
/// </summary>
public CoolMetaDataAttribute()
{
}
/// <summary>
/// Initializes a new instance of the
/// <see cref="CoolMetaDataAttribute" /> class.
/// </summary>
/// <param name="MetaData">The metadata
/// for the attribute.</param>
public CoolMetaDataAttribute(int MetaData)
{
this.m_MetaData = MetaData;
}
/// <summary>
/// Gets the metadata for the attribute.
/// </summary>
/// <value>The metadata for the attribute.</value>
public int MetaData
{
get
{
return this.m_MetaData;
}
}
}
The Custom Exception patternMost custom exceptions derive from the same template. As a result, creating custom exceptions can be a menial task. Documentation on the members generated can be controlled using the In order to include this pattern in your code, your code should look like this: CodePatternCustomExceptionDeclaration exception =
new CodePatternCustomExceptionDeclaration("Foo",
new CodeParameterDeclarationExpression(
typeof(int), "Bar"));
exception.SetComment("Bar", "A healthy snack-bar");
The code generated by above will be: [System.SerializableAttribute()]
public class FooException : System.Exception
{
/// <summary>
/// Value for the property <see cref="Bar" />.
/// </summary>
private int m_Bar;
/// <summary>
/// Initializes a new instance of the <see cref="FooException" /> class.
/// </summary>
/// <param name="Bar">A healthy snack-bar.</param>
public FooException(int Bar)
{
this.m_Bar = Bar;
}
/// <summary>
/// Initializes a new instance of the <see cref="FooException" /> class.
/// </summary>
/// <param name="Bar">A healthy snack-bar.</param>
/// <param name="message">The message in the exception.</param>
public FooException(int Bar, string message) :
base(message)
{
this.m_Bar = Bar;
}
/// <summary>
/// Initializes a new instance of the <see cref="FooException" /> class.
/// </summary>
/// <param name="info">The data needed to serialize
/// or deserialize an object.</param>
/// <param name="context">The source and destination
/// of a given serialized stream.</param>
/// <remarks>This member supports the .NET Framework infrastructure
/// and is not intended to be used directly from your code.</remarks>
protected FooException(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) :
base(info, context)
{
this.m_Bar = ((int)(info.GetValue("m_Bar", typeof(int))));
}
/// <summary>
/// Initializes a new instance of the <see cref="FooException" /> class.
/// </summary>
/// <param name="Bar">A healthy snack-bar.</param>
/// <param name="message">The message in the exception.</param>
/// <param name="innerException">An exception
/// encapsulated in the new exception.</param>
public FooException(int Bar, string message,
System.Exception innerException) :
base(message, innerException)
{
this.m_Bar = Bar;
}
/// <summary>
/// Gets a healthy snack-bar.
/// </summary>
/// <value>A healthy snack-bar.</value>
public int Bar
{
get
{
return this.m_Bar;
}
}
/// <summary>
/// Populates a <see
/// cref="System.Runtime.Serialization.SerializationInfo" />
/// with the data needed to serialize the target object.
/// </summary>
/// <param name="info">The <see
/// cref="System.Runtime.Serialization.SerializationInfo" />
/// to populate with data.</param>
/// <param name="context">The destination
/// (see <see cref="System.Runtime.Serialization.StreamingContext" />)
/// for this serialization.</param>
/// <exception cref="System.ArgumentNullException">Thrown when
/// the <paramref name="info" /> parameter is a null reference
/// (Nothing in Visual Basic).</exception>
[System.Security.Permissions.SecurityPermissionAttribute(
System.Security.Permissions.SecurityAction.LinkDemand,
Flags=System.Security.Permissions.
SecurityPermissionFlag.SerializationFormatter)]
public override void GetObjectData(
System.Runtime.Serialization.SerializationInfo
info,
System.Runtime.Serialization.StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("m_Bar", this.m_Bar, typeof(int));
}
}
The Delegate patternThe Delegate pattern, also known as the EventHandler pattern, is a recurring pattern from the CLR, in which a delegate is created with an In order to include this pattern in your code, your code should look like this: CodePatternDelegate delegateType = new CodePatternDelegate(
"ItemChanged",
new CodeParameterDeclarationExpression(typeof(int), "OldValue"),
new CodeParameterDeclarationExpression(typeof(int), "NewValue"));
delegateType.SetComment("OldValue", "The value before the change");
delegateType.SetComment("NewValue", "The value after the change");
nameSpace.Types.AddRange(delegateType);
The code generated by above will be: /// <summary>
/// Represents a method that takes a <see cref="System.Object" />
/// and <see cref="ItemChangedEventArgs" />.
/// </summary>
/// <param name="sender">The event's originating object.</param>
/// <param name="e">The event's arguments.</param>
public delegate void ItemChangedEventHandler(object sender,
ItemChangedEventArgs e);
/// <summary>
/// Contains the arguments for events based
/// on the <see cref="ItemChangedEventHandler" /> delegate.
/// </summary>
public class ItemChangedEventArgs : System.EventArgs
{
/// <summary>
/// Value for the property <see cref="OldValue" />.
/// </summary>
private int m_OldValue;
/// <summary>
/// Value for the property <see cref="NewValue" />.
/// </summary>
private int m_NewValue;
/// <summary>
/// Initializes a new instance of the
/// <see cref="ItemChangedEventArgs" /> class.
/// </summary>
/// <param name="OldValue">The value before the change.</param>
/// <param name="NewValue">The value after the change.</param>
public ItemChangedEventArgs(int OldValue, int NewValue)
{
this.m_OldValue = OldValue;
this.m_NewValue = NewValue;
}
/// <summary>
/// Gets the value before the change.
/// </summary>
/// <value>The value before the change.</value>
public virtual int OldValue
{
get
{
return this.m_OldValue;
}
}
/// <summary>
/// Gets the value after the change.
/// </summary>
/// <value>The value after the change.</value>
public virtual int NewValue
{
get
{
return this.m_NewValue;
}
}
}
The Disposable Type patternA recurring pattern from the CLR, this recommended pattern for use of the In order to include this pattern in your code, your code should look like this: CodePatternTypeDeclaration declaration =
new CodePatternTypeDeclaration("MyType");
// ...
declaration.ApplyDisposablePattern(
new CodeInstanceReferenceExpression(new
CodeFieldReferenceExpression(new CodeThisReferenceExpression(),
"myReferenceTypeField"), typeof(object)),
new CodeInstanceReferenceExpression(new
CodeFieldReferenceExpression(new CodeThisReferenceExpression(),
"myValueTypeField"), typeof(int)));
The code generated by above will be: public class MyType : System.IDisposable
{
/// <summary>
/// Releases all resources used by the object.
/// </summary>
public void Dispose()
{
this.Dispose(true);
System.GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the unmanaged resources used by the object
/// and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">true to release both managed
/// and unmanaged resources; false
/// to release only unmanaged resources.</param>
/// <remarks>This method is called by the public
/// <see cref="Dispose()" /> method and the Finalize method.
/// <see cref="Dispose" /> invokes the protected
/// <see cref="Dispose(System.Boolean)" /> method with
/// the <paramref name="disposing" /> parameter set to true.
/// Finalize invokes Dispose with
/// <paramref name="disposing" /> set to false.
/// When the <paramref name="disposing" /> parameter is true,
/// this method releases all resources held
/// by any managed objects that this object references.
/// This method invokes the <see cref="Dispose()" />
/// method of each referenced object.
/// Notes to Inheritors: This method can be called multiple times
/// by other objects. When overriding it, be careful not to reference
/// objects that have been previously
/// disposed of in an earlier call.</remarks>
protected virtual void Dispose(bool disposing)
{
if ((disposing == true))
{
if ((this.myReferenceTypeField != null))
{
((System.IDisposable)(this.myReferenceTypeField)).Dispose();
}
((System.IDisposable)(this.myValueTypeField)).Dispose();
}
}
}
Please note that due to a bug in the .NET Framework, Creation of finalizers is impossible! Please vote on this issue on LadyBug. The Event patternThe Event pattern is a recurring pattern from In order to include this pattern in your code, your code should look like this: type.Members.AddRange(new CodePatternEvent(
"EventHappened", Scope.Instance, typeof(EventHandler)));
The code generated by above will be: public event System.EventHandler EventHappened;
/// <summary>
/// Raises the <see cref="EventHappened" /> event.
/// </summary>
/// <param name="e">The value passed
/// for the event's e parameter.</param>
protected virtual int OnEventHappened(System.EventArgs e)
{
if ((this.EventHappened != null))
{
this.EventHappened(this, e);
}
}
Please note that due to a bug in the .NET Framework, Language restrictions: Visual Basic does not allow return values from events. The Flags patternThe Flags pattern is a recurring pattern from the entire CLR, in which an In order to include this pattern in your code, your code should look like this: nameSpace.Types.Add(new CodePatternFlags("MyFlags",
"A", "B", "C"));
The code generated by above will be: [System.FlagsAttribute()]
public enum MyFlags : int
{
A = 1,
B = 2,
C = 4,
}
The For Each patternThe For Each pattern is built into C#, but is not native to IL. The pattern iterates over a collection that implements the In order to include this pattern in your code, your code should look like this: method.Statements.AddRange(new CodePatternForEach(
new CodeTypeReference(typeof(int)),
new CodeVariableReferenceExpression("myCollection"),
new CodeTypeReference("EnumeratorType")
/* , Contained statements */));
The code generated by above will be: System.Collections.IEnumerator enumerator0 =
((System.Collections.IEnumerator)(myCollection)).GetEnumerator();
try
{
for (; enumerator0.MoveNext();)
{
int element0 = ((int)(enumerator0.Current));
// Contained statements ...
}
}
finally
{
if (((enumerator0 != null) &&
enumerator0.GetType().IsInstanceOfType(
typeof(System.IDisposable))))
{
((System.IDisposable)(enumerator0)).Dispose();
}
}
The Get Property/Field patternThe Get Property/Field pattern is a recurring pattern from the entire CLR, in which a In order to include this pattern in your code, your code should look like this: type.Members.AddRange(new CodePatternGetField("Value",
new CodeTypeReference(typeof(int)), Scope.Instance));
The code generated by above will be: private int m_Value;
public int Value
{
get
{
return this.m_Value;
}
}
The Is Instance Of patternThe Is Instance Of pattern is built into C# as the In order to include this pattern in your code, your code should look like this: method.Statements.Add(new CodePatternIsInstExpression(
new CodeVariableReferenceExpression("myVariable"),
new CodeTypeReference(typeof(IMyInterface))));
The code generated by above will be: myVariable.GetType().IsInstanceOfType(typeof(IMyInterface))
The Lock patternThe Lock pattern is built into C#, but is not native to IL. The pattern locks a resource using the In order to include this pattern in your code, your code should look like this: method.Statements.AddRange(new CodePatternLock(
new CodeFieldReferenceExpression(
new CodeThisReferenceExpression(), "SyncRoot")
/* , Contained statements... */));
The code generated by above will be: object lockCachedExpr0 = this.SyncRoot;
System.Threading.Monitor.Enter(lockCachedExpr0);
try
{
// Contained statements...
}
finally
{
System.Threading.Monitor.Exit(lockCachedExpr0);
}
The Nullable Value Type Property patternThe Nullable Value Type Property pattern is used in CLR 1.x (pre-generics and Nullable<T>) to denote a value type property with a null value. This pattern is used predominately in Typed DataSets. In order to include this pattern in your code, your code should look like this: type.Members.AddRange(new CodePatternNullableProperty("Value",
new CodeTypeReference(typeof(int)), Scope.Instance));
The code generated by above will be: private int m_Value;
/// <summary>
/// See <see cref="IsValueNull" /> for information about this field.
/// </summary>
private bool m_IsValueNull = true;
public int Value
{
get
{
if ((this.m_IsValueNull == true))
{
throw new System.InvalidOperationException("Can not" +
" get value when it is null. Check for " +
"nullability by calling IsValueNull.");
}
return this.m_Value;
}
set
{
this.m_Value = value;
this.m_IsValueNull = false;
}
}
/// <summary>
/// Gets whether the value of <see cref="Value" /> is null.
/// </summary>
/// <value>Whether the value of <see cref="Value" /> is null.</value>
public bool IsValueNull
{
get
{
return this.m_IsValueNull;
}
}
/// <summary>
/// Sets the value of <see cref="Value" /> to null.
/// </summary>
public void SetValueNull()
{
this.m_IsValueNull = true;
}
The Observer patternThe Observer pattern is a classic pattern which allows subscribers to be notified of the changes to a value. This implementation allows changes to the value of a property to be announced using an event. Documentation on the members generated can be controlled using the In order to include this pattern in your code, your code should look like this: type.Members.AddRange(new CodePatternObserver("MyValue",
new CodeTypeReference(typeof(int)), Scope.Instance));
The code generated by above will be: /// <summary>
/// Value for the property <see cref="MyValue" />.
/// </summary>
private int m_MyValue;
public int MyValue
{
get
{
return this.m_MyValue;
}
set
{
if ((this.m_MyValue != value))
{
int oldValue = this.m_MyValue;
this.m_MyValue = value;
this.OnMyValueChanged(new MyValueChangedEventArgs(oldValue,
this.m_MyValue));
}
}
}
/// <summary>
/// Occurs when the <see cref="MyValue" /> property is changed.
/// </summary>
public event MyValueChangedEventHandler MyValueChanged;
/// <summary>
/// Raises the <see cref="MyValueChanged" /> event.
/// </summary>
/// <param name="e">The value passed
/// for the event's e parameter.</param>
protected virtual void OnMyValueChanged(MyValueChangedEventArgs e)
{
if ((this.MyValueChanged != null))
{
this.MyValueChanged(this, e);
}
}
/// <summary>
/// Represents a method that takes a <see cref="System.Object" />
/// and <see cref="MyValueChangedEventArgs" />.
/// </summary>
/// <param name="sender">The event's originating object.</param>
/// <param name="e">The event's arguments.</param>
public delegate void MyValueChangedEventHandler(object sender,
MyValueChangedEventArgs e);
/// <summary>
/// Contains the arguments for events based
/// on the <see cref="MyValueChangedEventHandler" /> delegate.
/// </summary>
public class MyValueChangedEventArgs : System.EventArgs
{
/// &l | ||||||||||||||||||||||||||||