Developing on Staxmanade

C# 4.0 Optional Parameters – Exploration.

{… Removed big long story about how I ended up writing this post which provides no value to the blog…}

Summary of big long story to at least give a little context as to why (yet another post on optional parameters):

I threw an idea out to the Moq discussion group of how we could use the named/optional parameters in a future version of Moq. (you can read the thread here) In my original feature request I displayed my lack of concrete knowledge in the named/optional parameters support that is eventually coming with .net 4.0.

Once I learned that you could place default values on interfaces it left me with questions… So, what better way to figure them out? Go test it…

Disclaimer: (Shouldn’t every blog have some context enlightening disclaimer?)
I haven’t looked up best practices or lessons learned from people that have had this language feature (VB), so I’m just doing this as an experiment for myself. Hope some of my findings help the other C#’ers wanting to learn a little about the feature.

What are optional parameters?

DimeCasts.Net, Derik Whittaker has a nice intro video # 153 - Exploring .Net 4 Features - Named and Optional Parameters

OR check out - http://tinyurl.com/yz3pc9o

 

Can an interface define a default value?

Yes!
image

 

Can I specify a default in the concrete implementation, if the interface has a default also?

Yes!

image

What happens when the concrete implementation has a different default value than the interface’s default?

If the interface has a default value specified, that is different from the concrete implementation, then it depends on what reference you’re using when executing the method.

image

In the case below we are executing the method directly off of the Foo instance and will therefore get the concrete implementation’s default value when executing.

(new Foo()).Bar() – would use the value of ‘1000’.

And in the case below we cast the Foo instance to an IFoo and it will then use the interfaces default value when executing.

((IFoo) new Foo()).Bar() – would use the value of ‘1’.

Below are some examples of the different use cases.

[TestClass]
public class UnitTest1
{
[TestMethod]
public void Should_get_the_concrete_class_default_value()
{
Foo f1 = new Foo();
f1.Bar();
f1.ParamValue.ShouldBeEqualTo(1000);
}

[TestMethod]
public void Should_get_the_interface_default_value()
{
IFoo f = new Foo();
f.Bar();
f.ParamValue.ShouldBeEqualTo(1);
}

[TestMethod]
public void Should_get_the_interface_default_value_because_of_explicit_cast()
{
Foo f = new Foo();
((IFoo)f).Bar();
f.ParamValue.ShouldBeEqualTo(1);
}

[TestMethod]
public void Should_get_the_concrete_class_default_value_because_of_explicit_cast()
{
IFoo f = new Foo();
((Foo)f).Bar();
f.ParamValue.ShouldBeEqualTo(1000);
}
}

interface IFoo
{
int ParamValue { get; }

void Bar(int paramValue = 1);
}

class Foo : IFoo
{
public int ParamValue { get; private set; }
public void Bar(int paramValue = 1000)
{
ParamValue = paramValue;
}
}


 



The next experiment - Extract Interface.



Next I tried removing the IFoo interface that I’d created manually, because I wanted to exercise the “Extract Interface…” functionality, just to see how it dealt with the these defaults.



Luckily, there were no surprises. The interface it created was exactly (less spacing) the same as I originally had.



Although it didn’t display the default constant value in the dialog during creation, there was a hint that the method signature had a default by placing [] around the int resulting in “Bar([int])”.



image




Side Tool Issue: Can’t say I like how it forced me to put the interface in a different file, I guess it’s enforcing “best practice” here, but I prefer to do this later in the dev cycle than immediately (kind of like how R# allows you to place in the file next to the original class). #ToolGettingInWay





Optional Parameter Issue: One issues I see with this solution was the dirty/icky copy/paste feeling I got when extracting the interface – the default was copied from the class to the interface.




Possible solutions to the “dirty/icky copy/paste feeling” the extract interface gives.


(in no particular order of preference)




  • Place all defaults into a constant and reference the constant in both the interface and the concrete implementation(s).


  • Don’t place the defaults in the concrete implementation (only in the interface). As you should probably not be depending on the concrete implementation to begin with, you wouldn’t need it there (and wouldn’t even call it). This would also help in the case that there are multiple concrete implementation and having to sift through the code looking for all instances to updated defaults for could be very error prone.



On the surface named parameters seem like a very simple feature to the C# language. But after delving into the feature a little, I can see there are many complicated scenarios you can get your self caught up into.



As with anything…Use with care!