F#, an Ideal Language For Writing .NET Unit Tests

by Marc Sigrist 8. February 2015 20:35

In this post, we are going to test the escaped concact/split functions that we implemented last time. Along the way, it will become apparent why F# is an ideal language for writing .NET unit tests. These are the signatures of the two F# functions to be tested:

val concatEscape : esc:char -> sep:string -> strings:seq<string> -> string
val splitUnescape : esc:char -> sep:string -> string:string -> seq<string>

Seen from C# (e.g., in Visual Studio's object browser), the signatures would look like this:

public static string concatEscape(char esc, string sep, IEnumerable<string> strings);
public static IEnumerable<string> splitUnescape(char esc, string sep, string @string);

We will use Visual Studio's built-in Test Explorer to visualize the results, which will look like this:

Test Explorer results

Step 1: Prepare the Test Project

Create a new F# class library project named e.g. MyCompany.Foundation.Tests, and add the following NuGet packages:

 

Step 2: Define Custom Operators to Further Simplify Testing

Add a new F# source code file called StringTests.fs to the project, with the following code:

namespace MyCompany.Foundation.Tests

open System
open NUnit.Framework
open FsUnit.TopLevelOperators
open MyCompany.Foundation.Core

[<AutoOpen>]
module Operators =
    let throws<'T when 'T :> exn> : (unit -> unit) -> unit = should throw typeof<'T>

    /// Asserts equality of two instances of the same type.
    let (.=) (actual:'T) (expected:'T) = should equal expected actual 
    

throws is a supporting function who tests whether a given function throws a certain type of exception. The .= operator enables a very simple syntax two test if two values of the same type are equal, e.g. x .= y.

Step 3: Begin a Module for the Tests

In the StringTests.fs file, add the following code:

module StringTests =

That's all you need to do to create the test fixture class required by NUnit. It is not necessary to define a full-blown class, and it is not necessary to add a [<TestFixture>] attribute.

Step 4: Implement reusable testing functionality

concatEscape and splitUnescape both require an escape character and a separator string as arguments, which must be validated in the same way. To avoid repetitive code, we define a function testSeparatorValidationOf who

  • takes another function f as parameter,
  • calls f with several combinations of escape character and separator string,
  • tests whether f correctly validates the escape character and separator string.

 

    let private testSeparatorValidationOf f =
        let test sep = f '!' sep

        // Separator must not be null
        (fun _ -> test null |> ignore) |> throws<ArgumentNullException>
        
        // Separator must not be empty
        (fun _ -> test String.Empty |> ignore) |> throws<ArgumentException>

        // Separator must not have same beginning and ending
        for sep in ["xx"; "xax"; "xabx"; "xyxy"; "xyaxy"; "xyabxy"
                    "xyzqwertyabcxyzqwerty"] do
            (fun _ -> test sep |> ignore) |> throws<ArgumentException>

        // Separator must not start with escape character
        (fun _ -> test "!; " |> ignore) |> throws<ArgumentException>

Note that we did not have to write any type annotations. For instance, the F# compiler automatically inferred that the parameter f must be of type char -> string -> 'a, i.e., a function who takes a character parameter and a string parameter and returns a value of generic type. In C#, to achieve something similar, we would have to write:

    static a testSeparatorValidationOf<a>(Func<char, string, a> f){
        Func<string, a> test = sep => f('!', sep);
    
        // Etc. ...
    }

Step 5: Implement Test Functions

The first two tests check whether concatEscape and splitUnescape validate arguments correctly:

    [<Test>]
    let ``concatEscape validates parameters correctly``() = 
        // Test validation of separator
        testSeparatorValidationOf
        <| fun esc sep -> String.concatEscape esc sep ["asdf"; "qwer"; "uiop"]

        // Test validation of input string sequence (must not be null)
        (fun _ -> String.concatEscape '!' "; " null |> ignore) 
        |> throws<ArgumentNullException>

    [<Test>]
    let ``splitUnescape validates parameters correctly``() = 
        // Test validation of separator
        testSeparatorValidationOf 
        <| fun esc sep -> String.splitUnescape esc sep "asdfqwpoiqwe"

        // Test validation of source string (must not be null)
        (fun _ -> String.splitUnescape '!' "; " null |> ignore) 
        |> throws<ArgumentNullException>

Note how F# allows using any kind of expression as identifier when you put it inside pairs of backtick marks, as in ``This :) is " a valid function name``.

The next test checks if concatEscape correctly handles cases where the whole sequence of elements is empty, or some elements are empty strings:

    [<Test>]
    let ``concatEscape handles missing or "" elements correctly``() =
        let concat = String.concatEscape '~' ";"
        let e, ab, cd = String.Empty, "ab", "cd"

        concat []          .= e
        concat [e]         .= e
        concat [e; e]      .= ";"
        concat [e; ab]     .= ";ab"
        concat [ab; e]     .= "ab;"
        concat [e; e; e]   .= ";;"
        concat [e; e; ab]  .= ";;ab"
        concat [e; ab; e]  .= ";ab;"
        concat [ab; e; e]  .= "ab;;"
        concat [e; ab; cd] .= ";ab;cd"
        concat [ab; e; cd] .= "ab;;cd"
        concat [ab; cd; e] .= "ab;cd;"

Note how we have defined the local concat function by partially applying the existing String.concatEscape function (calling it with only the first two of three possible arguments. This results in a new function who has the esc and sep arguments already built-in and expects only the remaining strings argument in its signature). Also note the usage of our custom-defined .= should equal operator. These F#-specific features allow us to test 12 different cases with minimum plumbing, but maximum expressiveness.

The next test checks whether concatEscape handles null strings correctly. This is needed because types who are defined by non-F# languages, such as System.String of mscorlib.dll, can be null. (Types who are defined in F#, even if they are reference types, by default cannot be null.)

    [<Test>]
    let ``concatEscape handles null elements like "" elements``() =
        let concat = String.concatEscape '~' ";"
        let e, ab, cd = String.Empty, "ab", "cd"

        concat [null]             .= concat [e]
        concat [null; null]       .= concat [e; e]
        concat [null; ab]         .= concat [e; ab]
        concat [ab; null]         .= concat [ab; e]
        concat [null; null; null] .= concat [e; e; e]
        concat [null; null; ab]   .= concat [e; e; ab]
        concat [null; ab; null]   .= concat [e; ab; e]
        concat [ab; null; null]   .= concat [ab; e; e]
        concat [null; ab; cd]     .= concat [e; ab; cd]
        concat [ab; null; cd]     .= concat [ab; e; cd]
        concat [ab; cd; null]     .= concat [ab; cd; e]

The following test checks whether concatEscape uses the escape character as specified in the requirements: The escape character should only escape itself (by duplication) if strictly necessary. Note, again, how F# allows to write 49 different test cases with minimum plumbing and maximum expressivity:

    [<Test>]
    let ``concatEscape uses escape character correctly``() =
        let concat = String.concatEscape '&' ";"
  
        // Escape single pre-existing separator
        concat [";"]       .= "&;"
        concat [";ab"]     .= "&;ab"
        concat ["ab;"]     .= "ab&;"
        concat ["ab;cd"]   .= "ab&;cd"
        concat [";"; ""]   .= "&;;"
        concat [";"; "ab"] .= "&;;ab"
        concat [""; ";"]   .= ";&;"
        concat ["ab"; ";"] .= "ab;&;"

        // Escape series of pre-existing separators
        concat [";;"]       .= "&;&;"
        concat [";;ab"]     .= "&;&;ab"
        concat ["ab;;"]     .= "ab&;&;"
        concat ["ab;;cd"]   .= "ab&;&;cd"
        concat [";;"; ""]   .= "&;&;;"
        concat [";;"; "ab"] .= "&;&;;ab"
        concat [""; ";;"]   .= ";&;&;"
        concat ["ab"; ";;"] .= "ab;&;&;"

        // Repeat pre-existing escape characters if followed by pre-existing separator
        // and then escape the pre-existing separator.
        concat ["&;"]          .= "&&&;"
        concat ["&;ab"]        .= "&&&;ab"
        concat ["ab&;"]        .= "ab&&&;"
        concat ["ab&;cd"]      .= "ab&&&;cd"
        concat ["&&;"]         .= "&&&&&;"
        concat ["&&;ab"]       .= "&&&&&;ab"
        concat ["ab&&;"]       .= "ab&&&&&;"
        concat ["ab&&;cd"]     .= "ab&&&&&;cd"
        concat ["&;"; ""]      .= "&&&;;"
        concat ["ab&;"; "cd"]  .= "ab&&&;;cd"
        concat ["&&;"; ""]     .= "&&&&&;;"
        concat ["ab&&;"; "cd"] .= "ab&&&&&;;cd"
        concat [""; "&;"]      .= ";&&&;"
        concat ["ab"; "&;cd"]  .= "ab;&&&;cd"
        concat [""; "&&;"]     .= ";&&&&&;"
        concat ["ab"; "&&;cd"] .= "ab;&&&&&;cd"

        // Repeat pre-existing escape characters if followed by "real" separator.
        concat ["&"; ""]      .= "&&;"
        concat ["ab&"; "cd"]  .= "ab&&;cd"
        concat ["&&"; ""]     .= "&&&&;"
        concat ["ab&&"; "cd"] .= "ab&&&&;cd"

        // Do not repeat pre-existing escape characters 
        // if not followed by pre-existing or real separator.
        concat ["&"]          .= "&"
        concat ["&ab"]        .= "&ab"
        concat ["ab&"]        .= "ab&"
        concat ["ab&cd"]      .= "ab&cd"
        concat ["&&"]         .= "&&"
        concat ["&&ab"]       .= "&&ab"
        concat ["ab&&"]       .= "ab&&"
        concat ["ab&&cd"]     .= "ab&&cd"
        concat [""; "&"]      .= ";&"
        concat ["ab"; "&cd"]  .= "ab;&cd"
        concat [""; "&&"]     .= ";&&"
        concat ["ab"; "&&cd"] .= "ab;&&cd"

        // Use escape character correctly, even if the escape character is a space,
        // and is contained in the separator (but not in first position).
        String.concatEscape ' ' "; "
            [""; "a"; ""; "b"; "c; "; "d"; "; e"; " f"; "g "; "h"; ";i"; "j;"; ""] .= 
            "; a; ; b; c ; ; d;  ; e;  f; g  ; h; ;i; j;; "

The next test checks whether splitUnescape reproduces the input of concatEscape as expected. Note how F#, as an expression-based language, allows us to initialize inputLists (a list of string lists) in a very straight-forward way, with all permutations needed for testing visible at one glance. Using only 30 lines of code (excluding white space and comments), we are testing the concatenation and splitting of 110 lists of input strings with 9 different separators = 990 cases overall:

    [<Test>]
    let ``splitUnescape reverses concatEscape correctly``() = 
        /// Input lists for concatEscape, with '^' as escape and "|" as separator.
        let inputLists = 
            /// 3 permutations are enough to cover all positions (start, inside, and end).
            let permutate3 x y z =
                [[x; y; z]; [x; z; y]; [y; x; z]; [y; z; x]; [z; x; y]; [z; y; x]]

            /// Permutate 3 strings and use 3 variants of the last string
            /// (alone, prefixed, or suffixed).
            let permutate3x3 x y z preZ postZ = 
                let perm a b c = permutate3 x y <| sprintf "%s%s%s" a b c
                List.collect id [perm z preZ postZ; perm preZ z postZ; perm preZ postZ z]

            [ // Singular items
              [ [""]; [null]; ["^"]; ["|"]; ["^|"]; ["a"] ]
              
              // Singular items at start/middle/end of string and/or list
              permutate3 "a" "bc" ""
              permutate3 "d" "ef" null
              permutate3x3 "g" "hi" "^" "jk" "lm"
              permutate3x3 "o" "pq" "^^" "rs" "tu"
              permutate3x3 "v" "wx" "^^^" "yz" "12"
              permutate3x3 "a" "bc" "|" "de" "ef"
              permutate3x3 "g" "hi" "^|" "jk" "lm"
              
              // Some advanced complex input lists
              [ [ "asbc"; ""; "ef"; ""; ""; "gh"; null; "i"; null; null; "kl" ]
                [ "op^q^^rs^^^tu^^^^vw^^^^^x"; "op|q||rs|||t"; "op^|q^|^|rs^|^|^|t" ] ] ]
            |> List.collect id

        let esc = '^'
        let seps = ["x"; "xy"; "x^"; "xxy"; "xx^"; "xyy"; "xy^"; "x^y"; "x^^"]

        for lst in inputLists do
            for sep in seps do
                // Replace "|" placeholder with current separator.
                let concatSource =
                    lst |> List.map (function null -> null | s -> s.Replace("|", sep)) 
                let concatResult = String.concatEscape esc sep concatSource

                // Split result should be like concat source, except for nulls, which are "".
                let expectedSplitResult =
                    concatSource |> List.map (function null -> String.Empty | x -> x)
                let actualSplitResult =
                    String.splitUnescape esc sep concatResult |> Seq.toList
                actualSplitResult .= expectedSplitResult

The following test checks whether we can correctly reproduce lists of lists of lists of input strings by

  • repeatedly concatenating the strings at each nesting level, resulting in a large single string, and then
  • repeatedly splitting back the strings at each level.

 

    [<Test>]
    let ``splitUnescape reverses nested concatEscape correctly``() = 
        let esc, sep = '&', ";"

        let nestedConcatSources = 
            [ [ ["asdf"; "&x; asf & asdf"; "qipwueriuy"]
                ["qwer;qwer&;qerpo"; ""; "qwer"]
                [""; "qiuq"]
                [""]
                [" qwu  ^qpoij&*qwoerui;"] ] 
              [ ["qwieoruy"; "qwopeiurj&&"] ]
              [ ["tyiqwe"; "a"]
                [""; "&^a;lsdkj&q ;  "; "qwer"]
                ["qoidu"; ""; "qwer&qq"] ] ]

        let concatResult = 
            let concat = String.concatEscape esc sep
            let mapConcat = List.map concat
            nestedConcatSources |> List.map mapConcat |> mapConcat |> concat

        let splitResult =
            let split = String.splitUnescape esc sep >> Seq.toList
            let mapSplit = List.map split
            concatResult |> split |> mapSplit |> List.map mapSplit

        splitResult .= nestedConcatSources

In the last line above, note how F#'s built-in structural equality for (nested) lists allowed us to verify that splitResult equals nestedConcatSources with just a single line of code.

 

As is typical for functional code, concatEscape/splitUnescape rely on recursion. An inportant feature of the F# compiler is the ability to optimize recursive code so that stack overflows cannot occur, as long as the recursive calls are in tail position. Our last test checks whether we can pass lists of large strings, as well as large lists of strings, to the concatEscape/splitUnescape functions, without producing a stack overflow.

 

    /// This test will only pass in release mode (when --optimize is set).
    [<Test; Timeout 3000>]
    let ``concatEscape and splitUnescape scale without stack overflow``() =
        let roundTripTest strings =
            let esc, split = '&', ";"
            let concatResult = String.concatEscape esc split strings
            // Causes stack overflow in debug mode (when --optimize is not set).
            let splitResult = String.splitUnescape esc split concatResult |> Seq.toArray
            splitResult .= strings

        // Avoid stack overflow with source strings of 10k length each
        let rep2K = String.replicate 2000
        roundTripTest [| rep2K "qwert"; rep2K "abcd&"; rep2K "ab;cd" |]

        // Avoid stack overflow with source list of 10k items
        roundTripTest [| for i in 1 .. 10000 -> sprintf "%ixg;s&;c" i |]

Conclusion

We have implemented a comprehensive battery of test cases to verify the correctness of the concatEscape and splitUnescape functions. Using the light syntax of the expression-based, functional-first language F#, it is possible to write type-safe unit tests much more succinctly and self-documentary than with C# or any other .NET language.

Tags:

C# | F#

F# for C# developers: Creating escaped concat/split functions in F#

by Marc Sigrist 22. December 2014 03:36

The built-in .NET Join and Split methods for strings do not provide a way for escaping the separator. If the separator is already contained in the input strings before joining, the strings cannot be reproduced by splitting:

open System
let inputStrings = [| "Hello, world."; "How are you?" |]
let separator = ", "
let joinedResult = String.Join(separator, inputStrings)
let reproducedStrings = joinedResult.Split([| separator |], StringSplitOptions.None)
printfn "%A" inputStrings
printfn "%A" reproducedStrings

The output of the above code is:

[|"Hello, world."; "How are you?"|]   // An array with 2 elements
[|"Hello"; "world."; "How are you?"|] // An array with 3 elements

To solve this problem, we are going to implement a pair of functions called concatEscape and splitUnescape. Besides roundtrip concatenation, the functions will provide some additional benefits.

For C# Developers...

In the example, I will explain a total of about 30 different F# concepts that do not exist in C# 6.0. In case F# is completely new to you, here are some important basics:

  • F# uses indentation instead of curly braces { } to define scoped blocks of code.
  • F# (almost never) uses a return keyword. It is an expression-based language, and so the last executed line of a body of code implicitly specifies the return value.
  • It is not necessary to separate function parameters or arguments by commas and wrap them in parentheses. When you see something like let myFunction x y = x + Y - 1, myFunction is the name of the function, and x and y are the names of parameters. Everything after the = is the body of the function.
  • You will see almost no type declarations in the code. Nevertheless, F# is a statically typed language. In some ways, it is even more "strongly" typed than C#. The difference to C# is that the F# compiler automatically infers the types for you, and also automatically makes them generic where appropriate. Again, this greatly simplifies the code, so that you can focus your thinking more on the business logic.

Some Preliminary Remarks

Please note that, by exposing F# features unavailable in C#, I am in no way trying to make the C# language appear unfavorable. I consider C# to be a great language, in which I programmed intensely for about eight years with much joy. However, some C# coders mistakenly believe that learning F# is of not of much use, because "C# will eventually have all the features of F# included anyway". Nothing could be further from the truth. While some of the (rather cosmetic) F# features can be introduced quite smoothly to C#, the more substantial features would be very hard, or almost impossible, to integrate in a sane way. The main reason for this is that C# was not designed from the ground up as an expression-based language, and does not include important concepts such as non-nullability and immutability at its core.

Unit tests will be shown separately in an upcoming blog post. While test-driven development dictates that tests should be written before implementation, this order would be impractical for blogging. Furthermore, implementation and unit testing have a different quality in F# compared to C#. In F#, one normally writes most critical code in a script file first, constantly evaluating and improving each couple of lines in the REPL. Finally, the finished code is copied from the script file to the source file, which usually contains very few or zero bugs.

Step 1: Define the Requirements

For concatenation,

  • process the input strings as a stream (do not load all elements into memory at once)
  • use only a minimum number of escapes, if any. E.g., if the escape character pre-exists in an input string, it should itself be escaped by duplication only in cases where it precedes a real separator or an escaped pre-existing separator.
  • if the separator is empty, raise an ArgumentException
  • in other respects, behave like the built-int .NET Join method:
    • If the separator is null, raise an ArgumentNullException.
    • If the input sequence is null, raise an ArgumentNullException.
    • If the input sequence is empty, treat it like one with a single empty-string element.
    • If the input sequence contains null elements, treat them like empty strings.

For splitting,

  • return the output strings as a stream (do not load all elements into memory at once)
  • accurately reproduce the original input strings when given the same escape character and separator, except for the following cases (which work the same way as the built-in .NET Split method):
    • if the original input sequence was empty, reproduce it as sequence with a single empty-string element
    • if the original input sequence contained null strings, reproduce them as empty strings

Step 2: Define a Namespace

1  namespace MyCompany.Foundation.Core
2  
3  open System
4  open System.Text

Step 3: Implement Supporting Functionality

We are going to need some supporting functions to validate the concat/split function parameters:

 5  [<RequireQualifiedAccess>]
 6  module Assert = 
 7      let notNull argName arg = if arg = null then nullArg argName
 8      
 9      let notNullOrEmpty argName arg = 
10          notNull argName arg
11          if Seq.isEmpty arg then invalidArg argName "Value cannot be empty."

The above code uses the following features not present in C# 6.0:

  • Lines 5–6: Enforcement of qualified access to a module. In C# 6.0, it is now possible to open a static class with the using keyword. In F#, modules are similar to static classes, but allow for much simplified syntax. In addition, the access to modules can be declared with [<AutoOpen>] (open by default) or [<RequireQualifiedAccess>] (non-openable).
  • Line 9: Automatic type inference and generalization. argName is compiled as a string parameter, because it is used to call the notNull function, which itself calls the nullArg function, which requires a string argument. arg is compiled as a parameter of generic type, which must implement the sequence interface, because it is used to call Seq.isEmpty (Seq<> is the same as IEnumerable<>). Furthermore, arg is compiled as a type who "can be null", because it is validated via the notNull function (types declared in F# can never be null, but types declared outside F#—such as String or IEnumerable<T>>— can still be null).

Step 4: Declare the Token Type

To simplify the parsing, we will convert the input strings to token streams and then analyze/decompose the tokens streams via F#'s built-in pattern matching facilities.

12  type private Token = 
13      | Esc of Count:int
14      | Sep
15      | Val of Content:string

The above code uses the following features not present in C# 6.0:

  • Line 12: Private accessibility in namespace declaration groups. The Token type is only accessible by code who has been declared inside the namespace MyCompany.Foundation.Core in the same library.
  • Lines 12–15: Discriminated union type. An instance of Token symbolizes either a consecutive number of escapes (Esc), a separator (Sep), or anything else (Val). Later, when a Token instance is analyzed via pattern matching, the F# compiler will emit a warning if a case has been forgotten by the developer.

Step 5: Implement Tokenization

We define a Tokenizer module with just one function named create. The function takes the escape character and separator as parameters, validates them, and returns another function. The returned function has the escape character and separator already built-in (in the closure). It takes only a single argument, which is the string to be tokenized.

16  [<RequireQualifiedAccess>]
17  module private Tokenizer =
18    /// Returns a function who can convert a given source string to a token stream.
19    let create esc sep = 
20      let sepName = "sep"
21      let sepLen = String.length sep
22      
23      // Validate parameters
24      Assert.notNullOrEmpty sepName sep
25      if sep.[0] = esc then invalidArg sepName "Separator cannot start with escape char."
26      if sepLen > 1 then
27          let iMax = sepLen - 1
28          for i in 0 .. iMax / 2 do
29              if sep.[0 .. i] = sep.[sepLen - i - 1 .. iMax] then
30                  invalidArg sepName "Separator cannot have same beginning and ending."
31        
32      // Return the tokenizer function
33      fun source -> 
34          match String.length source - 1 with
35          | -1 -> Val String.Empty |> Seq.singleton
36          | iMax -> 
37            let (|Esc|_|) = 
38                let rec aux acc i = 
39                    if i <= iMax && source.[i] = esc then aux (acc + 1) (i + 1) else acc
40                aux 0 >> function 0 -> None | count -> Some count
41            
42            let (|Sep|_|) i = 
43                if i <= iMax - sepLen + 1 
44                   && String.CompareOrdinal(source, i, sep, 0, sepLen) = 0 then Some()
45                else None
46            
47            let rec read valLen i = 
48              seq { let wrapVal() = 
49                        if valLen > 0 
50                        then source.Substring(i - valLen, valLen) |> Val |> Seq.singleton
51                        else Seq.empty
52                    if i <= iMax then 
53                        match i with
54                        | Esc count -> 
55                            yield! wrapVal(); yield Esc count; yield! read 0 (i + count)
56                        | Sep -> yield! wrapVal(); yield Sep; yield! read 0 (i + sepLen)
57                        | _ -> yield! read (valLen + 1) (i + 1)
58                    else yield! wrapVal() }
59            read 0 0

The above code uses the following features not present in C# 6.0:

  • Line 18: Simplified XML documentation comments. Comment lines starting with triple slashes /// are converted to what looks in C# like ///<summary>...</summary>. Inside the F# comment, angular brackets can be used deliberately, such as MyType<T, SomeOtherType<U>>.
  • Line 29: Slice expressions allow retrieving a range of values from a collection in a succinct way.
  • Line 34: Syntactic pattern matching. match ... with ... analyzes and decomposes syntactic patterns, who can come in many different shapes with arbitrary nesting and complexity.
  • Line 35: Type functions and automatic inference of type parameters. Seq.singleton is inferred as Seq.singleton<Token>, which is a call to an F#-defined type function.
  • Lines 37, 42: Active patterns. Functions named (| ... |) can define various kinds of active patterns (single-case or optional patterns with or without arguments, multi-case patterns), which provides even more flexibility in pattern matching than is available in other ML-derived languages. The patterns are matched in lines 54 and 56, with completeness checking at compile time.
  • Line 37: Value declaration with arbitrary initialization body. We have pointed out above that let (|Esc|_|) = defines an active pattern function. More precisely, (|Esc|_|) is just an ordinary value who happens to be a lambda function. The value is initialized with the result of the last line of the body below (a new lambda function created by composing the left and right sides of the >> operator). In C#, we can use the var keyword only with very simple initializers (and not at all with delegates); in F#, we can use the let keyword with an arbitrary body of code containing nested scopes of arbitrary depth. Ironically, this feature of the "functional-first" language F# makes it possible to implement the OO principle of encapsulation in an even more fine-grained way than in the "OO-first" language C#.
  • Lines 38, 47: Tail call optimization. rec aux defines a recursive function named aux ("aux" is a common naming convention for an inner auxiliary function within an outer recursive function). The recursive call to aux, aux (acc + 1) (i + 1), is in tail position, which is compiled to a construct that will never cause a stack overflow regardless of recursive depth. The same is true for the rec read function.
  • Line 40: Partial function application. aux 0 calls the aux function with only one parameter, which gets a new, partial version of aux where the first parameter is already "built in", but the second parameter still has to be passed.
  • Line 40: Function composition. The partial version of aux is composed (i.e., "sticked together") with the following function using the >> operator.
  • Line 40: Condensed pattern matching with the function keyword. function 0 ... is an abbreviation for fun x -> match x with 0 .... It defines a lambda function with a parameter whose name we do not care about.
  • Line 40: option types. None and Some ...are the two possible cases of a discriminated union named option<'T>, which we are pattern matching against.
  • Lines 48–58: Computation expressions with unified syntax and custom builders. seq { ... } defines a sequence (compiled as IEnumerable<Token>). This is a kind of computation expression. Instead of seq { ... }, we could also have used [ ... ] to get a list or [| ... |] to get an array of Tokens. The same kind of syntactic principle can also be used with async { ... } for defining asynchronous algorithms or query {...} for defining queries. Furthermore, it is possible in F# to implement custom computation expression builders (e.g., to simplify continuation passing with cont { ... } or to simplify checking of failure conditions with maybe { ... }). Iterators, async methods, and query expressions are also available in C#, but they are hard-wired into the compiler—C# does not have a unified, extensible mechanism for defining monadic constructs.
  • Line 50: Function pipelining. The |> operator "forwards" the argument from the left side to the function or constructor on the right side. This makes it possible to express the "path" of an argument through various transformations in an elegant and self-documenting way.
  • Lines 55–58: yield! keyword for appending multiple elements. C# 6.0 only allows appending single elements using yield. Because of yield!, it is possible to recursively program sequences in an intuitive way (e.g., for traversing directory trees etc.), while at the same time avoiding stack overflows thanks to tail call optimization.

Step 6: Implement Concatenation and Splitting

Now that we have a custom tokenizer, we can use it in our concat/split functions. Instead of doing the parsing on the "character by character" level, we can analyze groups of tokens via syntactic pattern matching, with the added benefit of automatic case completeness checking at compile time.

 60  [<RequireQualifiedAccess>]
 61  module String = 
 62    /// Returns a new string by connecting the given strings with the given separator.
 63    let concatEscape esc sep (strings:seq<_>) = 
 64      Assert.notNull "strings" strings
 65      let sb = StringBuilder()
 66      
 67      let appendTokens areLast ts = 
 68          let appendEsc count = sb.Append(esc, count) |> ignore
 69          let appendVal (v: string) = sb.Append v |> ignore
 70          let appendSep() = appendVal sep
 71          
 72          let rec aux = function
 73              | [] -> ()
 74              | Esc count :: [] -> appendEsc <| if areLast then count else count * 2
 75              | Esc count :: (Sep :: _ as ts) -> appendEsc (count * 2); aux ts 
 76              | Esc count :: ts -> appendEsc count; aux ts
 77              | Sep :: ts -> appendEsc 1; appendSep(); aux ts
 78              | Val v :: ts -> appendVal v; aux ts
 79          
 80          aux ts
 81          if not areLast then appendSep()
 82      
 83      strings
 84      |> Seq.map (Tokenizer.create esc sep >> List.ofSeq)
 85      |> Seq.fold (fun ts1 ts2 -> Option.iter (appendTokens false) ts1; Some ts2) None
 86      |> Option.iter (appendTokens true)
 87      
 88      sb.ToString()
 89      
 90    /// Reproduces the original substrings from a string created with concatEscape.
 91    let splitUnescape esc sep string = 
 92        Assert.notNull "string" string
 93        let emptyVal = Val String.Empty
 94        let sepVal = Val sep
 95        let flipAppend x1 x2 = Seq.append x2 x1
 96        
 97        // Produce token stream
 98        string
 99        |> Tokenizer.create esc sep 
100        
101        // Convert token stream to StringBuilder stream
102        |> flipAppend [emptyVal]
103        |> Seq.scan 
104          (fun (sb:StringBuilder, t1) t2 ->
105              match t1, t2 with
106              | Esc count, Sep when count % 2 = 1 -> sb.Append(esc, count / 2), sepVal
107              | Esc count, Sep -> sb.Append(esc, count / 2), Sep
108              | Esc count, _ -> sb.Append(esc, count), t2
109              | Sep, _ -> StringBuilder(), t2
110              | Val v, _ -> sb.Append v, t2)
111          (StringBuilder(), emptyVal)
112        |> Seq.map fst
113        
114        // Of each series of repeated StringBuilder references, keep only the last
115        // reference (which points to the StringBuilder's completed state). 
116        // Convert the remaining StringBuilder references to strings.
117        |> flipAppend [null]
118        |> Seq.pairwise
119        |> Seq.filter (fun (sb1, sb2) -> sb1 <> sb2)
120        |> Seq.map (fst >> sprintf "%O")

The above code uses the following features not present in C# 6.0:

  • Line 61: Module merging. We give our module the name String. However, there is already a .NET class named System.String and an F# library module named FSharp.Core.String. Now, when someone opens our MyCompany.Foundation.Core namespace and types String., Visual Studio IntelliSense will automatically list all static members and functions together. This is a straightforward and intuitive way of extending pre-existing static library functionality without having to go through a formal "extension member definition" mechanism. (Such a mechanism exists in F#, too, but it is used less often than in C#. It allows you to define not only instance extension methods, but also static extension methods, extension properties, and even extension events.)
  • Line 63: Wildcards. By writing seq<_> using the wildcard symbol _, we force the compiler to declare the strings parameter as a generic sequence (IEnumerable<...>), but let it still infer the type parameter automatically. This is sometimes callled "mumble typing". Because of the way the strings parameter is used in the function, it is ultimately inferred as seq<string>.
  • Lines 73–78: Syntactic pattern matching on union cases and list patterns. [] represents an empty list, and Esc count :: [] represents a list with a single element at the head, who itself represents the Esc case of the Token union type. The variable count is automatically initialized at runtime (if the overall pattern matches) with the number of consecutive escape characters found in the string. Further patterns are used in the remaining lines, and the F# compiler emits a warning if we forget a match.
  • Line 91: Only few reserved keywords in F#. In line 89, we use string as the name of a parameter. This is possible because string is not a keyword in F#; it's a type abbreviation that can be replaced with a custom definition without a problem. The same is true for all other names of "built-in" types, as well as most "built-in" operators (who are just predefined library functions).
  • Line 105–110, 120: Syntactic tuple types with pattern matching support.t1, t2 defines a tuple, which is matched against in the lines 105–110, with case completeness checking at compile time. In line 120, fst is a built-in F# library function who returns the first item of a tuple.
  • Line 120: Statically typed format strings. sprintf "%O" defines a function who takes an object and returns the object's ToString() result. In our example, it gets the string from each StringBuilder instance. (Other available format specifiers would be %i for integer numbers, %s for strings, %b for booleans, and more.)

Summary

I have used string concatenation and splitting as a didactical example to demonstrate many F# features not known in C# 6.0. Using other examples, I could continue with still more features: inlining, pattern matching with records, automatic structural equality and comparison, aggressive optimization of closures and generic calls, units of measure, type providers, custom operators as functions or members, quotations, ...

There is one feature I did not point out explicitly, because it is omnipresent: Immutability. All values in the example are read-only (which is the default in F#); I did not have to declare even a single value as mutable. Immutability simplifies understanding the program flow, because one does not have to worry about the same state being modified in different places. The advantages of immutability are more striking in parallel code, which I did not use in the example. By contrast, in C#, all values are mutable by default (and are consequently called "variables"), and local immutability is not even available, except for constants.

In the next blog post, I will use F# to systematically define an extensive battery of unit tests against the example, using a bare minimum of ceremony.

Tags:

C# | F#

Covariance and Contravariance in C# 4.0

by Marc Sigrist 20. March 2011 02:07

C# 4.0 allows to declare variance compatibility for delegates and interfaces. This means, for instance, that one can assign an IEnumerable<Cat> to an IEnumerable<Animal>.  The term variance compatibility, in this context, defines the kind of assignment compatibility between two closed generic types, which exists when the parameters of those types are derived from each other (or are themselves variant to each other). In other words: Given two types T1<P1> and T2<P2>,  variance defines how T1 is assignment compatible with T2 in cases where P1 is in an inheritance relationship with P2 (or is itself variant to P2). A more detailed definition can be found in the C# 4.0 language specification, section 13.1.3 Variant type parameter lists. There are three kinds of variance:

Kind of Variance Delegate Example Interface Example
Invariance
delegate T Clone<T>(T t);
interface IList<T>{
    T this[int index] { getset; }
    // Etc.
}
Covariance
delegate T Func<out T>();
interface IEnumerator<out T>{
T Current { get; }
};
Contravariance
delegate void Action<in T>(T t);
interface IComparer<in T>{
int Compare(T x, T y);
}

Invariance

In the above table, Clone<T> and IList<T> are invariant in T. As a consequence, it is not possible to assign Clone<Dog> to Clone<Animal>, or vice versa. This is because T is used both as an incoming parameter and an outgoing parameter at the same time. Imagine what would happen if one could actually assign an IList<Dog> to an IList<Animal>. If we then retrieved animals[0], we would get a dog as animal. So far, so good. However, what would happen if we set animals[0] = new Cat()? What should the underlying IList<Dog> do when it gets the cat assigned? Should it perhaps throw an InvalidCastException? There is no way this can be handled without violation of the strong typing principle. Therefore, the core languages of the .Net Framework: C#, F#, and VB.NET (with option strict on) do not allow covariance or contravariance in this situation.

IList<Animal> animals = new Collection<Animal>();
IList<Cat> cats = new Collection<Cat>();
// The following line would produce a compiler error.
// animals = cats;
// Thanks to the above compiler error, we can always be sure
// that the following line will not produce a runtime exception.
animals.Add(new Dog());

Unfortunately, ever since C# 1.0, strong typing has not been completely enforced with regards to arrays (many other languages, e.g. Java, have the same problem). The following programming error is not prevented by the compiler:

Cat[] cats = { new Cat(), new Cat() };
Animal[] animals = cats; // Compiler allows covariance, eventhough in parameters exist.
animals[0] = new Dog(); // --> ArrayTypeMismatchException at runtime!

Here is an interesting side note: It is sometimes argued that breaking type safety with arrays was a planned, pragmatic decision at the time of C# 1.0, because generics did not exist yet. However, according to Don Syme, who was responsible for generics in the CLR, back then it was not even certain whether generics would ever be introduced at all. Microsoft was reluctant, and the responsible research team was underfunded. The feature could only be introduced in C# 2.0 under extreme pressure at the very last minute. Luckily, things are different now. Runtime-integrated generics, and the many technologies built open them (such as Lambda expressions, Linq, and F#), have become a great success story, and they clearly distinguish Microsoft .Net and Mono from other popular development frameworks.

Covariance

In the above table, Func<out T> and IEnumerator<out T> are covariant in T. As a consequence, it is possible to assign Func<Cat> to Func<Animal>. Strong typing can be enforced by the compiler, because T is only used as outgoing parameter, never as incoming parameter. To enable covariance in T, T must be explicitly annotated with the generic modifier out.

Func<Animal> createAnimal = () => new Animal();
Func<Cat> createCat = () => new Cat();
createAnimal = createCat;
var a = createAnimal(); // Gets a cat as animal.

Contravariance

In the above table, Action<in T> and IComparer<in T> are contravariant. Therefore, it is possible to assign Action<Animal> to Action<Cat> or IComparer<Animal> to IComparer<Cat>. Strong typing can be enforced by the compiler, because T is only used as incoming parameter, never as outgoing parameter. To enable contravariance in T, it must be explicitly annotated with the generic modifier in.

Action<Animal> animalAction = animal => animal.Sleep();
Actionn<Cat> catAction = cat => cat.Meow();
catAction(new Cat()); // Meow...
catAction = animalAction;
catAction(new Cat()); // Zzz...

Other Considerations

Classes and structs cannot be declared as covariant or contravariant. Even if such a syntax were allowed, the feature would be useless in most cases. For instance, it would be logically impossible to assign a Collection<Cat> to a Collection<Animal>, because Collection<T> uses T both as an incoming parameter and an outgoing parameter. Every kind of collection, even if it is immutable, somehow needs a way to be built up (using T as incoming parameter) and a way to be read from (using T as outgoing parameter). Still, in some cases, where T is used only as incoming parameter or only as outgoing parameter, the feature might be practical (imagine some kind of Builder<out T> or Deserializer<out T>). However, the complexity of the C# compiler is stunning already today. For instance, given something as simple as an interface I1<out T>, it has to enforce type safety for extreme situations like I1<I1<I1<I2<I1<Pet>, I3<I2<Dog, I1<Cat>>>>>>>. But even if it can be proved that it is theoretically possible to create a compiler for C# who is sophisticated enough to check type safety for covariant or contravariant classes, it still does not necessarily make sense to spend the immense amount of time and resources it takes. Rather than doing this, I would prefer Microsoft to implement other features for the language, who take less effort, but are more urgent, such as read-only local "variables", an internal protected access modifier (as opposed to protected internal), and (my favorite wish) non-nullable reference types (if we have int? i, why not also have string! s? :)

I have covered the basics about variance in C#, the rest can mostly be concluded by logical deduction and combination (e.g., dealing with ref and out method parameters, etc.). However, if you truly want to dive into the matter, I recommend Eric Lippert's (of the C# compiler team) excellent twenty-one-part blog series on the subject.

Prerequisits for implementing a keep-alive mechanism in WCF 3.0

by Marc Sigrist 7. March 2010 00:59

There seems to be a common misconception that, in order to find out whether a WCF session is still alive, one has to implement some kind of custom ping or heartbeat operation on the service, which is to be regularly called by the client application. However, the WCF framework, when configured correctly, already does this for you in the background.

The key to this feature is the ReliableSession class. When ReliableSession.Enabled is true, WCF will continuously generate internal infrastructure messages to check whether the service is still ready. After the service has been dead for some specified time, the proxy's ClientBase<TChannel>.State is automatically set to CommunicationState.Faulted, and the ICommunicationObject.Faulted event is raised, giving the client application a chance to react. The delay between the service breakdown and the Faulted event can be specified through the ReliableSession.InactivityTimeout. If, for instance, the InactivityTimeout is set to four minutes, WCF will generate several infrastructure messages during every four minutes. Therefore, if you shut down the service, the Faulted event will be raised on the client after four to six minutes. If you set the InactivityTimeout to 20 seconds, it will be raised after 20 to 30 seconds. The exact duration depends on WCF-internal implementation details.

In order for this to work, you also have to set the Binding.ReceiveTimeout to at least the duration of the InactivityTimeout. The ReceiveTimeout specifies how long the service will stay alive without any service operations being called from the client application. If, for instance, you expect your client application to call service operations at least twice an hour, you may set the ReceiveTimeout to 30 minutes, but still let the InactivityTimeout be 4 minutes or so, in order that the client application can take measures in the background when the service is temporarily interrupted.

The following example shows how to define both timeouts on the client and server sides, either configuratively (in the App.config file) or imperatively (in code). The example presupposes that you are using a reliable session-capable binding (such as WSHttpBinding or NetTcpBinding), and that you have applied the ServiceContractAttribute.SessionMode, ServiceBehaviorAttribute.InstanceContextMode, OperationContractAttribute.IsInitiating, and OperationContractAttribute.IsTerminating properties in meaningful ways. If you are not sure about that, you may want to read the respective MSDN documentation entries first. A comparison table of the system-provided bindings can be found here. Furthermore, if you do not like hand-coding configuration files, you can use the WCF service Configuration Editor Tool, which is installed with the Microsoft Windows SDK.

Defining the Timeouts Configuratively

<configuration>
  <
system.serviceModel>

    <!--
In the App.config files of the service host and client apps: -->
    <
bindings>
      <
netTcpBinding>
        <
binding name="myNetTcpBinding" receiveTimeout="00:30:00">
          <
reliableSession enabled="true" inactivityTimeout="00:04:00"/>
        </
binding>
      </
netTcpBinding>
    </
bindings>

    <!--
In the App.Config file of the service host app: -->
    <
services>
      <
service name="MyTest.MyService">
        <
endpoint
        
address="net.tcp://localhost/MyService/"
        
binding="netTcpBinding"
        
bindingConfiguration="myNetTcpBinding"
        
contract="MyTest.IMyService"/>
      </
service>
    </
services>

    <!--
In the App.Config file of the client app: -->
    <
client>
      <
endpoint
      
address="net.tcp://localhost/MyService/"
      
binding="netTcpBinding"
      
bindingConfiguration="myNetTcpBinding"
      
contract="MyTest.IMyService"/>
    </
client>

  </
system.serviceModel>
</
configuration>

Defining the Timeouts Imperatively

// using System;
// using System.ServiceModel;

// In the service host and client apps:

var
myNetTcpBinding = new NetTcpBinding();
myNetTcpBinding.ReceiveTimeout = new TimeSpan(0, 30, 0);
myNetTcpBinding.ReliableSession.Enabled = true;
myNetTcpBinding.ReliableSession.InactivityTimeout = new TimeSpan(0, 4, 0);

// In the service host app:
var
host = new ServiceHost(typeof(MyService));
host.AddServiceEndpoint(typeof(IMyService), myNetTcpBinding, "net.tcp://localhost/MyService");

// In the client app:
var
factory = new ChannelFactory<IMyService>(myNetTcpBinding, "net.tcp://localhost/MyService");
var 
channel = factory.CreateChannel();

Book review: Pro WPF in C# 2008

by Marc Sigrist 27. February 2010 17:55
Book image Title: Pro WPF in C# 2008
Subtitle: Windows Presentation Foundation with .NET 3.5, Second Edition
Publisher: Apress® Books for Professionals by Professionals™
Series: The Expert's Voice in .NET
Author: Matthew MacDonald
Date of appearance: February, 2008 (1040 Pages)

This book is a thorough examination of the WPF 3.5 technology – its architecture, what you can do with it, and how to do it. It is directed at professional C# developers.

The book is written in a systematic, comprehensible way. A chapter starts with a basic introduction and includes graphics of the respective WPF sub-class hierarchy. The chapter's subject is then steadily explored in more and more detail. For instance, at the beginning, there are five pages just on resolution independence. XAML is explained in-depth, starting with the four ways of loading and compiling: Code-only, code and uncompiled XAML, code and compiled XAML, and XAML only; this is followed by the specifics of the XAML grammar (markup extensions, attached properties, etc.). There are seven pages on non-rectangular windows and sixteen pages on playing sound on different OS versions. As the book goes on, the author really shines in describing complex subjects, such as 3-D drawing, in a logically understandable way. Towards the end, there is a tabular overview of features missing in WPF compared to Windows Forms, with recommendations on when to choose one over the other, or both of them together, and how to mix them best.

The volume also contains lots of small, but precious pieces of surplus information, such as: Properties of WPF controls can be set in any order, without causing any change in behavior; or: By using an overloaded version of DependencyObject.SetValue in code, you can attach a value for any dependency property, even if it is not defined as an attached property (which is not possible in XAML). In addition, the author mentions various quirks of WPF, and how to get around them, if possible. Example: When you restart an animation that is almost complete, and the animation had the current position as the starting point, the animation will appear to slow down. Another example: Windows Vista always requires permission elevation for a setup, even though, in the case of Click Once, this makes no sense. As a consequence, a Click Once WPF application, on Vista, cannot be installed under a regular user account; the user is forced to install it under an admin account - which defeats the purpose of using Click Once in the first place...

Developers are all-too-familiar with the Pareto principle: 80% of the tasks of a project can be solved „easily“ in 20% of the time, but solving the other 20% takes at least 80% of the time. If you want to use WPF in a productive way, I strongly recommend taking the time to study this book. Admittedly, at 1040 pages, this is quite some endeavor. However, you will be rewarded many times over, as you will be saved a lot of frustration and unexpected delays, when you already know from the beginning how to solve much of the other 20%.

Yet another comment on the C# cast ( ) and „as“ operators…

by Marc Sigrist 21. February 2010 19:39

Time and again, the question comes up whether to prefer the cast ( ) or the „as“ operator in C# to perform conversions to reference types. According to the C# 3.0 language specification, the two operators behave differently in several respects:

C# Conversion Operator( )as
Name Cast operator as operator
C# 3.0 Specification section 7.6.6 7.9.11
Can convert to ValueType Yes No
Executes user-defined explicit conversions Yes No
Compile-time errors CS0030: Cannot convert type 'X' to 'Y'. CS0030: Cannot convert type 'X' to 'Y'.
CS0077: The as operator must be used with a reference type or nullable type ('Y' is a non-nullable value type).
Runtime behavior if not convertible Throws InvalidCastException: "Unable to cast object of type 'X' to type 'Y'." Returns null

Given the above comparison table, it follows that you must use the cast operator ( ) in the following cases:

  • to convert to a ValueType:
    object o = 137; var i = (int)o;
  • to convert via a user-defined explicit operator overload defined in the source type. Note that the overload may return anything whatsoever: The same instance, a new instance of the same type, a new instance of a completely non-related type, null, a value type or reference type...
    // Declared inside a class called Source:
    static explicit operator Target(Source s) {return new Target();}

By contrast, you must use the "as" operator in the following cases:

  • to be 100% sure that that the result points to the same instance, or null, but never anything else:
    var bar = foo as Bar;
  • to return null if a runtime conversion to the desired reference type is not possible, as in this construct:
    var bar = foo as Bar ?? new Bar();

Furthermore, the "as" operator is a bit more tolerant with generics at compile time than the cast operator. That’s because the as operator casts „directly“ to a type of the same inheritance line:

TDerived GetDerived<TBase, TDerived>(TBase b) where TDerived: class {
    // Produces compiler error CS0030: Cannot convert
    // type 'TBase' to 'TDerived'.

    // return (TDerived)b;

    // This compiles...
    return
b as TDerived;
}

In all other cases, from a technical point of view, you may freely choose between the cast and the "as" operator. Therefore, the „right“ decision depends on what you want to express most in the source code. If you use the "as" operator, you thereby clearly document that you are interested in a reference target type that points to the same instance, and nothing else whatsoever – i.e., you do not want some crazy custom explicit operator to produce any side effects! Moreover, as a matter of taste, the "as" operator makes your code less cryptic, because it uses no parentheses, whereas the cast ( ) operator looks like old-fashioned C code.

For these reasons, personally, I prefer the "as" operator over the cast ( ) operator. However, I have met other developers who almost religiously defend usage of the cast ( ) operator as a best practice, „because it immediately throws an InvalidCastException“ at runtime if necessary, while the "as" operator might only later produce a NullReferenceException. Yet, against this, it could be argued that the "as" operator is even more secure, because it does not allow you to compile if it’s a value type, and it will never produce side effects...

Some tools, such as JetBrains‘ ReSharper, warn you if you apply the "as" operator without checking for null with a null comparison within the same procedure, even if null is logically impossible; e.g., when you have defined a null-checking procedure in a separate external helper method. You can turn this off, but only on a per-user basis. Others who look at your code may still see the warning. You may also disable the warning by surrounding the „critical“ section with a specific ReSharper comment, but it’s annoying to have your code cluttered like this. Therefore, if your company uses ReSharper, this might be a pragmatic reason to prefer the cast ( ) operator over the "as" operator.

In my opinion, it would be better if the C# "as" operator did not return null, but throw an InvalidCastException if the direct cast is not possible. Nothing of importance would be lost by this – you can always check for null with the "is" operator - but a lot of misunderstandings and disagreements would be prevented. Interestingly, the VB.NET language has the DirectCast operator, which does exactly this. A similar behavior could be implemented in C# like this:

public static T As<T>(this object o) where T: class{
    if
(o == null) throw new NullReferenceException();
    if
(!(o is T)) throw new InvalidCastException();
    return
o as T;
}

Simulating multiple inheritance in C#

by Marc Sigrist 18. February 2010 23:15

It is possible to simulate multiple inheritance in C#, in a limited way, by applying extension methods to interfaces. An example is shown below. Note that this works only for method implementation inheritance, but not for properties or any other kinds of base members.

The C# team originally considered including extension properties in C# 4.0, but then dropped the feature. The reason apparently was that including extension properties would logically imply including indexed extension properties as well, which C#, by design, does not support. However, accessing (but not defining) indexed properties will be introduced in C# 4.0, in order to make it easier to work with COM interop. Another reason might be that extension properties in C# 4.0 would cause conceptual confusion with the already-existing feature of attached properties in WPF. Interestingly, F# does offer extension properties, but it cannot be used (yet?) as code-behind language for WPF XAML files.

You probably will not use this approach in order to „simulate multiple inheritance“ very often. However, the capacity to add extension methods to interfaces, as such, is a powerful feature. It allows to cleanly separate categories of standard behaviors from their owners.

 using System;

// This test shows how to simulate multiple inheritance in C#.

// Requires .Net Framework 3.5 as target framework.

namespace Test {

    interface IBase1 { }

 

    static class Base1Implementation {

        internal static void Base1Method(this IBase1 base1) {

            Console.WriteLine("Base1Method called.");

        }

    }

 

    interface IBase2 { }

 

    static class Base2Implementation {

        internal static void Base2Method(this IBase2 base2) {

            Console.WriteLine("Base2Method called.");

        }

    }

 

    class Derived: IBase1, IBase2 { }

 

    static class Program {

        static void Main() {

            var derived = new Derived();

            derived.Base1Method(); // Writes "Base1Method called."

            derived.Base2Method(); // Writes "Base2Method called."

        }

    }

}