March 14, 2010

C# 4.0: Code might smell with Optional Parameters

      I wrote few tests to understand Named, and Optional Parameters, and also reflected them in reflector to see what happens behind the scene.Now let's think about their impact on code readability.

      These would be very useful in calling Office Automation API. We don't need to worry about passing Type.Missing to zillion arguments, we can now just pass necessary arguments with the use of Named parameters.

       I can see misuse as well. I won't be surprised to see functions with lengthy optional parameters. That brings me a question: How many parameters can a function  have? I often find myself  debating whether a parameter can be an instance field of current object, or some other object. Let's turn to an expert, and see what he thinks.

        Uncle Bob talked about functional arguments in his articles for InformIT, and there is a dedicated chapter in his book Clean Code.  He says in this article Avoid Too Many Arguments, "Functions should have a small number of arguments. No argument is best, followed by one, two, and three. More than three is very questionable and should be avoided with prejudice.".

Clean Code: A Handbook of Agile Software Craftsmanship         He also recommends to not to have boolean arguments, as this is an indication that a function is doing two things. He writes in this article Eliminate Boolean arguments  "Boolean arguments loudly declare that the function does more than one thing. They are confusing and should be eliminated.". "Arguments are hard. They take a lot of conceptual power.", Uncle Bob writes in the book Clean Code.Writing unit tests for function with many arguments will be harder. Tests should cover all possible combinations of the parameters.
Refactoring: Improving the Design of Existing Code
   
       If you end up having function with more arguments, you can try following refactorings. Related Refactorings: (The book refered in this catalog) 
Replace Parameter with Method
Replace Parameter with Explicit Methods
Replace Record with Data Class
Parameterize Method
Remove Parameter

C# 4.0: Reflecting Named & Optional Parameters

I wrote few tests to understand Named and Optional Parameters. Now it's time to reflect.
I wrote few tests to see the what happens behind the scene.

Tests.
1. Reflecting Optional with default values
public void MethodWithDefaultValue(string name ="N/A", int cound =50)
 {
 
 }
gets converted to
public void MethodWithDeffaults([Optional, DefaultParameterValue("N/A")] string name, 
[Optional, DefaultParameterValue(50)] int count) 
{
}
You can declare a method using Optional Attribute, which allows you to have optional reference objects.

2. Reflecting Optional parametes when ommitted : When above method is called
public void ReflectMethodSignatureForOptionalWithDefaultValue()
{
ReflectClass optionalWithDefaultValue = new ReflectClass();
optionalWithDefaultValue.MethodWithDefaults();
}
gets converted to
public void ReflectMethodSignatureForOptionalWithDefaultValue()
{
new ReflectClass().MethodWithDefaults("N/A", 50);
}
Default values for the optional arguments are injected in the caller code.

3. Reflecting Named Parameters: if method in Test 1 called with named parameters,
public void ReflectNamedParametersCallerCode()
{
   ReflectClass optionalWithDefaultValue = new ReflectClass();
  optionalWithDefaultValue.MethodWithDefaults(count: 20, name: "Hello");
}
gets converted to
public void ReflectNamedParametersCallerCode() 
{ 
 ReflectClass optionalWithDefaultValue = new ReflectClass();
 int CS$0$0000 = 20;
string CS$0$0001 = "Hello";
optionalWithDefaultValue.MethodWithDefaults(CS$0$0001, CS$0$0000);
}
In the caller code,  A  local variable is created for every named parameter.
Complete Listing: In case you want to see yourself in reflector. Mouse hover on the listing to see clipboard/view source options.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace episodes
{
    
    public  class ReflectingNamedOptionalParams
    {
        public void ReflectMethodSignatureForOptionalWithDefaultValue()
        {
            ReflectClass optionalWithDefaultValue = new ReflectClass();
            optionalWithDefaultValue.MethodWithDefaults();
        }
        public void ReflectNamedParametersCallerCode()
        {
            ReflectClass optionalWithDefaultValue = new ReflectClass();
            optionalWithDefaultValue.MethodWithDefaults(count: 20, name: "Hello");
        }
    }

    public class ReflectClass
    {
        public void MethodWithDefaults(string name="N/A",int count=50)
        {
            
        }
    }
}

C# 4.0: Optional Parameters

C# 4.0 introduces optional parameters along with named parameters. A method can have optional parameters, which allows callers to skip them. A default value, which should be constant. can be specified for a optional parameter as part of the method declaration. When caller omits a optional parameter, a default value will be passed to the method. With the use of Named parameters syntax, you can skip some optional parameters in the order.  This is very useful when calling a COM interop assembly like Office integration.  I wrote few tests to understand these concepts. I wrote few tests on Named Parameters.
Note: VS2010, and NUnit is required to use this code as is in your machine.Mouse hover on the listing, you will see options like copy to clipboard,view source.

Tests:
TestOptionalAttribute
SkipOptionalParameters
SkipOrderWithTheUseOfNamedParmeters
TestOverloadResolution

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using NUnit.Framework;

namespace LearningCSharp40
{
    public class ParamArg
    {

    }
    public class UseOfOptionalAttribute
    {
        public string StringValue { get; set; }
        public UseOfOptionalAttribute([Optional]string param, [Optional] bool flag)
        {
            StringValue = param;
            BooleanValue = flag;
        }

        public ParamArg OptionalObjects([Optional]ParamArg arg, [Optional] ParamArg arg2)
        {
            return arg;
        }

        public bool BooleanValue { get; set; }
    }

    public class OptionalParamWithDefaultValue
    {
        public string StringValue { get; set; }
        public OptionalParamWithDefaultValue(string param)
            : this(param, true)
        {

        }

        public OptionalParamWithDefaultValue(string param = "N/A", bool flag = true, string name = "hi")
        {
            StringValue = param;
            BooleanValue = flag;
            Name = name;
        }
        public string Name { get; set; }
        public bool BooleanValue { get; set; }
    }

    [TestFixture]
    class LearnOptionalParams
    {
        //.NET reflector: When used with Optional attribute, compiler does substitute the default values of the types.
        [Test]
        public void TestOptionalAttribute()
        {
            UseOfOptionalAttribute useOfOptionalAttribute = new UseOfOptionalAttribute();
            Assert.AreEqual(useOfOptionalAttribute.StringValue, null);
            Assert.AreEqual(useOfOptionalAttribute.BooleanValue, false);
            Assert.IsNull(useOfOptionalAttribute.OptionalObjects(), "expecting null");
            Assert.IsNotNull(useOfOptionalAttribute.OptionalObjects(new ParamArg()), "expecting null");

        }

        [Test]
        public void SkipOptionalParameters()
        {

            OptionalParamWithDefaultValue optionalParamWithDefaultValue = new OptionalParamWithDefaultValue("Hello");
            Assert.AreEqual(optionalParamWithDefaultValue.StringValue, "Hello", "Expecting value passed in constructor");
            Assert.AreEqual(optionalParamWithDefaultValue.BooleanValue, true, "Expecting default value of flag");
            Assert.AreEqual(optionalParamWithDefaultValue.Name, "hi", "Expecting default value of Name");
        }

        [Test]
        public void SkipOrderWithTheUseOfNamedParmeters()
        {
            bool result = false;
            OptionalParamWithDefaultValue optionalParamWithDefaultValue = new OptionalParamWithDefaultValue(flag: result);
            Assert.AreEqual(optionalParamWithDefaultValue.StringValue, "N/A");
            Assert.AreEqual(optionalParamWithDefaultValue.BooleanValue, result);
        }




        [Test]
        public void TestOverloadResolution()
        {
            OverloadResolution overloadResolution = new OverloadResolution();
            Assert.AreEqual("NoArg", overloadResolution.Confused(), "Priority is for matching method signature: method with no arguments should be called");
            Assert.AreEqual("1Arg", overloadResolution.Confused("Hello"), "Method with one argument should be called");
        }
    }
    public class OverloadResolution
    {

        public string Confused()
        {
            return "NoArg";
        }

        public string Confused(string stringValue)
        {
            return "1Arg";
        }

        public string Confused(string stringValue = "N/A", bool flag = true)
        {
            return "2Arg";
        }

    }

}




March 9, 2010

C# 4.0: Named Parameters

C# 4.0: Named Parameters : C# introduces Named parameters. While calling a method, name: value pair syntax can be used for the parameters. Order doesn't matter. As you might guess, Named parameters can't precede optional parameters. I wrote few tests to understand these concepts.
You need VS2010, and NUnit if you want to try this code as is on your machine.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;

namespace LearningCSharp40
{
    public class Division
    {
        public int Divide(int dividend, int divisor)
        {
            return dividend / divisor;
        }
    }
    [TestFixture]
    public class LearnNamedParameters
    {
        Division division = new Division();
        private int quotientFromPositional = 0;
            
        [SetUp]
        public void Setup()
        {
            quotientFromPositional = division.Divide(20, 10); // positional
        }

        [Test]
        public void  QuotientShouldbeSameWhenCalledWithNamedParameters()
        {
            int quotientWithNamedParams = division.Divide(dividend: 20, divisor: 10); // named parameters
            Assert.AreEqual(quotientFromPositional, quotientWithNamedParams, 
                            "quotient should be 2 even with named parameters");
       
        }
        
        [Test]
        public void NamedParametersInDifferentOrder()
        {
            int quotientWithNamedParamsInDifferentOrder = division.Divide( dividend:20,divisor:10); // Order is different
            Assert.AreEqual(quotientFromPositional, quotientWithNamedParamsInDifferentOrder, 
                            "quotient should be same even called with named paratmeters in different parameter");
        }

       
        [Test]
        public void QuotientShouldBeSameWhenMixOfPositionalAndNamedApproachIsUsed()
        {
            int quotient = division.Divide( 20, divisor: 10); 
            Assert.AreEqual(quotientFromPositional, quotient,
                            "quotient should be same even with mix of positional, and named parameters");

        }
        [Test]
        [Ignore]
        public void NamedParametersCantProcedePosition_CompilerError()
        {
            Division division = new Division();
            //Uncomment to see the compiler error...
            // int quotient = division.Divide( dividend: 20,10); // named parameters cannot procede positional
            // Assert.AreEqual(2, quotient, "quotient should be 2 even with named parameters");
        }

        [Test]
        [Ignore]
        public void NamedParameterSpecifiesAParameterForWhichAPositionalArgumentHasAlreadyGiven_CompilerError()
        {
            Division division = new Division();
            //Uncomment to see the compiler error...
           // int quotient = division.Divide( 20,dividend:20); 
        }
    }
}

March 1, 2010

.NET 4.0: New API



.NET 4.0 adds several new methods. 
1) Enum.HasFlag  checks  whether a flag field present in the enum instance.


[Flags]
public enum PizzaToppings
{
Onions = 1,
Mushrooms = 2,
Chicken = 4
}
PizzaToppings myToppings = PizzaToppings.Onions |
PizzaToppings.Mushrooms;
 
Assert.IsFalse( myToppings.HasFlag(PizzaToppings.Chicken),
"Chicken is not expected"); 
2) String.IsNullOrWhiteSpace can be useful when whitespace condition needs to be checked along with Empty, and Null conditions.


[Test]
public void TestIsNullOrWhiteSpace()
{
string s = " \t ";
Assert.IsTrue( string.IsNullOrWhiteSpace(s),
"tabbed white spaces");
Assert.IsTrue(string.IsNullOrWhiteSpace(null), "Null");
Assert.IsTrue(string.IsNullOrWhiteSpace(""), "Empty");
Assert.IsTrue(string.IsNullOrEmpty(" \t ".Trim()));
}