March 14, 2010

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";
        }

    }

}