Click here to Skip to main content
Click here to Skip to main content

Tagged as

Determine Whether a C# Optional Parameter was Supplied

, 28 Jan 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
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:

 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:

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:

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:

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

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

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

// 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)

Share

About the Author

Ali Malekpour
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
SuggestionCorrection PinprofessionalRichard Deeming4-Feb-14 10:22 
GeneralRe: Correction PinmemberAli Malekpour28-Feb-14 8:35 
GeneralMy vote of 4 PinprofessionalPaulo Zemek28-Jan-14 14:58 
GeneralRe: My vote of 4 PinmemberEd Bouras3-Feb-14 7:37 
GeneralRe: My vote of 4 PinmemberJasmine25013-Feb-14 11:29 
QuestionI am impressed. PinprofessionalPaulo Zemek28-Jan-14 8:37 
AnswerRe: I am impressed. PinmemberAli Malekpour28-Jan-14 11:59 
QuestionI am puzzled PinmemberGuyThiebaut28-Jan-14 7:07 
AnswerRe: I am puzzled PinmemberAli Malekpour28-Jan-14 7:50 
GeneralRe: I am puzzled PinmemberGuyThiebaut28-Jan-14 22:52 
AnswerRe: I am puzzled PinmemberJasmine25013-Feb-14 11:27 
QuestionOK, so it's still not a problem... [modified] PinmvpDave Kreskowiak27-Jan-14 18:25 
GeneralMy vote of 3 PinprofessionalMatt T Heffron27-Jan-14 10:35 
GeneralRe: My vote of 3 PinmemberAli Malekpour27-Jan-14 17:43 
QuestionWhat's the problem you're trying to solve? PinmemberJohn Brett26-Jan-14 22:48 
AnswerRe: What's the problem you're trying to solve? PinmemberAli Malekpour27-Jan-14 17:40 
GeneralRe: What's the problem you're trying to solve? PinmemberJohn Brett28-Jan-14 2:08 
AnswerRe: What's the problem you're trying to solve? PinmemberPaulo Morgado3-Feb-14 1:19 
QuestionWhat is the added value above default(T) and or Nullable<T> ? PinprofessionalJV999926-Jan-14 21:33 
AnswerRe: What is the added value above default(T) and or Nullable<T> ? PinmemberAli Malekpour27-Jan-14 17:28 
GeneralRe: What is the added value above default(T) and or Nullable<T> ? [modified] PinprofessionalJV999927-Jan-14 22:06 
QuestionMore context needed PinmemberJP van Mackelenbergh26-Jan-14 21:04 
AnswerRe: More context needed PinmemberAli Malekpour27-Jan-14 17:32 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141220.1 | Last Updated 28 Jan 2014
Article Copyright 2014 by Ali Malekpour
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid