Hands-On Labs StyleCop and Code Analysis
08 December 2010
-
.NET
This week I will be one of the speakers at BASTA On Tour in Munich. One of the topics I am going to speak about is the Managed Extensibility Framework (MEF). In this blog post I want to share my slides and summarize the hands-on labs that I am going to go through with the participants.
Hands-On Lab 1: StyleCop Documentation Rules
Prerequisites:
Lab step by step description:
Create a class library project StyleCopDemo .
Add the following class to the newly created project:
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
namespace styleCopDemo
{
public class utils_Bucket < T >
{
public utils_Bucket ()
{
}
public utils_Bucket ( IEnumerable < Tuple < string , T >> data )
{
foreach ( var item in data ) {
this . data [ item . Item1 ] = item . Item2 ;
}
}
private Dictionary < string , T > data = new Dictionary < string , T >();
public T this [ string index ]
{
get
{
if ( data . ContainsKey ( index ))
{
return data [ index ];
}
else
return default ( T );
}
}
public int GetLength ()
{
return this . data . Keys . Count ;
}
private string Dump ()
{
return this . data . Aggregate < KeyValuePair < string , T >, StringBuilder >(
new StringBuilder (),
( agg , item ) =>
{
if ( agg . Length > 0 )
{
agg . Append ( ", " );
}
agg . AppendFormat ( "{0}: {1}" , item . Key , item . Value . ToString ());
return agg ;
}). ToString ();
}
public override string ToString ()
{
return this . Dump ();
}
}
}
Launch StyleCop Settings (right-click on project, select StyleCop settings ).
Enable all rules except Spacing Rules / SA1027
Build your project and make sure that there are no errors and no warning.
Run StyleCop on your project (right-click on project, select Run StyleCop ). As you can see StyleCop generated a bunch of warnings.
Correct all warnings appropriately.
After that your file should look similar to the following implementation:
//-------------------------------------------------------
// <copyright file="Bucket.cs" company="Contoso Ltd.">
// Copyright (c) Contoso Ltd. All rights reserved.
// </copyright>
//-------------------------------------------------------
namespace StyleCopDemo
{
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Text ;
/// <summary>
/// Implements a bucket
/// </summary>
/// <typeparam name="T">Type of the elements in the bucket</typeparam>
public class Bucket < T >
{
/// <summary>
/// Internal helper to store data
/// </summary>
private Dictionary < string , T > data = new Dictionary < string , T >();
/// <summary>
/// Initializes a new instance of the Bucket class.
/// </summary>
public Bucket ()
{
}
/// <summary>
/// Initializes a new instance of the Bucket class.
/// </summary>
/// <param name="data">The initial data.</param>
public Bucket ( IEnumerable < Tuple < string , T >> data )
{
foreach ( var item in data )
{
this . data [ item . Item1 ] = item . Item2 ;
}
}
/// <summary>
/// Gets the element at the specified index.
/// </summary>
/// <param name="index">Index of the element to get.</param>
/// <value>Element at the specified index.</value>
public T this [ string index ]
{
get
{
if ( this . data . ContainsKey ( index ))
{
return this . data [ index ];
}
else
{
return default ( T );
}
}
}
/// <summary>
/// Gets the length.
/// </summary>
/// <returns>Length of the dictionary</returns>
public int GetLength ()
{
return this . data . Keys . Count ;
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString ()
{
return this . Dump ();
}
/// <summary>
/// Dumps this instance.
/// </summary>
/// <returns>String representation of the dictionary</returns>
private string Dump ()
{
return this . data . Aggregate < KeyValuePair < string , T >, StringBuilder >(
new StringBuilder (),
( agg , item ) =>
{
if ( agg . Length > 0 )
{
agg . Append ( ", " );
}
agg . AppendFormat ( "{0}: {1}" , item . Key , item . Value . ToString ());
return agg ;
}). ToString ();
}
}
}
Hands-On Lab 2: StyleCop Build Integration
Prerequisites:
Visual Studio 2010
Download and install the latest version of the StyleCop from http://stylecop.codeplex.com/ Make sure that you have selected MSBuild integration files when installing StyleCop
Complete Hands-On Lab 1 (see above)
Lab step by step description:
Open the resulting solution from Hands-On Lab 1 (StyleCopDemo ).
Unload the StyleCopDemo project (right-click on project in Solution Explorer , select Unload Project ).
Edit StyleCopDemo project (right-click on project in Solution Explorer , select Edit StyleCopDemo.csproj ).
Scroll to the end of the file. Find the line <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> .
Immediately after that line add the following line:
<Import Project= "$(ProgramFiles)\MSBuild\Microsoft\StyleCop\v4.4\Microsoft.StyleCop.targets" />
Reload the StyleCopDemo project (right-click on project in Solution Explorer , select Reload Project ).
Build your project to see that there are no errors and warnings.
Break a StyleCop rule (e.g. remove the documentation of a method).
Build your project.
StyleCop should have run automatically, you should see an appropriate warning.
Unload the StyleCopDemo project (right-click on project in Solution Explorer , select Unload Project ).
Edit StyleCopDemo project (right-click on project in Solution Explorer , select Edit StyleCopDemo.csproj ).
Find the first PropertyGroup in the project file and add the following setting:
<StyleCopTreatErrorsAsWarnings> false</StyleCopTreatErrorsAsWarnings>
Reload the StyleCopDemo project (right-click on project in Solution Explorer , select Reload Project ).
Build your project.
The StyleCop warning should now be an error.
Suppress the warning/error using the SuppressMessage attribute:
[ SuppressMessage ( "Microsoft.StyleCop.CSharp.DocumentationRules" , "SA1600:ElementsMustBeDocumented" ,
Justification = "No time to write documentation..." )]
public class Bucket < T >
{
...
}
Hands-On Lab 3: Code Analysis
Prerequisites:
Visual Studio 2010
Complete Hands-On Lab 1 (see above)
Lab step by step description:
Add a new class library project to solution from Hands-On Lab 1 (StyleCopDemo ).
Add the following class to the newly created project:
namespace CodeAnalysisDemo
{
using System ;
using System.Collections.Generic ;
using System.Data.SqlClient ;
using System.IO ;
public interface ISwiftItem < T >
{
string ItemName { get ; }
List < T > Values { get ; }
}
public interface INamedItem
{
string ItemName { get ; }
}
public class SwiftItem < T > : ISwiftItem < T >
{
public string ItemName
{
get ;
set ;
}
public List < T > Values
{
get ;
set ;
}
}
public class SwiftFile
{
private Stream underlying_File ;
private string fileName ;
private object header ;
public SwiftFile ( string fileName )
{
this . underlying_File = new FileStream ( fileName , FileMode . Open );
this . fileName = fileName ;
this . header = new object ();
}
public List < Tuple < string , object >> Settings
{
get ;
set ;
}
public void AddObject < T >( SwiftItem < T > newObj )
{
lock ( this . header )
{
var header = string . Format ( "{0}: {1}" , newObj . ItemName , newObj . Values . Count );
foreach ( var item in newObj . Values )
{
if ( item is INamedItem )
{
var namedItem = item as INamedItem ;
// do something special with namedItem
}
}
// do something with newObj
}
}
public void CopyFile ( string source_file_name )
{
using ( var reader = new StreamReader ( new FileStream ( source_file_name , FileMode . Open )))
{
// TODO: copy file here
}
}
public void WriteToDatabase ( SqlConnection conn , string tenant )
{
var cmd = conn . CreateCommand ();
cmd . CommandText = string . Format ( "INSERT INTO Target ( Tenant, Data ) VALUES ( {0}, @Data )" , tenant );
// Build rest of the command and execute it
}
public void Close ()
{
try
{
this . underlying_File . Close ();
}
catch
{
Console . WriteLine ( "Error" );
}
}
}
}
Build your project and make sure that there are no error and no warnings.
Enable Code Analysis for the project and select rule set Microsoft All Rules .
You find the necessary options in the project's property window.
Build your project. As you can see code analysis shows you a bunch of warnings.
Try to correct all warnings appropriately. If you are not sure what a certain warning means or why it is important check the rule documentation in MSDN: http://msdn.microsoft.com/en-us/library/ee1hzekz.aspx
After that your file should look similar to the following implementation:
using System ;
[ assembly : CLSCompliant ( true )]
namespace CodeAnalysisDemo
{
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.Data ;
using System.Data.SqlClient ;
using System.Diagnostics.CodeAnalysis ;
using System.IO ;
public interface ISwiftItem < T >
{
string ItemName { get ; }
IEnumerable < T > Values { get ; }
}
public interface INamedItem
{
string ItemName { get ; }
}
public class SwiftItem < T > : ISwiftItem < T >
{
public string ItemName
{
get ;
set ;
}
public IEnumerable < T > Values
{
get ;
set ;
}
}
public class Setting
{
public string SettingName { get ; set ; }
public object SettingValue { get ; set ; }
}
public class SettingCollection : Collection < Setting >
{
}
public class SwiftFile : IDisposable
{
private Stream underlying_File ;
private object header ;
private bool disposed ;
public SwiftFile ( string fileName )
{
this . underlying_File = new FileStream ( fileName , FileMode . Open );
this . header = new object ();
this . Settings = new SettingCollection ();
}
public SettingCollection Settings
{
get ;
private set ;
}
public void AddObject < T >( SwiftItem < T > newObj )
{
if ( newObj != null )
{
lock ( this . header )
{
foreach ( var item in newObj . Values )
{
var namedItem = item as INamedItem ;
if ( namedItem != null )
{
// do something special with namedItem
}
}
// do something with newObj
}
}
}
[ SuppressMessage ( "Microsoft.Performance" , "CA1822" , Justification = "Will reference 'this' later" )]
public void CopyFile ( string sourceFileName )
{
var stream = new FileStream ( sourceFileName , FileMode . Open );
StreamReader reader ;
try
{
reader = new StreamReader ( stream );
}
catch
{
stream . Dispose ();
throw ;
}
try
{
// do something with reader
}
finally
{
reader . Dispose ();
}
}
[ SuppressMessage ( "Microsoft.Performance" , "CA1822" , Justification = "Will reference 'this' later" )]
public void WriteToDatabase ( SqlConnection conn , string tenant )
{
if ( conn == null || ( conn . State & ConnectionState . Open ) == 0 )
{
throw new ArgumentException ( "conn must not be null and must be open" );
}
var cmd = conn . CreateCommand ();
cmd . CommandText = "INSERT INTO Target ( Tenant, Data ) VALUES ( @Tenant, @Data )" ;
cmd . Parameters . Add ( "@Tenant" , System . Data . SqlDbType . NVarChar , 100 ). Value = tenant ;
// Build rest of the command and execute it
}
public void Close ()
{
try
{
this . underlying_File . Close ();
}
catch
{
Console . WriteLine ( "Error" );
throw ;
}
}
private void Dispose ( bool disposing )
{
if (! this . disposed )
{
if ( disposing )
{
this . underlying_File . Dispose ();
}
disposed = true ;
}
}
public void Dispose ()
{
this . Dispose ( true );
GC . SuppressFinalize ( this );
}
~ SwiftFile ()
{
Dispose ( false );
}
}
}