21 Apr 2015

Code Formatter

If you want to commit to the .NET open source code you need to follow the coding style that the teams use and to assist in that there is a helpful tool you can run over your code called Code Formatter. It is a simple command line tool which you can run over your code to fix up the style.

Clipboard01

Ultimately I think this is a great tool and something that can easily be added into your build process to keep the code clean. I would like to see someone like Steve Cadwallader amazing CodeMaid start to introduce this tool so it can just be part of Visual Studio.

A quick test

I decide to take it for a spin over my array fighter code, since it is a fairly messy piece of quick code I wrote,  to see what would change and it is was really minor.  First up was that copyright headers were added and I did play with that to tweak the header a bit, which is a great option.

image

Second up was that all my items had accessors placed on them and my fields were changed from starting with a capital to starting with a lowercase & _.

image

A bigger test

For a second test I ran it over a HUGE project which is frankly a disaster, and it took less than 10 seconds to handle just a few thousand files. Some things I saw there were the removal of blank lines:

image

Changing the copyright symbol to a unicode number

image

Static fields were prefixed with s_

image

Addition of spaces in auto-properties and between member names and getters/setters

image

image

I don’t personally like all of these, but they make sense and it is configurable.

Looking at the code

Since this is an open source tool we can look through the code for some interesting bits of info. the first area I looked at was the unit tests, since it is all unit tested using XUnit and I really like how they have crafted the tests. Makes a lot of sense and is simple to read & work with.

image

Nice to see someone using the System.ComponentModel.Composition for a lightweight DI framework, since it has been spoken about so often but I never see anyone use it. The most interesting part though is that this is built with Roslyn and it is amazing how easy it is to read that code. For example here is the code to insert the copyright header in 20ish lines of code:

image

if you are looking for Roslyn examples, this is a great place to start.

Dead Regions

“Hidden” inside the solution is a second tool named Dead Regions, which is used to find and potentially edit conditional compiler blocks of code which are always TRUE/FALSE. It is interesting, but I am not sure it has as much broad use as the formatter. Checkout the readme.md for good write up on it.

Tags: 
20 Apr 2015

String Interpolation (C# 6)

In our code today we often work with strings and want to mix code with it. The simplest version of this is just concatenation of strings:

// it is okay when it is small
var result = "Hello " + Name;
// it looks messy when it gets long
var result2 = "You " + SubjectName + " must be the pride and joy of " + SubjectHomeTown;
// and when you take it across multiple lines... it is ugly
var result3 = @"This was a " + TestResult + @".
I am making a " + ReminderTool + @" here: 
" + SuccessRating;

String.Format is a potential help here:

// multiple data slugs 
return string.Format("{0} is {1} years old and their favourite animal is {2}", Name, Age, AnimalsOrderedByFavourite().First());

//using string.format for formatting
return string.Format("Hamsters cost {0:0.00}", 14.22);

The problem with string.format is that it is possible to make mistakes with the format items, for example:

// this line works but outputs the wrong data. There is NO way for the compiler to identify this & you get no exceptions when you run it.
string.Format("{0} is {0} years old and their favourite animal is {2}", Name, Age, AnimalsOrderedByFavourite().First());

// this line compiles fine, but it will raise a X exception when run as there is only three parameters and your are asking for a forth
string.Format("{0} is {3} years old and their favourite animal is {2}", Name, Age, AnimalsOrderedByFavourite().First());

String Interpolation to the rescue!

String Interpolation aims to make these scenarios easier by allowing you to have blocks of code directly inside the string itself. To achieve this we need to tell the compiler that the string may contain blocks of code, and we use the $ symbol prefixed on the string to do that. We can then insert blocks of code using the same braces we normally use for blocks of code. This allows us to change to code in the above examples to:

var result = $"Hello {Name}";
var result2 = $"You {SubjectName} must be the pride and joy of {SubjectHomeTown}";
var result3 = $@"This was a {TestResult}.
I am making a {ReminderTool} here: 
{SuccessRating}";

return $"{Name} is {Age} years old and their favourite animal is {AnimalsOrderedByFavourite().First()}";

return $"Hamsters cost {14.22:0.00}";

In the above examples, it becomes clear what member what are working with and it is not possible for us to make mistakes as the compiler will run and identify the issues at compile time which are problems! It can also simplify things as with the third line where we use a continuation without having to have many @ symbols scattered around.

This is Code

This isn’t just a way to insert properties into strings, it is a way to insert code so you can do all kinds of interesting things with this:

// concat inside the code block
var example1 = $"Hello {Name + LastName}";

// calling methods
var example2 = $"Your home town is in {LookupProvinceState(SubjectHomeTown)}";

// async works too
var example3 = $"The temp is {await GetTemp()}";

// strings inside strings with interpolation
var example4 = $"Inception is {Rating + $"Inception is {Rating}"}";
// LINQ works & multi line works if you add an @
var example5 = $@"LINQ works too {from a in AnimalsOrderedByFavourite()
                                 where a.Length > 20
                                  select a}";

// we can do string formatting
var example6 = $"Your balance is {Balance:C}";

The limitation here is that it must be a single statement. You cannot type a semicolon (;) in the string interpolation.

Syntactic Sugar

This, like so many of the C# 6 features is syntactic sugar and really it is just converting it to use string.format. For example out first example above becomes:

string text = string.Format("Hello {0}", new object[] {
    this.Name + this.LastName
});

What about Cultures? The answer is IFormattable

Looking at example six in the “This is Code” section, you can see we use the string formatting for currency but we cannot specify the culture information, so we cannot specify HOW to format it if we want to be specific. What is the solution? The result of the the interpolated string is a string which also implements IFormattable, and you can create a method to set the correct culture very easily:

static void Demo()
{
    // formatting as south african
    var example1 = en_za($"Your balance is {Balance:C}");
}

public static string en_za(IFormattable formattable)
{
    return formattable.ToString(null, new CultureInfo("en-za"));
}

In .NET 4.6, the string will also support System.Runtime.CompilerServices.FormattedString which will enable other options for formatting.

Tags: 
15 Apr 2015

What is a Universal app?

An universal app (in regards to the Microsoft platform as Apple uses this same term for something similar but different enough) is made up, at a very high level, of three areas. The first area, the User Interface (UI) is what you see, and for a Universal App it is built either with XAML or HTML. The second is the logic which powers the UI, the logic can go by many names (code behind, controller, view model and more), and we will refer to the logic in this document, as the brains. The UI and brains work together, the brains giving the raw data to the UI which handles laying the data out and styling the data, and the UI providing input to the brains in the form of interactions from the user. The third piece is supporting code which the brains and UI use, for example code which talks to an API or a database. This third piece is sometimes very separate and sometimes very mixed into the brain.

Traditionally if you wanted to build an app which targets multiple platforms, you had to create a custom UI for each platform and traditional software development tied the brain and the UI tightly together which meant that you had to often rewrite the brain or copy & paste between different platforms. This leads to duplication in everything: cost, time to add features, bugs etc…

1

Microsoft has introduce a concept called Universal Apps, which is a way of creating a single core app which can be run on Windows phone 8.1, Windows 8, 8.1 or 10 and Xbox One (coming soon). The way this works is that the app is partitioned into multiple pieces, one for each target platform and an additional piece called Shared. For this article we will focus on building an app which targets Windows phone 8.1 and Windows 8.1 and thus we will have three pieces in our project.

2

Microsoft does have other technologies which can support even greater reuse of code, such as Portable Class Libraries (PCL), and there are industry patterns, for example Dependency Injection (DI) which can help but none are essential and none are directly relevant to this document. The take away though is that getting the most reuse of code, will take planning and understanding of software development by skilled and experience developers.

The core idea in a Universal app is what you place as much of the code in the shared partition, allowing each platform to use the exact same UI, brains and supporting code. Modern technologies included in XAML and CSS 3 for HTML allow the UI to respond to different screen sizes and different devices in an intelligent way, thus ensuring one UI can be built and used across all platforms. For the brains, proven practices, such as MVVM, allow the brain to be decoupled from the UI and that allows maximum reuse of that code.

In an ideal scenario you would get 100% of the code in shared however in reality it is between 75% and 95% (based off my own experiences and what the app does & what platforms it targets). The issue is that even for all the best technology today, some UI elements just do not work across different platforms, some features only exist on a specific platform and some optimizations can be made to improve performance or the user experience. In these cases the relevant piece of code or UI is moved out of shared partition and to the actual platform with minor duplication at worst.

A great example of this is settings: On Windows phone the design style says you have one settings page which is accessed from a button in the appbar. Windows 8.1, the design style suggests you do not have the settings in the appbar, but rather you use the settings charm and you make use of flyouts. In traditional development that would require totally separate code for the setting and totally separate UI. Following correct practice, you should be able to have the logic once, in shared, the UI broken into components, also in shared. Then on each platform all that is unique is how you compose those components together.

3

Tags: 
15 Apr 2015

Expression Bodied Members (C# 6)

C# 6
Want to learn about other C# 6 features? Check out the full list of articles & source code on GitHub

Expression bodied members, which is a new feature in C# 6 and is a very interesting feature which aims to have less lines of code in your app for simple things. What do I mean by simple things? Anything which is ONE statement.

I use the word statement and not line since you can break a statement across multiple lines and it will work. Think of statement to mean, when you hit your first ; you are done.

Here are some examples of one statement code blocks:

// a method with one statement in it
private double Tax()
{
    return 1.14;
}

// a read-only property with one statement
public double Price
{
    get
    {
        return CostPrice * Tax();
    }
}

// a method calling an event, in this case using the nullet operator
private void RaisePain(string propertyName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

All examples have one one statement and all examples are also multiple lines of code. The first and last examples are 4 lines of code (4 to 1 ratio) and the second example is 7 lines of code (7 to 1 ratio). Expression Bodied Members aims to get that to a 1 to 1 ratio! Smile

Expression Bodied Members

The name of this feature hints at everything you need to know for this: This is for members (properties, methods etc…), this is an expression and so we will use the => operator and that operator will be followed by the body of the expression. If we apply that logic to the first example we can rewrite them as this:

// still a method, just one line
private double Tax() => 1.14;

// still a readonly property
public double Price => CostPrice * Tax();

// still a method
private void RaisePain(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

This really cuts the code to statement ratio WAY down and lets you express simple things without much ceremony.

While it uses the => operator, I have intentionally NOT called these lambda’s because they do not support all the functionality of a lambdas.

Supported and Not Supported Scenarios

As we have seen both methods, with or without parameters, and read-only properties (i.e. a property without a setter) are supported. Operators are also supported:

public static Complex operator +(Complex a, Complex b) => a.Add(b);

Converters (implicit or explicit) are also supported:

public static implicit operator string(Name n) => n.First + " " + n.Last;

Indexers are also supported:

public Customer this[Id id] => store.LookupCustomer(id);

There are also members not supported by this:

  • Constructors: Often they have side effects and do not return anything plus there are a lot of complexity in them (inheritance for example) which makes them a poor fit.
  • Events: They need a add & remove code, so they can’t fit the one statement thinking.
  • Finalisers: Same issues as constructors

In summary, I think think that this should provide for a cleaner/simpler code but let me know what you think about it?

Tags: 
14 Apr 2015

NameOf (C# 6)

C# 6
Want to learn about other C# 6 features? Check out the full list of articles & source code on GitHub

nameof is a fantastic piece of syntactic sugar in C# 6, which aims to solve a major pain: magic strings. A magic string is a type of string in your application which is isn’t an input/output related but rather a marker or used for some comparison, for example you might do role checking with something that looks like this:

if (role == "admin")
{
}

The issue with this string “admin”, is that there is NO compiler checking for it. That means if you mistype the content of the string, you do not know it is wrong until the app fails. The second issue with a magic string, is that the refactoring tools do not commonly work with them, so if you changed “admin” to “administrator” in one place, there is no way to tools can find and update all the strings throughout you app and your app breaks again when it runs.

A lot of time you can clean this up with enumerators or objects. For example we can improve our role scenario but checking against an enumerator:

if (role == Roles.admin.ToString())
{
}

This no longer suffers from the two issues listed above Smile

Members & Magic Strings

The problem remains with magic strings which refer to members (fields, properties, methods, types etc…) of your code. In this first example we have a magic string pointing to the property Age:

public int Age
{
    get { return age; }
    set
    {
        age = value;
        RaisePropertyChanged("Age");
    }
}

For the second example we have a magic string  pointing to a class Track:

var type = Type.GetType("Track");

And finally our third example is around checking a field for null and raising an exception, if needed, with the correct field name:

private void RaisePropertyChanged(string propertyChanged)
{
    if (propertyChanged == null)
    {
        throw new ArgumentNullException("propertyChanged");
    }

    // do stuff
}

All of these examples suffer from the same two problems listed with magic strings, but until now, there hasn’t been a way to effectively clean them up.

nameof

The goal of the new nameof keyword is to solve this specific type of magic string, i.e. ones which refer to members in code. By using nameof we can change the above examples to the following:

// example 1
public int Age
{
    get { return age; }
    set
    {
        age = value;
        RaisePropertyChanged(nameof(Age));
    }
}

// example 2
var type = Type.GetType(nameof(Track));

// example 3
private void RaisePropertyChanged(string propertyChanged)
{
    if (propertyChanged == null)
    {
        throw new ArgumentNullException(nameof(propertyChanged));
    }

    // do stuff
}

Note we have eliminated the strings completely! This means if you mistype a member name at compile time you would get an error!

image

We also get FULL refactoring support now, since Visual Studio and other refactoring tools can get the information they need to identify that it is a member and replace/rename as needed.

More sugar

As mentioned at the start, this is just a case of syntactic sugar, the compiler is doing clever tricks for us and generating slightly different code. All nameof does, is instruct the compiler to convert the member to a string, so the outputted code is the same before with strings.

I want to recommend the TryRosyln website which is FANTASTIC to experiment with C# 6 and also shows you the decompiled code side by side, basically showing you how the syntactic sugar works:

image

What does it output?

In the first set of examples we looked at simple members, but what if we have something more complex for example a namespace or a class & property. In these cases it will output the last part each time:

nameof(Track.Band); // Class.Property - outputs: Property, in this case 'Band'
nameof(System.Configuration); // Namespace - outputs: Last namespace, in this case 'Configuration'
nameof(List); // List + Generics: outputs: The type of the object, in this case 'List' <
nameof(this.field); // this keyword + field - outputs the field name 'field'

What isn’t supported

nameof isn’t a solution to everything in .NET and there is a lot which won’t work. Here are some examples:

nameof(f()); // where f is a method - you could use nameof(f) instead
nameof(c._Age); // where c is a different class and _Age is private. Nameof can't break accessor rules.
nameof(List<>); // List<> isn't valid C# anyway, so this won't work
nameof(default(List<int>)); // default returns an instance, not a member
nameof(int); // int is a keyword, not a member- you could do nameof(Int32)
nameof(x[2]); // returns an instance using an indexer, so not a member
nameof("hello"); // a string isn't a member
nameof(1+2); // an int isn't a member

Is this a replacement for CallerMemberName?

I have written about a fantastic .NET 4.5 feature called CallerMemberName. To recap it is a way to attribute a parameter of a method and have the runtime change the value of that parameter to be the name of the calling member. In the following example the output will be ‘Main’, matching the name of the calling method:

private static void Main(string[] args)
{
    WhoCallsMe(); 
}

static void WhoCallsMe([CallerMemberName] string caller = "")
{
    Console.WriteLine(caller);
}
This seems to be similar to nameof but there are some fundamental differences, most importantly nameof is at COMPILE TIME and CallerMemberName is at RUNTIME. This means that this one method in the example can work with multiple callers; i.e. I could take the above example and have a different member call it and it will output the correct name of the caller. There is no way to do that with nameof, which is basically hard coded values. There is some overlap of functionality and in some cases, like XAML + RaisePropertyChanged where you could pick one or the other based on taste, but these two pieces of functionality do have their differences and there are times where CallerMemberName is the really only option.
Tags: 
14 Apr 2015

The null propagation operator - NULLET (C# 6)

C# 6
Want to learn about other C# 6 features? Check out the full list of articles & source code on GitHub

NULLs, you either hate them or you loath them. They are the source of so many issues in the apps we write, especially the dreaded NullReferenceException. The current fix is to be very defensive in your programming, for example:

public static void Exception(Track track)
{
    // this may cause an exception if the track object, band property or the frontman property is null
    Console.WriteLine("HI! " + track.Band.FrontMan.Name);

    // defensive programming to avoid null exceptions
    if (track != null && track.Band != null && track.Band.FrontMan != null)
    {
        Console.WriteLine("HI! " + track.Band.FrontMan.Name);
    }
}

Constant null checking also makes working with events particularly ugly, because you need to check for nulls before you call an event:

internal class NullConditionalEvent : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePain(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

NULLET

Clipboard01With C# 6, a new operator has been added; which is officially called the null propagation operator: ?.

Some people have taken to calling it the Elvis operator as it looks like two eyes & a splash of hair similar to Elvis, however I think that hair looks a lot more like a mullet so I have taken to calling this the NULLET operator.

The nullet operator allows us to check for nulls, so we can change the first example to the following which will NOT raise a a null exception:

public static void Exception(Track track)
{
    Console.WriteLine("HI! " + track?.Band?.FrontMan?.Name);
}

The way the nullet works, is that it tells .NET to check if the preceding item is null BEFORE moving to the next item. If the item is null, then it stops evaluation at that point and returns null, for example with track:

image

If you want the internals of this logic, check out this amazing post on Roslyn Codeplex site.

The important question is what will be the outcome of our above example; if any of the properties are null then it will be “HI!” + NULL which becomes “HI! “ and so that is what will be written to the screen.

Events & Methods

In the first example, there is an example of events and how you need to check for null before raising it. A side note is I am using events as the example, but this applies to any delegate. In the following example you can see how the nullet can help us clean up the event code:

private void RaisePain(string propertyName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

To be able to call the event we need something for the nullet to do after the evaluation, so we use the invoke method. This is also a great example of how the nullet is more than just support properties, it works with methods too.

Null + Indexers = Support for arrays & lists

Nullet can work indexers as well, with a small syntax change – instead of ?., you have just ? (i.e. just a question mark, no trailing full stop). In both scenarios below the code happily executes with NO null issues because we have added the ? between the object and the indexer.

Track[] album1 = null;
Console.WriteLine("Album 1 Track 6 " + album1?[5]);

List<Track> album2 = null;
Console.WriteLine("Album 2 Track 3 " + album2?[4]);

Not the end of all your null pain

The key fact around nullet is that it is a short hand in your code which prevents NullReferenceException but you can still have exceptions related to NULL. An example of this when you use Enum.Parse:

Enum.Parse(typeof(Example), track?.Band?.FrontMan?.PrimaryInstrument);

In this scenario, if you have any section as null, it will return a null, however the  Enum.Parse method cannot handle null and will raise a NullArgumentException. In this case the method you are passing the null too is key, so you still need to make sure you are checking for nulls in some scenarios, but you can use nullet to make the checking much cleaner:

if (!string.IsNullOrWhiteSpace(track?.Band?.FrontMan?.PrimaryInstrument))
{
    Enum.Parse(typeof(Example), track?.Band?.FrontMan?.PrimaryInstrument);
}
Tags: 
10 Apr 2015

Exception Filtering (C# 6)

C# 6
Want to learn about other C# 6 features? Check out the full list of articles & source code on GitHub

Many of the other C# 6 features I’ve covered are implemented using syntactic sugar, exception filters isn’t. This has been in the deep parts of .NET for some time and in C# 6 it is getting surfaced finally. This is very much the introduction to Exception Filtering, and I have another post with advanced topics I would love for you to read.

Catching Exceptions

Catching exceptions today uses the exceptions type as the condition for the the catch block.

try
{
    // code which could throw exception
}
catch (HttpException ex)
{
   // catching only HttpExceptions
}

In the above code, any HttpException will be caught but any exception of any other type won’t.

try
{
    // code which could throw exception
}
catch (HttpException ex)
{
   // catching only HttpExceptions
}
catch (Exception ex)
{
    // catching everything except HttpExceptions
}

We can catch everything by catching the base Exception class. Order here matters, but the compiler will help prevent you from hurting yourself:

image

Today, if you want more fine grain control of what a catch block responds to you need to do that in the catch block itself.  For example, handling an exception differently based on values in the exception.

try
{
    // some code
}
catch (HttpException ex)
{
    if (ex.HttpCode == 401)
    {
        // need to do an authenticate
    }
    else
    {
        // some other HTTP error we need to handle
    }
}

Another good example is logging, based on a setting you may want to log or not log exceptions.

try
{
    // some code
}
catch (HttpException ex)
{
    if (loggingTurnedOn)
    {
        // log it
    }

    throw;
}

Exception Filters

Exception Filters adds a way to have additional requirements for a catch block in the form of a boolean expression which we append with the when keyword. We can now rewrite the two examples as follows:

//example 1
try
{
    // some code
}
catch (HttpException ex)
    when (ex.HttpCode == 401)
{
    // need to do an authenticate
}
catch (HttpException ex)
{
    // some other HTTP error we need to handle
}

//example 2
var loggingTurnedOn = false;
try
{
    // some code
}
catch (HttpException ex)
    when (loggingTurnedOn)
{
    // log it               
    throw;
}

The when clause can take in ANYTHING which evaluates to a boolean expression. You can put ands or ors and chain things together:

when (loggingTurnedOn && ex.HttpCode != 401)

You can pass methods in to the when condition too:

public static bool ThrowException()
{
    //elided
}

public static void OldPain2()
{  
    try
    {
        // some code
    }
    catch (HttpException ex)
        when (ThrowException())
    {
       // do something
    }
}

When isn’t if

Lastly, the language chosen for this feature is important, it isn’t IF, because it isn’t an IF statement – it is a WHEN condition. One of the important distinctions around that is that an IF statement supports ELSE and WHEN does not.

Tags: 
01 Apr 2015

Static Using Statements (C# 6)

C# 6
Want to learn about other C# 6 features? Check out the full list of articles & source code on GitHub

Static using statements are a new feature of C# 6 designed to improve the readability of code. Before we begin: a word of warning for this feature. IMHO This feature is not meant to be used everywhere in your code. This feature is like salt, a little is great – too much, can cause issues.

With the warning out of the way, lets see what it does? There are times where the class name can break the flow of the code, lowering readability. Here is some example code we will work with:

using System;

internal class StaticUsing
{
    private void BrokenFlow()
    {
        Console.WriteLine(Math.Cos(5) * Math.Tan(20) + Math.PI);
    }
}

The addition of the Math class makes this a little harder to read, so we can use the new static using statement feature to remove that:

using System;
using static System.Math;

internal class StaticUsing
{
    private void BrokenFlow()
    {
        Console.WriteLine(Cos(5) * Tan(20) + PI);
    }
}

Note, that while we have removed the Math class from line 8, we have added a new using statement with the static keyword in line 2. This line tells the compiler where to look if it cannot find the method/property in the current scope. This is just syntactic sugar again and the resulting code is the same as in the first example.

The only requirement for this to work is that the class is that the class is static. That means this works with Math, Console, Convert & any of YOUR own static classes too.

What happens if I have a member with the same name already in scope?

What happens if there is already a member in scope with the same name, for example here I have defined a Cos method:

public static void BrokenFlow()
{
    Console.WriteLine(Cos(5) * Tan(20) + PI);
}

public static double Cos(int value)
{
    return -1;
}

In this scenario it uses the locally defined member first, as that is the first one which the compiler finds. This is an example of where you can shoot yourself in the foot by using this feature too often and running into hard to find conflicts.

What about Extension Methods?

C# 3.0 added support for extension methods which let you write methods which appear to be part of the underlying class but are actually separate. I’ve added the example method to the string class in the following example:

namespace ConsoleApplication1
{
    using System;
    using ExtensionMethods;

    internal class StaticUsing
    {
        public static void BrokenFlow()
        {
            Console.WriteLine("Robert".Example());
        }
    }
}

namespace ExtensionMethods
{
    public static class Extensions
    {
        public static string Example(this string s)
        {
            return "Hello " + s;
        }
    }
}

Adding the static using statement still allows extension methods to be used as normal, however you cannot call the extension method directly this way. If you want to call the extension method directly, you must add the class (& potentially the namespace) in front of the method as you do with C# 5 and before.

using System;
using static ExtensionMethods.Extensions;

internal class StaticUsing
{
    public static void BrokenFlow()
    {
        Console.WriteLine(ExtensionMethods.Extensions.Example("Leslie")); // this works
        Console.WriteLine(Example("Robert")); // this does NOT work
        Console.WriteLine("Robert".Example()); // this works
    }
}
Tags: 

Pages

dontclickme