Unintelligible

Saturday, October 2, 2010

Gendarme MSBuild task

I just wrote a quick MSBuild wrapper around the great Gendarme tool. It allows running Gendarme as part of an MSBuild task/project, and supports all the options that the command-line runner supports.

It also integrates with Visual Studio:

Visual Studio screenshot

The binary can be downloaded here: https://github.com/unintelligible/GendarmeMsBuild/downloads

The source code is available here: https://github.com/unintelligible/GendarmeMsBuild

posted by Nick at 1:14 am - filed in .net  

Monday, May 24, 2010

Resharper 4.5 inspection severity list

For some reason, I couldn’t find a list of the different Resharper code inspections and their severity anywhere on the web (not even in Jetbrains’ site); this is useful to go through the available inspections with team members to decide which we should enable across our team.

So, for convenience, I’m posting one here; this is for Resharper 4.5. Click to expand the table.

Settings entry name Section Definition Default severity
ClassCanBeSealed.Global Code Redundancies Class can be made sealed - Non-private accessibility DO_NOT_SHOW
ClassCanBeSealed.Local Code Redundancies Class can be made sealed - Private accessibility DO_NOT_SHOW
MemberCanBeInternal Code Redundancies Member or type can be made internal DO_NOT_SHOW
ConvertIfStatementToReturnStatement Code Redundancies ‘if-return’ statement can be re-written as ‘return’ statement HINT
ConvertIfStatementToSwitchStatement Code Redundancies Convert ‘if’ statement to ’switch’ statement HINT
ConvertToAutoPropertyWithPrivateSetter Code Redundancies Convert property to auto-property with private setter HINT
InvertIf Code Redundancies Invert ‘if’ statement to reduce nesting HINT
SimilarAnonymousTypeNearby Code Redundancies Similar anonymous type detected nearby HINT
SuggestUseVarKeywordEverywhere Code Redundancies Use ‘var’ keyword when possible HINT
ClassNeverInstantiated.Global Code Redundancies Class is never instantiated - Non-private accessibility SUGGESTION
ClassNeverInstantiated.Local Code Redundancies Class is never instantiated - Private accessibility SUGGESTION
ClassWithVirtualMembersNeverInherited.Global Code Redundancies Class with virtual members never inherited - Non-private accessibility SUGGESTION
ClassWithVirtualMembersNeverInherited.Local Code Redundancies Class with virtual members never inherited - Private accessibility SUGGESTION
ConverClosureToMethodGroup Code Redundancies Convert anonymous method to method group SUGGESTION
ConvertConditionalTernaryToNullCoalescing Code Redundancies ‘?:’ expression can be re-written as ‘??’ expression SUGGESTION
ConvertIfStatementToConditionalTernaryExpression Code Redundancies ‘if’ statement can be re-written as ‘?:’ expression SUGGESTION
ConvertIfStatementToNullCoalescingExpression Code Redundancies ‘if’ statement can be re-written as ‘??’ expression SUGGESTION
ConvertNullableToShortForm Code Redundancies Convert ‘Nullable‘ to ‘T?’ SUGGESTION
ConvertToAutoProperty Code Redundancies Convert property to auto-property SUGGESTION
ConvertToConstant.Global Code Redundancies Convert local variable or field to constant - Non-private accessibility SUGGESTION
ConvertToConstant.Local Code Redundancies Convert local variable or field to constant - Private accessibility SUGGESTION
ConvertToLambdaExpression Code Redundancies Convert to lambda expression SUGGESTION
ConvertToStaticClass Code Redundancies Convert to static class SUGGESTION
EventNeverInvoked.Global Potential Errors & Bad Practices Abstract or virtual event is never invoked SUGGESTION
EventNeverSubscribedTo.Global Code Redundancies Event is never subscribed to - Non-private accessibility SUGGESTION
EventNeverSubscribedTo.Local Code Redundancies Event is never subscribed to - Private accessibility SUGGESTION
FieldCanBeMadeReadOnly.Global Code Redundancies Field can be made readonly - Non-private accessibility SUGGESTION
FieldCanBeMadeReadOnly.Local Code Redundancies Field can be made readonly - Private accessibility SUGGESTION
InvokeAsExtensionMethod Code Redundancies Convert static method invocation to extension method call SUGGESTION
JoinDeclarationAndInitializer Code Redundancies Join local variable declaration and assignment SUGGESTION
MemberCanBeMadeStatic.Global Code Redundancies Member can be made static - Non-private accessibility SUGGESTION
MemberCanBeMadeStatic.Local Code Redundancies Member can be made static - Private accessibility SUGGESTION
MemberCanBePrivate.Global Code Redundancies Member can be made private - Non-private accessibility SUGGESTION
MemberCanBePrivate.Local Code Redundancies Member can be made private - Private accessibility SUGGESTION
MemberCanBeProtected.Global Code Redundancies Member can be made protected - Non-private accessibility SUGGESTION
MemberCanBeProtected.Local Code Redundancies Member can be made protected - Private accessibility SUGGESTION
MoreSpecificForeachVariableTypeAvailable Code Redundancies Iteration variable can be declared with a more specific type SUGGESTION
PublicConstructorInAbstractClass Code Redundancies Make constructor in abstract class protected SUGGESTION
ReplaceWithStringIsNullOrEmpty Code Redundancies Use ‘String.IsNullOrEmpty’ SUGGESTION
SuggestBaseTypeForParameter Code Redundancies Parameter can be declared with base type SUGGESTION
SuggestUseVarKeywordEvident Code Redundancies Use ‘var’ keyword when initializer explicitly declares type SUGGESTION
SuspiciousTypeConversion.Global Potential Errors & Bad Practices Suspicious type conversion or check SUGGESTION
TooWideLocalVariableScope Code Redundancies Local variable has too wide declaration scope SUGGESTION
UnaccessedField.Global Code Redundancies Unaccessed field - Non-private accessibility SUGGESTION
UnassignedField.Global Potential Errors & Bad Practices Unassigned field SUGGESTION
UnusedMember.Global Code Redundancies Unused declaration - Non-private accessibility SUGGESTION
UnusedMethodReturnValue.Global Code Redundancies Method return value is never used - Non-private accessibility SUGGESTION
UnusedParameter.Global Code Redundancies Unused parameter - Non-private accessibility SUGGESTION
UseObjectOrCollectionInitializer Code Redundancies Use object or collection initializer when possible SUGGESTION
VirtualMemberNeverOverriden.Global Code Redundancies Virtual member is never overriden - Non-private accessibility SUGGESTION
VirtualMemberNeverOverriden.Local Code Redundancies Virtual member is never overriden - Private accessibility SUGGESTION
AccessToModifiedClosure Potential Errors & Bad Practices Access to modified closure WARNING
AccessToStaticMemberViaDerivedType Potential Errors & Bad Practices Access to a static member of a type via a derived type WARNING
AssignNullToNotNullAttribute Value Analysis Possible ‘null’ assignment to entity marked with ‘Value cannot be null’ attribute WARNING
BaseMemberHasParams Potential Errors & Bad Practices Base member has ‘params’ parameter, but overrider hasn’t WARNING
BitwiseOperatorOnEnumWihtoutFlags Potential Errors & Bad Practices Bitwise operation on enum which is not marked by [Flags] attribute WARNING
CannotApplyEqualityOperatorToType Potential Errors & Bad Practices Compare with ‘==’ types marked by ‘CannotApplyEqualityOperatorAttribute’ WARNING
CharImplicitlyConvertedToNumeric Potential Errors & Bad Practices Char is implicitly converted to integral numeric type WARNING
CompareNonConstrainedGenericWithNull Potential Errors & Bad Practices Possible compare of value type with ‘null’ WARNING
ConditionIsAlwaysTrueOrFalse Value Analysis Expression is always ‘true’ or always ‘false’ WARNING
ConstantNullColescingCondition Value Analysis ‘??’ condition is known to be null or not null WARNING
ConstructorInitializerLoop Potential Errors & Bad Practices Possible cyclic constructor call WARNING
DoNotCallOverridableMethodsInConstructor Potential Errors & Bad Practices Virtual member call in constructor WARNING
DoubleNegationOperator Code Redundancies Double negation operator WARNING
EmptyConstructor Code Redundancies Empty constructor WARNING
EmptyDestructor Code Redundancies Empty destructor WARNING
EmptyGeneralCatchClause Potential Errors & Bad Practices Empty general catch clause WARNING
EmptyNamespace Code Redundancies Empty namespace declaration WARNING
EnumUnderlyingTypeIsInt Code Redundancies Underlying type of enum is ‘int’ WARNING
FormatStringProblem Potential Errors & Bad Practices Problems in format string WARNING
ForStatementConditionIsTrue Code Redundancies ‘true’ is redundant as ‘for’-statement condition WARNING
FunctionNeverReturns Potential Errors & Bad Practices Function never returns WARNING
InconsistentNaming Potential Errors & Bad Practices Inconsistent Naming WARNING
LocalVariableHidesMember Potential Errors & Bad Practices Local variable hides member WARNING
ParameterHidesMember Potential Errors & Bad Practices Parameter hides member WARNING
PartialMethodWithSinglePart Code Redundancies Redundant ‘partial’ modifier on method declaration WARNING
PartialTypeWithSinglePart Code Redundancies Redundant ‘partial’ modifier on type declaration WARNING
PossibleIntendedRethrow Potential Errors & Bad Practices Exception rethrow possibly intended WARNING
PossibleInterfaceMemberAmbiguity Potential Errors & Bad Practices Possible ambiguity while accessing member by interface WARNING
PossibleLossOfFraction Potential Errors & Bad Practices Possible loss of fraction WARNING
PossibleNullReferenceException Value Analysis Possible ‘System.NullReferenceException’ WARNING
RedundantAnonymousTypePropertyName Code Redundancies Redundant anonymous type property explicit name WARNING
RedundantAssignment Code Redundancies Assignment is not used WARNING
RedundantAttributeParentheses Code Redundancies Parentheses are redundant if attribute has no arguments WARNING
RedundantBaseConstructorCall Code Redundancies Redundant base constructor call WARNING
RedundantBaseQualifier Code Redundancies Redundant ‘base.’ qualifier WARNING
RedundantBoolCompare Code Redundancies Redundant boolean comparison WARNING
RedundantCaseLabel Code Redundancies Redundant ‘case’ label WARNING
RedundantCast Code Redundancies Redundant cast WARNING
RedundantCatchClause Code Redundancies Redundant catch clause WARNING
RedundantCollectionInitializerElementBraces Code Redundancies Redundant braces in collection initializer WARNING
RedundantDefaultFieldInitializer Code Redundancies Redundant field initializer WARNING
RedundantDelegateCreation Code Redundancies Explicit delegate creation expression is redundant WARNING
RedundantEmptyFinallyBlock Code Redundancies Redundant empty finally block WARNING
RedundantEmptyObjectCreationArgumentList Code Redundancies Redundant empty argument list on object creation expression WARNING
RedundantEmptyObjectOrCollectionInitializer Code Redundancies Redundant empty object or collection initializer WARNING
RedundantExplicitArrayCreation Code Redundancies Redundant explicit type in array creation WARNING
RedundantExplicitArraySize Code Redundancies Redundant explicit size specification in array creation WARNING
RedundantExplicitNullableCreation Code Redundancies Redundant explicit nullable type creation WARNING
RedundantExtendsListEntry Code Redundancies Redundant class or interface specification in base types list WARNING
RedundantIfElseBlock Code Redundancies Redundant ‘else’ keyword WARNING
RedundantLambdaParameterType Code Redundancies Redundant lambda parameter explicit type specification WARNING
RedundantLambdaSignatureParentheses Code Redundancies Redundant lambda signature parentheses WARNING
RedundantNameQualifier Code Redundancies Redundant name qualifier WARNING
RedundantOverridenMember Code Redundancies Redundant member override WARNING
RedundantParams Code Redundancies ‘params’ modifier is always ignored on overrides WARNING
RedundantStringToCharArrayCall Code Redundancies Redundant ’string.ToCharArray()’ call WARNING
RedundantThisQualifier Code Redundancies Redundant ‘this.’ qualifier WARNING
RedundantToStringCall Code Redundancies Redundant ‘object.ToString()’ call WARNING
RedundantTypeArgumentsOfMethod Code Redundancies Redundant type arguments of method WARNING
RedundantUnsafeContext Code Redundancies Unsafe context declaration is redundant WARNING
RedundantUsingDirective Code Redundancies Redundant using directive WARNING
ReferenceEqualsWithValueType Potential Errors & Bad Practices ‘Object.ReferenceEquals’ is always false because it is called with value type WARNING
RequiredBaseTypesConflict Potential Errors & Bad Practices Required base type conflicting another type WARNING
RequiredBaseTypesDirectConflict Potential Errors & Bad Practices Type specified in BaseTypeRequired attribute conflicts another type WARNING
RequiredBaseTypesIsNotInherited Potential Errors & Bad Practices Base type is required WARNING
SealedMemberInSealedClass Code Redundancies Sealed member in sealed class WARNING
UnaccessedField.Local Code Redundancies Unaccessed field - Private accessibility WARNING
UnsupportedRequiredBaseType Potential Errors & Bad Practices BaseTypeRequired attribute supports only classes and interfaces WARNING
UnusedAnonymousMethodSignature Code Redundancies Anonymous method signature is not necessary WARNING
UnusedAutoPropertyAccessor.Global Potential Errors & Bad Practices Auto-implemented property accessor is never used - Non-private accessibility WARNING
UnusedAutoPropertyAccessor.Local Potential Errors & Bad Practices Auto-implemented property accessor is never used - Private accessibility WARNING
UnusedMember.Local Code Redundancies “”Unused declaration - Private accessibility WARNING
UnusedMethodReturnValue.Local Code Redundancies Method return value is never used - Private accessibility WARNING
UnusedParameter.Local Code Redundancies Unused parameter - Private accessibility WARNING
UnusedTypeParameter Code Redundancies Unused type parameter WARNING
ValueParameterNotUsed Potential Errors & Bad Practices ‘value’ parameter is not used WARNING

Apologies for the formatting; you can also download the CSV.

posted by Nick at 6:54 pm - filed in .net  

Tuesday, October 20, 2009

Named Reader Writer Lock in C#

A while ago, I found an article about implementing a named lock class in C#, allowing for the following code:

private static NamedLock<string> namedLock = new NamedLock</string><string>();
public void DoSomethingConcurrent(string lockName)
{
    using (namedLock.Lock(lockName))
    {
       //Do something concurrent
    }
}
</string>

This is useful because it allows the same code block to be run concurrently, with locking occurring depending on the arguments being passed in. The author thought it might be useful to have some reader-writer type functionality added in too though; since I had a similar requirement, I wrote a named reader-writer lock, based partly on the NamedLock posted above but using a ReaderWriterLockSlim as the underlying lock rather than a simple Monitor.

I’ve posted the code to Github, including a unit test suite. The most interesting file is the class itself. Usage is almost identical to the NamedLock:

private static NamedReaderWriterLock<string> namedRWLock = new NamedReaderWriterLock</string><string>();
public void DoSomethingConcurrent(string lockName)
{
    using (namedRWLock.LockRead(lockName))
    {
       //Do something concurrent that only requires 
       //read access to the resource
    }
    using (namedRWLock.LockUpgradeableRead(lockName))
    {
       //Do something concurrent that only requires 
       //read access to the resource, but that may require
       //upgrading to a Write lock later in the code
    }
    using (namedRWLock.LockWrite(lockName))
    {
       //Do something concurrent that requires 
       //write access to the resource
    }
}
</string>

Performance-wise, the reader-writer functionality does seem to improve concurrent access over the named lock; here’s the output of the performance tests running on my machine:

NameLock_vs_NamedReaderWriterLockSlim_PerformanceTest_Balanced
 
Run 1 - finished NamedRWLock run in 3734.2794ms
Run 1 - finished NamedLock run in 5499.8592ms
Run 1 - finished Monitor run in 5468.61ms
Run 2 - finished NamedRWLock run in 3609.2826ms
Run 2 - finished NamedLock run in 5468.61ms
Run 2 - finished Monitor run in 5468.61ms
Run 3 - finished NamedRWLock run in 3609.2826ms
Run 3 - finished NamedLock run in 5468.61ms
Run 3 - finished Monitor run in 5515.4838ms
 
3 passed, 0 failed, 0 skipped, took 47.92 seconds.
 
NameLock_vs_NamedReaderWriterLockSlim_PerformanceTest_SkewedRead
 
Run 1 - finished NamedRWLock run in 6656.0796ms
Run 1 - finished NamedLock run in 8843.5236ms
Run 1 - finished Monitor run in 8812.2744ms
Run 2 - finished NamedRWLock run in 6562.332ms
Run 2 - finished NamedLock run in 8812.2744ms
Run 2 - finished Monitor run in 8812.2744ms
Run 3 - finished NamedRWLock run in 6562.332ms
Run 3 - finished NamedLock run in 8812.2744ms
Run 3 - finished Monitor run in 8812.2744ms
 
3 passed, 0 failed, 0 skipped, took 76.59 seconds.

These tests compare the performance of the NamedReaderWriterLock with the NamedLock, using a standard Monitor as a control value. The first set of tests balances reads and writes, whereas the second performs four times more reads than writes (and likely reflects real-life scenarios more closely.)

The NamedReaderWriterLock provided around 35% improvement over the NamedLock in the test where as many writes as reads are performed, and a 25% improvement in the test skewed towards reads. This is a reasonably figure, although in the greater scheme of things it’s likely to be a only a minor improvement compared to the cost of, say, making a DB call; still, it’s worth having.

The code is available under the BSD license.

posted by Nick at 10:17 pm - filed in .net  

Sunday, July 27, 2008

WSE 2.0 in Visual Studio 2008

A slightly older .Net 2 application I wrote for a client to upload files to Amazon S3 makes use of the Amazon S3 SOAP API. The SOAP API has the advantage over the REST API of being able to stream large files as DIME attachements when uploading to the S3 servers, rather than trying to load them up in one go, which is handy when uploading large files. The S3 WS API requires Microsoft’s Web Services Enhancements (WSE) 2.0 though, rather than the later WSE 3.0, because in WSE 3.0 DIME is replaced by MTOM (which Amazon doesn’t support - in typical WS-* way).

However, when I tried to install this, I got the following error:

Unknown error in CorBindToRuntimeHost

Error 1001.InstallUtilLib.dll: Unknown error in CorBindToRuntimeHost

The workaround (a reminder to myself) is to use the WSE 2.0 SP3 installer rather than the older versions that come up when searching for WSE 2.0 on google.

posted by Nick at 6:24 pm - filed in .net, windows  

Friday, July 20, 2007

Strongly-named assemblies and InternalsVisibleTo

One of the projects I work on wasn’t using strongly-named assemblies, so we decided to sign them (mainly to prevent version conflicts on sales laptops). However, after generating the key-pair and setting up the different assemblies to use it, building the solution gave this error:

Friend assembly reference ‘Company.Product.UnitTests’ is invalid. Strong-name signed assemblies must specify a public key token in their InternalsVisibleTo declarations.

The problem was that some of the assemblies were making their internals visible to the UnitTests assembly (making them accessible so that they could be tested), but when these assemblies are strongly names, the public key token must also be included in the InternalsVisibleTo declaration in Assembly.cs.

This introduces a chicken-and-egg scenario; the A assembly makes its internals visible to the UnitTests assembly, but must know the UnitTests assembly public key in order to build. The UnitTests assembly depends on assembly A to build, but assembly A can’t be built until we know what the public key token for UnitTests is, which in turn can’t build because it depends on assembly A…

After looking into a couple of solutions, the one that worked for me was:

  1. Sign all assemblies with the key-pair
  2. Generate the public key for the key-pair as such (from the Visual Studio command prompt):

    sn -p path\to\keypair.snk path\to\keypair.pub

  3. Get the public key token for the public key as such (still from the Visual Studio command prompt):

    sn -tp path\to\keypair.pub

    This will output a long (256 characters) public key; copy this to the clipboard

  4. Declare the InternalsVisibleTo attribute in Assembly.cs as such:

    [assembly: InternalsVisibleTo("Company.Product.UnitTests, PublicKey=<your public key>")]

    where <your public key> is replaced by the public key output in (3)

  5. Build the solution. The solution should now build as expected.

This assumes that all assemblies use the same key-pair - suggestions to use the public key token (using PublicKeyToken=... rather than PublicKey=...) didn’t work for me.

posted by Nick at 5:58 pm - filed in .net  

Tuesday, May 8, 2007

Problem Installing .Net Framework 3.0

The .Net Framework 3.0 refused to install on my machine this morning - a quick look through the installation logs (particularly C:\DOCUME~1\<your user name>\LOCALS~1\Temp\VSW1\VSSWMSIFailInfo.txt) revealed the following:

Error 1402.Could not open key:
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/Eventlog/Security/ServiceModel 3.0.0.0.
System error 5. Verify that you have sufficient access to that key,
or contact your support personnel.

I am a local admin on my machine, so this surprised me a little. The permissions on this key were set to grant Full Control to the ASP.NET user. I granted myself Full Control, but this didn’t seem to help. Granting Everyone Full Control seemed to resolve the issue - other keys in the subtree grant Full Control to the local Administrator group and the System user, so I revoked permissions from Everyone and granted these access to the key once the installation was complete (ah, the power…).

posted by Nick at 6:55 pm - filed in .net  

Friday, May 4, 2007

Using WatiN to automate accessibility testing in .Net

WatiN is a web testing framework for .Net - it is a C# port of Watir, which is in Ruby. Both function by driving Internet Explorer and are fully scriptable, which means you can do things like:

[Test]
public void SearchForWatiNOnGoogle()
{
    using (IE ie = new IE("https://www.google.com"))
    {
        ie.TextField(Find.ByName("q")).TypeText("WatiN");
        ie.Button(Find.ByName("btnG")).Click();
        Assert.IsTrue(ie.ContainsText("WatiN"));
    }
}

This is from the WatiN documentation page, which is pretty helpful - as this shows, it’s easy to get started, and writing basic tests is straightforward (although perhaps not as fast as the Ruby version). WatiN also integrates well with NUnit and other testing frameworks (including TestDriven.Net and the Visual Studio 2005 test runner). I’ve been using this at work to run some basic accessibility tests on some of our web-based products; although accessibility testing can’t be completely automated, it’s pretty handy to be able to perform repetitive HTML inspections automatically. This checks that all <img> tags on a page have the required “alt” attribute (required in the WCAG 1.0 guidelines on images):

[Test]
public void CheckAllImagesHaveAltAttributes()
{
    using (IE ie = new IE("https://localhost/test.aspx"))
    {
        foreach (Image img in ie.Images)
            if (img.Alt == null || img.Alt.Trim().Length == 0)
                Assert.Fail(
                    String.Format("The image with id '{0}' "
                    + "and src '{1}' doesn't have a alt tag "
                    + "in '{2}'",
                    img.Id, img.Src, ie.Url)
                );
    }
}

This checks that all links on a page have a “title” attribute (an overly strict interpretation of the WCAG 1.0 guidelines on links):

[Test]
public void CheckAllLinksHaveTitleAttributes()
{
    using (IE ie = new IE("https://localhost/test.aspx"))
    {
        foreach (Link link in ie.Links)
            if (link.Title == null || link.Title.Trim().Length == 0)
                Assert.Fail(
                    String.Format("The link with id '{0}' "
                    + "and text '{1}' doesn't have a title tag "
                    + "in '{2}'",
                    link.Id, link.InnerHtml, ie.Url)
                );
    }
}

Or that all input fields in a form have an associated “label” tag (WCAG 1.0 guidelines on form labels):

delegate void TestInputFor(IE ie, Element inputField);
 
[Test]
public void CheckAllFormFieldsHaveLabels()
{
    using (IE ie = new IE("https://localhost/test.aspx"))
    {
        TestInputFor assertElementHasLabel =
            delegate(IE ieInstance, Element inputField)
        {
            //check whether the corresponding label exists.
            Debug.WriteLine("Checking whether element "
                + inputField.Id + " has a label");
            int cnt = 0;
            foreach (Label lbl in ieInstance.Labels)
                if (lbl.For.Equals(inputField.Id))
                    cnt++;
            //assert the label was found
            Assert.AreEqual(1, cnt,
                String.Format("Expected a label for text field"
                + " with ID {0}, but found {1:d} in page '{2}'",
                inputField.Id, cnt, ieInstance.Url));
        };
 
        foreach (CheckBox cb in ie.CheckBoxes)
        {
            assertElementHasLabel(ie, cb);
        }
 
        foreach (TextField tf in ie.TextFields)
        {
            if(tf.GetAttributeValue("type").Equals("hidden"))
                continue;
            assertElementHasLabel(ie, tf);
        }
 
        foreach (SelectList sl in ie.SelectLists)
        {
            assertElementHasLabel(ie, sl);
        }
 
        foreach (RadioButton rb in ie.RadioButtons)
        {
            assertElementHasLabel(ie, rb);
        }
    }
}

These are rather basic tests; and they are far from extensive. However, they should give a good idea how much time can be saved by automated testing, especially in the context of standard systems which get customised on a per-client basis (e.g. a content management system for which the layout is customised on a per-client basis; the tests are written once and can be run against each client’s version).

posted by Nick at 8:00 pm - filed in .net, accessibility, testing  

Thursday, October 5, 2006

String Formatting in C#

String formatting is handy to save on both code and garbage collection - it saves on code because it handles formatting including localisation in a way which would be tedious to reproduce manually, and on garbage collection because it uses a StringBuilder internally to perform its magic. This is a quick reference for the different types of formatting offered by the String.Format() method.

Basics

String formatting occurs via character subsitution: control characters in the string to be formatted are replaced with the arguments you provide it as such:

String.Format("{0} says hello", "John");

-> "John says hello"

Here, {0} is substituted with the argument provided - you can pass up to three arguments or an argument array:

String.Format("{0} from {1} says hello to {2}", "John", "Sheffield", "Jane");
 
String.Format("{0} from {1} says hello to {2} from {3}", new object[]{"John", "Sheffield", "Jane", "Hull"});

The parameters to be substituted (e.g. {0}) are formatted as such:

{<index>[,<alignement>][:<format specifier>]}

Where:

  • index is the index of the object in the argument array to be used in the substitution
  • padding determines whether the substituted string should be padded to be a certain length
  • the format specifier allows the use of predefined formatting for the parameter

The .Net Framework provides standard specifiers depending on the type of the argument, as well as the ability to perform custom formatting for numeric and DateTime data types. The following specifiers can be used:

Numerals - Standard Formatting

The following standard specifiers are available for formatting numbers:

Specifier Name Syntax Output
(using double 9.8765)
Output
(using int -98765)
c (or C) Currency {0:c} £9.88 -£98,765.00
d (or D) Decimal
(whole number)
{0:d} System.FormatException -98765
e (or E) Exponent {0:e} 9.876500e+000 -9.876500e+004
f (or F) Fixed point {0:f} 9.88 -98765.00
g (or G) General {0:g} 9.8765 -98765
n (or N) Number {0:n} 9.88 -98,765.00
r (or R) Round trippable {0:r} 9.8765 System.FormatException
x (or X) Hexadecimal {0:x} System.FormatException fffe7e33

The currency specifiers is culture-dependent, and output will depend on the Locale of the current thread - the example outputs come from my box running with the “en-UK” locale, but would be different when run on another locale.

Numerals - Custom Formatting

In addition to the standard date specifiers, the following specifiers can be used for custom number formatting:

Specifier Name Syntax Output
(using double 9.8765)
Output
(using int -98765)
0 Zero Placeholder {0:00.0000} 09.8765 -98765.0000
# Digit Placeholder {0:##.##} 9.88 -98765
. Decimal Point Separator {0:#.#} 9.9 -98765
, Thousands Separator {0:#,#} 10 -98,765
% Percentage {0:#%} 988% -9876500%
e (or E) Exponent Placeholder {0:00e+000} 99e-001 -99e+003
; Group Separator {0:#,##0.00;(#,##0.00);Zero} 9.88 (98,765.00)

Dates - Standard Formatting

The following standard specifiers are available for formatting dates:

Specifier Name Syntax Output
(using June 6th, 2000, at 00:00:01)
d Short Date {0:d} 01/06/2000
D Long Date {0:D} 01 June 2000
t Short Time {0:t} 00:00
T Long Time {0:T} 00:00:01
f Full date and time (short time) {0:f} 01 June 2000 00:00
F Full date and time (long time) {0:F} 01 June 2000 00:00:01
g Default date and time (short time) {0:g} 01/06/2000 00:00
G Default date and time (long time) {0:G} 01/06/2000 00:00:01
M Day and Month {0:M} 01 June
r RFC1123 date string {0:r} Sat, 01 Jun 2000 00:00:01 GMT
s Sortable date and time (ISO 8601) {0:s} 2000-06-01T00:00:01
u Universal, local time {0:u} 2000-06-01 00:00:01Z
U Universal, UTC time {0:U} 31 May 2000 23:00:01
Y or y Month and Year {0:Y} June 2000

Dates - Custom Formatting

In addition to the standard date specifiers, the following specifiers can be used for custom date formatting:

Specifier Name Syntax Output
(using June 6th, 2000, at 00:00:01)
dd Day Number {0:dd} 01
ddd Short Day Name {0:ddd} Sat
dddd Full Day Name {0:dddd} Saturday
f, ff, fff etc… Second fractions {0:fff} 000
gg Era {0:gg} A.D.
hh 2 digit hour (12 hour) {0:hh} 00
HH 2 digit hour (24 hour) {0:HH} 00
mm 2 digit minute {0:mm} 00
MM Month Number {0:MM} 06
MMM Short Month Name {0:MMM} Jun
MMMM Full Month Name {0:MMMM} June
ss Seconds {0:ss} 01
tt AM/PM {0:tt} AM
yy 2 digit year {0:yy} 00
yyyy 4 digit year {0:yyyy} 2000
zz Short Timezone Offset {0:zz} +01
zzz Full Timezone Offset {0:zzz} +01:00
: Separator {0:hh:mm:ss} 00:00:01
/ Separator {0:dd/MM/yyyy} 01/06/2000

All of these specifiers are culture-dependent (apart from the Universal specifiers, which are universal and therefore not culture-dependent), and will depend on the Locale of the current thread - the example outputs come from my box running with the “en-UK” locale, but would be different when run on another locale.

Enumerators

The following standard specifiers are available for formatting Enums:

Specifier Name Syntax Output
(using DateTimeKind.Local)
g General (Flag if present, Decimal otherwise) {0:g} Local
f Flag (string) {0:f} Local
d Decimal {0:d} 2
x Hexadecimal {0:z} 00000002

Strings

The only formatting available to Strings is the padding option, which can be positive or negative depending on whether the string should be left- or right-aligned:

String.Format("'{0,10}'", "John");

-> ' John'

String.Format("'{0,-10}'", "John");

-> 'John '

Culture

As mentionned previously, the currency and date formats are culture-dependent. This means that the output of the formatted string will depend on what locale is made available to the method. By default, the String.Format method will use the locale of the current thread as a basis for formatting; the current thread locale can be set as such:

//set the culture in the current thread to French
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
String.Format("{0:c}", 22.45);

-> 22,45 €
String.Format is also overloaded to accept a CultureInfo argument, which can be used to format a DateTime or currency based on a specific locale:

//Format a currency using French currency formatting
String.Format("{0:c}", 22.45, new CultureInfo("fr-FR"));

-> 22,45 €

Sample Code

Sample code demonstrating all of the specifiers mentionned above and setting the culture for the current thread:
C# String Formatting - Sample Code

This code can be compiled using the C# compiler and run from the command line. Using the command prompt (and assuming the code has been saved to C:\StringFormatExample.cs):

C:>SET PATH=%PATH%;%WINDIR%\Microsoft.NET\Framework\v2.0.50727;
C:>csc StringFormatExample.cs
Microsoft (R) Visual C# 2005 Compiler version 8.00.50727.42
for Microsoft (R) Windows (R) 2005 Framework version 2.0.50727
Copyright (C) Microsoft Corporation 2001-2005. All rights reserved.

C:>StringFormatExample.exe fr-FR

Reference

There is actually quite a reasonable amount of reference material available - I only found the other two blog posts after writing this though :-/ (I decided to post anyways)

MSDN docs for standard number formatting

MSDN docs for custom number formatting

MSDN docs for standard date formatting

MSDN docs for custom date formatting

https://blog.stevex.net/index.php/string-formatting-in-csharp/

https://idunno.org/archive/2004/14/01/122.aspx

posted by Nick at 6:51 pm - filed in .net