Question

EDIT:
If you got here looking for the fast answer: It is possible to do this, but the overview of the function does not show the result I was looking for.

The answer 'Code Contracts' by Aidin gives an error if you omit one of the optional, but required (because...) parameters. This seems to be the easiest way to force it upon your user, but requires some explanation to the user of the function.

The answer 'Place Logic in Class' by Shawn Holzworth fitted my need of easily showing the user of the function what options they have, but it requires a different approach and a lot of code rewriting.


I'm trying to create a function that accepts different sets of parameters. In the code I check what set of parameters are given and based on that I do something to the URL in order to receive a set of values containing personal information from insurance companies (that is why there is no other code). A search on Google gave no results on making the parameters conditional on each other, therefore I'm not sure it is even possible.
Basically the idea is to have a function call like this:

  dobFormatted
, infodateFormatted
, (BSN | insuranceID | lastname | postalcode, Homenummer[, Homenummeradd])
[, insuranceType]

Allowing the function to be used in these ways:

  • dobFormatted, infodateFormatted, BSN
  • dobFormatted, infodateFormatted, BSN, insuranceType
  • dobFormatted, infodateFormatted, insuranceID
  • dobFormatted, infodateFormatted, insuranceID, insuranceType
  • dobFormatted, infodateFormatted, lastname
  • dobFormatted, infodateFormatted, lastname, insuranceType
  • dobFormatted, infodateFormatted, postalcode, Homenummer
  • dobFormatted, infodateFormatted, postalcode, Homenummer, insuranceType
  • dobFormatted, infodateFormatted, postalcode, Homenummer, Homenummeradd
  • dobFormatted, infodateFormatted, postalcode, Homenummer, Homenummeradd, insuranceType

So the code forces the call to the function to contain the required information. I'm aware that this could be achieved by creating multiple versions of the function requiring different sets of parameters and returning the output of the current function. However I think that would make the code less readable and maintainable. Besides, there are also 3 ways of completing the function using only 3 strings, but the output depends on the 3rd parameter.


Currently I have this code:

public static string getVecozoURL(string dobFormatted, string infodateFormatted, 
        string BSN = "", string insuranceID= "", string lastname= "",
        string postalcode = "", int Homenummer = 0, string Homenummeradd = "",
        string insuranceType = "Both") {
    string URL= "";
    if (FCelfproef.BSN(BSN)) {
        // Do stuff
    } else if (!insuranceID.IsEmpty()) {
        // Do stuff
    } else if (postalcode .Length > 4 && Homenummer > 0) {
        // Do stuff
    } else if (!lastname.IsEmpty()) {
        // Do stuff
    }
    // Do some other stuff
    return URL;
}

Is there a easy way to force this or do I have to inform all people that are going to use this about the way the function works?

Was it helpful?

Solution 3

All of these solutions are great as long as you are open to overload your functions. I would personally overload the function; however, if you persist to use only one function then I highly recommend you to use Code Contracts to enforce any logic you desire to any combinations of parameters. Here is an example:

public string XYZ(string dobFormatted, string infodateFormatted, 
        string BSN = "", string insuranceID= "", string lastname= "",
        string postalcode = "", int Homenummer = 0, string Homenummeradd = "",
        string insuranceType = "Both")
{
    Contract.Requires(!string.IsNullOrEmpty(dobFormatted) || insuranceType == "NONE");    
}

You can have as many as Requires() functions as you want. You can enable the static check for Code Contracts and compiler can warn you when you are jeopardizing your constrains.

Visit http://msdn.microsoft.com/en-us/library/dd264808.aspx for more information.

Remember you can use Code Contracts even in your overloaded functions.

OTHER TIPS

You can create method signatures with separate overloads in C sharp

public void TestMethod(string stringValue, int intValue)
public void TestMethod(double doubleValue, int intValue)
public void TestMethod(string stringValue, string otherStringValue)   
public void TestMethod(string stringValue, int intValue, string otherStringValue)

These could all be called as separate methods.

Overloads


Also in C sharp you can use optional parameters.

public void TestMethod(int intValue
                      ,int optionalIntValue = 0
                      ,int optionalSecondValue = 4)

where you could call like

TestMethod(1) 
//optionalValue = 0(default) & optionalSecondValue = 4(default)

TestMethod(1, optionalSecondValue:2) 
//optionalValue = 0(default) & optionalSecondValue = 2

TestMethod(1, 12) 
//optionalValue = 12 & optionalSecondValue = 4(default)

Because the method parameters share types overloading a single method is going to run into trouble. For those to be the only allowable calls you'll need separate methods for each.

public static string getVecozoURL_BSN(string dobFormatted, string infodateFormatted, string BSN, string insuranceType = "Both")
public static string getVecozoURL_InsurranceID(string dobFormatted, string infodateFormatted, string insuranceID, string insuranceType = "Both")
public static string getVecozoURL_Lastname(string dobFormatted, string infodateFormatted, string lastname, string insuranceType = "Both")
public static string getVecozoURL_Postalcode(string dobFormatted, string infodateFormatted, string postalcode, int Homenummer, string Homenummeradd = "", string insuranceType = "Both")

However since you're concerned with readability/maintainability, it may make more sense to encapsulate the logic in it's own class. Something like:

class VecozoURLFormater
{
    public string DobFormatted { get; private set; }
    public string InfodateFormatted { get; private set; }
    public string InsuranceType { get; private set; }
    public VecozoURLFormater(string dobFormatted, string infodateFormatted, string insuranceType = "Both")
    {
        DobFormatted = dobFormatted;
        InfodateFormatted = infodateFormatted;
        InsuranceType = insuranceType;
    }

    public string FromBSN(string BSN){/*...*/}
    public string FromInsurranceID(string insuranceID){/*...*/}
    public string FromLastname(string lastname){/*...*/}
    public string FromPostalcode(string postalcode, int Homenummer, string Homenummeradd = "") {/*...*/}
}

You can redesign your method to take in an object of a class that you define containing all this info, or you can create multiple methods with different parameters, basically either overload it or different methods.

I do not think that you could have conditional parameters based on the value of another parameter, as in if this parameter is there, then don't need the other, or so on...

Chaining together overloads of the method is your answer. This will allow everyone to manage it how they'd like.

I have returned the string of the chain in the example below, but you could easily add logic into each method so that you didnt have a nasty if statement at the end.

    public static string getVecozoURL(string dobFormatted, string infodateFormatted, string BSN)
    {
        return getVecozoURL(dobFormatted, infodateFormatted, BSN, "", "", "", 0, "", "Both");
    }

    public static string getVecozoURL(string dobFormatted, string infodateFormatted, string BSN, string insuranceID)
    {
        return getVecozoURL(dobFormatted, infodateFormatted, BSN, insuranceID, "", "", 0, "", "Both");
    }

    public static string getVecozoURL(string dobFormatted, string infodateFormatted, string BSN, string postalcode, int Homenummer)
    {
        return getVecozoURL(dobFormatted, infodateFormatted, BSN, "", "", postalcode, Homenummer, "", "Both");
    }

    public static string getVecozoURL(string dobFormatted, string infodateFormatted,
    string BSN, string insuranceID, string lastname,
    string postalcode, int Homenummer, string Homenummeradd,
    string insuranceType)
    {
        string URL = "";
        if (FCelfproef.BSN(BSN))
        {
            // Do stuff
        }
        else if (!insuranceID.IsEmpty())
        {
            // Do stuff
        }
        else if (postalcode.Length > 4 && Homenummer > 0)
        {
            // Do stuff
        }
        else if (!lastname.IsEmpty())
        {
            // Do stuff
        }
        // Do some other stuff
        return URL;
    }

Try this approach:

public enum VecozoOverloadedOptions { BSN, InsuranceID, LastName, PostalCode };

public class VecozoValues
{
    public string DobFormatted;
    public string infodateFormatted;
    public VecozoOverloadedOptions OverloadChoice;
    public string OverloadedValue;
    public int Homenummer;
    public string Homenummeradd;
    public string InsuranceType;
}

...

public static string getVecozoURL(VecozoValues data)
{
   ...

This type of problem is often best solved with a fluent syntax:

public class VecozoURLBuilder
{
    private string _dobFormatted;
    private string _infodateFormatted;
    private string _bsn;
    private string _insuranceID;
    ...

    public VecozoURLBuilder DobFormatted(string dobFormatted)
    {
        _dobFormatted = dobFormatted;
        return this;
    }

    public VecozoURLBuilder InfodateFormatted(string infodateFormatted)
    {
        _infodateFormatted= nfodateFormatted;
        return this;
    }
    ...

    public string VecozoURL()
    {
        // create string from dobFormatted  etc.
    }
}

It is then used like this:

var url = new VecozoURLBuilder()
    .DobFormatted("xxx")
    .InfodateFormatted("yyy")
    .VecozoURL();

Obviously, the full thing would have fields for all the required values in the question and fluent methods for all the fields.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top