Click here to Skip to main content
15,879,535 members
Articles / Programming Languages / C#

Determine Whether a C# Optional Parameter was Supplied

Rate me:
Please Sign up or sign in to vote.
4.14/5 (13 votes)
28 Jan 2014CPOL1 min read 39.8K   14   25
Determine whether a C# optional parameter was supplied

Introduction

Using the following generic C# structure, a developer will be able to determine whether the optional argument value was supplied or default value has been chosen.

Background

I was designing a data access layer to call database procedures. In stored procedures, we can have default parameters and my problem was to find a way to avoid passing parameters when developer chooses to use default parameter. Imagine the following Oracle stored procedure definition:

SQL
PROCEDURE GetEmployee (employeeId IN NUMBER,
   departmentName IN VARCHAR DEFAULT '%',
   departmentId IN NUMBER DEFAULT 100) AS ...

My first impression was the best way to write a mapping function is:

C#
public Employee GetEmployee(int employeeId,
    string departmentName = "%", int departmentId = 100)
{
    ...
    command.Parameters.Add("employeeId", employeeId);
    command.Parameters.Add("departmentName", departmentName);
    command.Parameters.Add("departmentId", departmentId);
    ...
} 

But this has two problems. First, you are providing something to the DbCommand which is not necessary and the second and more important problem is if a database developer changes the default value in the procedure's definition, C# is still passing the same old value which is not the default value anymore. So I thought I could solve the problem by doing something like this:

C#
public Employee GetEmployee(int employeeId,
    string departmentName = default(string), int departmentId = default(int))
{
    ...
    command.Parameters.Add("employeeId", employeeId);
    if (departmentName != default(string))
    {
    	command.Parameters.Add("departmentName", departmentName);
    }
    if (departmentId != default(int))
    {
        command.Parameters.Add("departmentId", departmentId);
    }
    ...
} 

This was a little bit better and if we don't supply value to optional parameters everything works fine as expected. But we will not be able to pass some values to the procedure using this method. In C# default(int) equals to zero and default(string) is null. So if we need to pass null for departmentName or zero for the departmentId, method body will ignore them and procedure will be executed using departmentName = '%' and departmentId = 100 instead of departmentName = NULL and departmentId = 0.

Redefining C# method using Optional<T> generic types will give us exactly what we want:

C#
public Employee GetEmployee(int employeeId,
    Optional<string> departmentName = default(Optional<string>),
    Optional<int> departmentId = default(Optional<int>))
{
    ...
    command.Parameters.Add("employeeId", employeeId);
    if (departmentName.HasValue)
    {
    	command.Parameters.Add("departmentName", departmentName.Value);
    }
    if (departmentId.HasValue)
    {
        command.Parameters.Add("departmentId", departmentId.Value);
    }
    ...
}

This is useful especially when we want to create proxy functions like what we do in ORM libraries.

Optional<T> Structure

C#
public struct Optional<T>
{
    public Optional(T value)
    {
        _value = value;
        _hasValue = true;
    }

    public static explicit operator T(Optional<T> optional)
    {
        return optional._value;
    }
    
    public static implicit operator Optional<T>(T value)
    {
        return new Optional<T>(value);
    }

    T _value;
    public T Value
    {
        get { return _value; }
    }
    
    bool _hasValue;
    public bool HasValue
    {
        get { return _hasValue; }
    }

    public override string ToString()
    {
        return string.Format("Optional{ HasValue: {0}, Value: '{1}')", HasValue, Value);
    }
} 

Usage

C#
void Main()
{
    TestFunction("required value",
        1234,
        0,
        arg05: "string value",
        arg06: string.Empty,
        arg08: ConsoleColor.Red,
        arg10: 4,
        arg11: "object value",
        arg12: ConsoleColor.Blue,
        arg13: default(Optional<object>), // this is an exception and will be considered as not supplied 
        arg14: true);
}

public void TestFunction(
    string arg01,
    Optional<int> arg02 = default(Optional<int>),
    Optional<int> arg03 = default(Optional<int>),
    Optional<int> arg04 = default(Optional<int>),
    Optional<string> arg05 = default(Optional<string>),
    Optional<string> arg06 = default(Optional<string>),
    Optional<string> arg07 = default(Optional<string>),
    Optional<ConsoleColor> arg08 = default(Optional<ConsoleColor>),
    Optional<ConsoleColor> arg09 = default(Optional<ConsoleColor>),
    Optional<object> arg10 = default(Optional<object>),
    Optional<object> arg11 = default(Optional<object>),
    Optional<object> arg12 = default(Optional<object>),
    Optional<object> arg13 = default(Optional<object>),
    Optional<object> arg14 = default(Optional<object>),
    Optional<bool> arg15 = default(Optional<bool>))
{
    Console.WriteLine("// arg01: {0}", arg01);
    Console.WriteLine("// arg02: {0}", arg02);
    Console.WriteLine("// arg03: {0}", arg03);
    Console.WriteLine("// arg04: {0}", arg04);
    Console.WriteLine("// arg05: {0}", arg05);
    Console.WriteLine("// arg06: {0}", arg06);
    Console.WriteLine("// arg07: {0}", arg07);
    Console.WriteLine("// arg08: {0}", arg08);
    Console.WriteLine("// arg09: {0}", arg09);
    Console.WriteLine("// arg10: {0}", arg10);
    Console.WriteLine("// arg11: {0}", arg11);
    Console.WriteLine("// arg12: {0}", arg12);
    Console.WriteLine("// arg13: {0}", arg13);
    Console.WriteLine("// arg14: {0}", arg14);
    Console.WriteLine("// arg15: {0}", arg15);
} 

Runtime Results

C#
// arg01: required value
// arg02: Optional { HasValue: True, Type: [System.Int32], Value: '1234' }
// arg03: Optional { HasValue: True, Type: [System.Int32], Value: '0' }
// arg04: Optional { HasValue: False, Type: [System.Int32], Value: '0' }
// arg05: Optional { HasValue: True, Type: [System.String], Value: 'string value' }
// arg06: Optional { HasValue: True, Type: [System.String], Value: '' }
// arg07: Optional { HasValue: False, Type: [System.String], Value: '' }
// arg08: Optional { HasValue: True, Type: [System.ConsoleColor], Value: 'Red' }
// arg09: Optional { HasValue: False, Type: [System.ConsoleColor], Value: 'Black' }
// arg10: Optional { HasValue: True, Type: [System.Int32], Value: '4' }
// arg11: Optional { HasValue: True, Type: [System.String], Value: 'object value' }
// arg12: Optional { HasValue: True, Type: [System.ConsoleColor], Value: 'Blue' }
// arg13: Optional { HasValue: False, Type: [System.Object], Value: '' }
// arg14: Optional { HasValue: True, Type: [System.Boolean], Value: 'True' }
// arg15: Optional { HasValue: False, Type: [System.Boolean], Value: 'False' }

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionOption Pin
Sacha Barber26-Nov-16 11:37
Sacha Barber26-Nov-16 11:37 
GeneralMy vote of 5 Pin
D V L9-Sep-15 23:17
professionalD V L9-Sep-15 23:17 
SuggestionCorrection Pin
Richard Deeming4-Feb-14 9:22
mveRichard Deeming4-Feb-14 9:22 
GeneralRe: Correction Pin
Ali Malekpour28-Feb-14 7:35
Ali Malekpour28-Feb-14 7:35 
GeneralMy vote of 4 Pin
Paulo Zemek28-Jan-14 13:58
mvaPaulo Zemek28-Jan-14 13:58 
GeneralRe: My vote of 4 Pin
Middle Manager3-Feb-14 6:37
Middle Manager3-Feb-14 6:37 
GeneralRe: My vote of 4 Pin
Jasmine25013-Feb-14 10:29
Jasmine25013-Feb-14 10:29 
QuestionI am impressed. Pin
Paulo Zemek28-Jan-14 7:37
mvaPaulo Zemek28-Jan-14 7:37 
AnswerRe: I am impressed. Pin
Ali Malekpour28-Jan-14 10:59
Ali Malekpour28-Jan-14 10:59 
QuestionI am puzzled Pin
GuyThiebaut28-Jan-14 6:07
professionalGuyThiebaut28-Jan-14 6:07 
AnswerRe: I am puzzled Pin
Ali Malekpour28-Jan-14 6:50
Ali Malekpour28-Jan-14 6:50 
GeneralRe: I am puzzled Pin
GuyThiebaut28-Jan-14 21:52
professionalGuyThiebaut28-Jan-14 21:52 
AnswerRe: I am puzzled Pin
Jasmine25013-Feb-14 10:27
Jasmine25013-Feb-14 10:27 
QuestionOK, so it's still not a problem... Pin
Dave Kreskowiak27-Jan-14 17:25
mveDave Kreskowiak27-Jan-14 17:25 
GeneralMy vote of 3 Pin
Matt T Heffron27-Jan-14 9:35
professionalMatt T Heffron27-Jan-14 9:35 
GeneralRe: My vote of 3 Pin
Ali Malekpour27-Jan-14 16:43
Ali Malekpour27-Jan-14 16:43 
QuestionWhat's the problem you're trying to solve? Pin
John Brett26-Jan-14 21:48
John Brett26-Jan-14 21:48 
AnswerRe: What's the problem you're trying to solve? Pin
Ali Malekpour27-Jan-14 16:40
Ali Malekpour27-Jan-14 16:40 
GeneralRe: What's the problem you're trying to solve? Pin
John Brett28-Jan-14 1:08
John Brett28-Jan-14 1:08 
AnswerRe: What's the problem you're trying to solve? Pin
Paulo Morgado3-Feb-14 0:19
professionalPaulo Morgado3-Feb-14 0:19 
QuestionWhat is the added value above default(T) and or Nullable<T> ? Pin
JV999926-Jan-14 20:33
professionalJV999926-Jan-14 20:33 
AnswerRe: What is the added value above default(T) and or Nullable<T> ? Pin
Ali Malekpour27-Jan-14 16:28
Ali Malekpour27-Jan-14 16:28 
GeneralRe: What is the added value above default(T) and or Nullable<T> ? Pin
JV999927-Jan-14 21:06
professionalJV999927-Jan-14 21:06 
QuestionMore context needed Pin
JP van Mackelenbergh26-Jan-14 20:04
JP van Mackelenbergh26-Jan-14 20:04 
AnswerRe: More context needed Pin
Ali Malekpour27-Jan-14 16:32
Ali Malekpour27-Jan-14 16:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.