2009-11-07

Optionelle parametre i C#4.0

Introduktion

I den foregående blogpost har jeg skrevet om dynamics i C#4.0. I denne post vil jeg skrive om optionelle parametere og se på, hvordan de kan bruges - både i forhold til når man skal kommunikere med et api der indeholder metoder med optionelle parametre og når man vil bruge dem i sine egne metoder i C#

I denne post, har jeg udover nunit også anvendt moq til at lave kode eksempler. Jeg vil ikke komme nærmere ind på moq her, men det er et mocking framework der kan anvendes i kombination med et unittest framework, til at lave tests der ikke er afhængig af en bestemt implementering af logik.

Optionelle parametre

Optionelle parametre er en ting, som VB programmører har kunnet anvende i mange år. Helt enkelt gør det ud på, at man kan undlade parametre, hvis den metode man kalder har en defineret default værdi for parameteren.

Hvis man fx forestiller sig følgende interface til et api, der anvender optionelle parametre

public interface IApiFunctions
{
    void ApiFunction(int arg1, int arg2=1);
}

Så kan man som vist herunder, vælge at kalde metoden ApiFunction uden at angive en værdi for parameteren arg2

[Test]
public void GivenOnly1Paramter()
{
    var mock = new Moq.Mock<IApiFunctions>();
    IApiFunctions sut = mock.Object;

    Assert.DoesNotThrow(() => sut.ApiFunction(1));
}

Man kan selvfølgelig også angive begge parametrene, hvis man har behov for det

[Test]
public void GivenBothParamters()
{
    var mock = new Moq.Mock<IApiFunctions>();
    IApiFunctions sut = mock.Object;
    Assert.DoesNotThrow(() => sut.ApiFunction(1, 2));
}

Navngivne parametre

En anden mulighed som man også har, er at angive navnet på parametrene som man angiver. Hvis man forstiller sig at api'et indeholder endnu en metode der ligner den følgende

void ApiFunction2(int arg1, int arg2 = 1,string arg3="NaN");

Så kan man vælge at kalde den uden at angive arg2, ved at angive at det er arg3, som man

[Test]
public void UsingNamedParamters()
{
    var mock = new Moq.Mock<IApiFunctions>();
    IApiFunctions sut = mock.Object;
    Assert.DoesNotThrow(() => sut.ApiFunction2(1, arg3: "Mojn"));
}

Dynamics

Et lidt andet eksempel på optionelle parametre, som egentlig mere hører til under min sidste post om dynamics, er hvor man kalder en metode i et api, som har optionelle parametre. Jeg har som eksempel tilføjet følgende metode til det ironpython script, som jeg tidligere har anvendt.
def Add(arg1,arg2=2):
 return arg1+arg2
Som man kan se, så har denne metode en parameter arg2 som har en default værdi. Hvis jeg fx. prøver at kalde denne metode i en ironpython consol, så får jeg følgende resultat
IronPython 2.6 (2.6.10920.0) on .NET 2.0.50727.4200
Type "help", "copyright", "credits" or "license" for more information.
>>> def Add(arg1,arg2=2):
...     return arg1+arg2
...
>>> Add(3,1)
4
>>> Add(3)
5

Dvs at jeg kan kalde metoden, med 1 eller 2 parametre og få det forventede resultat. Hvis jeg forsøger at kalde metoden vha dynamics fra C#, får jeg som man kan se herunder samme resultat

[Test]
public void CanHandleOptionalParameterInIronPython()
{
    var py = Python.CreateRuntime();
    dynamic ironPython = py.UseFile("IronPythonFunctions.py");
    Assert.AreEqual(5, ironPython.Add(3));
}

[Test]
public void CanHandleOptionalParameterInIronPython()
{
    var py = Python.CreateRuntime();
    dynamic ironPython = py.UseFile("IronPythonFunctions.py");
    Assert.AreEqual(4, ironPython.Add(3,1));
}

Dvs at man via af dynamics kan udnytte andre sprogs mulighed for optionelle parametre.

Overloading

En anden situation, hvor man i nogen tilfælde, kan udnytte optionelle parametre, er når man laver 1 eller flere overloads af en metode, for at kunne kalde den uden at angive bestemte paramtere. Se fx. følgende klasse

class MethodOverloads
{
    public void DoStuff(int StuffID)
    {
        string name = "Stuff";
        DoStuff(StuffID, name);
    }

    public void DoStuff(int StuffID, string name)
    {
        double factor = 2;
        DoStuff(StuffID, name, factor);
    }

    public void DoStuff(int StuffID, string name, double factor)
    {
        /* DoStuff */
    }
}

Der er egentlig ikke noget i vejen med at lave ovenstående konstruktion af metoder, og med lidt kommentarer kunne man sagtens overskue hvilken af metoderne man skulle kalde. Men man kunne med optionelle parametre istedet skrive følgende

class MethodOverloads
{
   public void DoStuffAllInOne(int StuffID, string name="Stuff", double factor=2)
    {
        /* DoStuff */
    }
}

Her kan man med det samme, når man vil kalde metoden, se hvilke parametre man skal angive. Men det som den sidste løsning giver mulighed for, er at man også kan kalde metoden uden at angive parameteren name. For at kunne gøre dette i det klassiske eksempel, skulle man have lavet endnu en overload. Som man kan se i den følgende test, er der ingen forskel på, hvordan det ser ud når man kalder de to forskellige løsninger

[Test]
public void CallNormalMethod()
{
    MethodOverloads sut = new MethodOverloads();
    Assert.DoesNotThrow(() => sut.DoStuff(1));
    Assert.DoesNotThrow(() => sut.DoStuff(1,"Stuff"));
    Assert.DoesNotThrow(() => sut.DoStuff(1,"Stuff",2));
}

[Test]
public void CallAllInOneMethod()
{
    MethodOverloads sut = new MethodOverloads();exit()
    Assert.DoesNotThrow(() => sut.DoStuffAllInOne(1));
    Assert.DoesNotThrow(() => sut.DoStuffAllInOne(1, "Stuff"));
    Assert.DoesNotThrow(() => sut.DoStuffAllInOne(1, "Stuff", 2));
}

Der er sikkert mange meninger for og imod at bruge optionelle parametre, men det er ihverfald noget som der kan anvendes især, når man arbejder med ms office automation, hvor der typisk er mange optionelle parametre i de forskellige metoder i interfacet.

Næste post

I næste post vil jeg beskrive endnu en ny feature i C#4.0, eller dvs faktisk om to features nemlig Covariance og Contravariance.

Ingen kommentarer:

Tilføj en kommentar