Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# optional parameter besides null for class parameter?

Tags:

c#

.net

What is the best solution to this problem? I'm trying to create a function that has several optional parameters of class types for which null is a meaningful value and cannot be used as a default. As in,

public void DoSomething(Class1 optional1, Class2 optional2, Class3 optional3)
    {
        if (! WasSpecified(optional1)) { optional1 = defaultForOptional1; }
        if (! WasSpecified(optional2)) { optional2 = defaultForOptional2; }
        if (! WasSpecified(optional3)) { optional3 = defaultForOptional3; }

        // ... do the actual work ...
    }

I can't use Class1 optional1 = null because null is meaningful. I can't use some placeholder class instance Class1 optional1 = defaultForOptional1 because of the compile-time constant requirement for these optional parameters I've come up with the following options:

  1. Provide overloads with every possible combination, which means 8 overloads for this method.
  2. Include a Boolean parameter for each optional parameter indicating whether or not to use the default, which I clutters up the signature.

Has anyone out there come up with some clever solution for this?

Thanks!

edit: I ended up writing a wrapper class for so I didn't have to keep repeating Boolean HasFoo.

    /// <summary>
    /// A wrapper for variables indicating whether or not the variable has
    /// been set.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public struct Setable<T>
    {
        // According to http://msdn.microsoft.com/en-us/library/aa288208%28v=vs.71%29.aspx,
        // "[s]tructs cannot contain explicit parameterless constructors" and "[s]truct
        // members are automatically initialized to their default values."  That's fine,
        // since Boolean defaults to false and usually T will be nullable.

        /// <summary>
        /// Whether or not the variable was set.
        /// </summary>
        public Boolean IsSet { get; private set; }

        /// <summary>
        /// The variable value.
        /// </summary>
        public T Value { get; private set; }

        /// <summary>
        /// Converts from Setable to T.
        /// </summary>
        /// <param name="p_setable"></param>
        /// <returns></returns>
        public static implicit operator T(Setable<T> p_setable)
        {
            return p_setable.Value;
        }

        /// <summary>
        /// Converts from T to Setable.
        /// </summary>
        /// <param name="p_tee"></param>
        /// <returns></returns>
        public static implicit operator Setable<T>(T p_tee)
        {
            return new Setable<T>
            {
                IsSet = true
              , Value = p_tee
            };
        }
    }
like image 804
Don 01001100 Avatar asked May 23 '12 20:05

Don 01001100


1 Answers

I would at least consider creating a new type for the parameter:

public void DoSomething(DoSomethingOptions options)

... where DoSomethingOptions could look like this:

public class DoSomethingOptions
{
    private Class1 class1;
    public bool HasClass1 { get; private set; }

    public Class1 Class1 
    {
        get { return class1; }
        set
        {
            class1 = value;
            HasClass1 = true;
        }
    }

    ... for other properties ...
}

Then you can call it with:

DoSomething(new DoSomethingOptions { Class1 = null, Class2 = new Class2() });

You don't end up with an exponential set of overloads, and you can still call it reasonably compactly.

This is similar to the approach that Process takes with ProcessStartInfo.

like image 158
Jon Skeet Avatar answered Sep 30 '22 06:09

Jon Skeet