Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Workaround for optional ref parameters in C#

I'm trying to write a method that takes references to boolean flags and modify them. The booleans are all declared separately (i.e. not in an indexable data structure) and the caller of the method should be able to decide which booleans are being modified.

Example code (this works):

class Program
{
    private static bool b1, b2, b3, b4, b5;

    private static void doSomething(ref bool setTrue, ref bool setFalse, ref bool invert)
    {
        setTrue = true;
        setFalse = false;
        invert = !invert;
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Pre: {0}, {1}, {2}, {3}, {4}", b1, b2, b3, b4, b5);
        doSomething(ref b1, ref b3, ref b5);
        Console.WriteLine("Post: {0}, {1}, {2}, {3}, {4}", b1, b2, b3, b4, b5);
    }
}

Output, as expected:

Pre: False, False, False, False, False
Post: True, False, False, False, True

So far, so good. Now these parameters should be optional on the method. That is, the caller can choose to e.g. use the setTrue and the invert effect, but not the setFalse one.

Basically, what I'd like to do is this:

doSomething(ref b1, null, ref b5); // error CS1503: Argument 2: cannot convert from '<null>' to 'ref bool'

And then declare the doSomething method like this:

private static void doSomething(ref bool setTrue, ref bool setFalse, ref bool invert)
{
    if(setTrue != null) setTrue = true;
    if(setFalse != null) setFalse = false;
    if(invert != null) invert = !invert;
}

Note that I do not want to check if the value is null. The values are real bools and can't be null (and declaring them as bool? doesn't really solve my problem). I only want to give the caller the ability to give null as the reference.

While the implementation of the method may be more complex, I'd really like to keep the invocation down to one line. (I.e. avoid having to declare temporary variables just for this call.)

One possibility would be to declare (eight) overloads for the function with all combinations of bools given or not, but then I need to come up with some scheme to make sure they all have unique signatures. (I'm stuck with C# 3.0, so no named parameters.)

Am I missing something? Is there a clean workaround? Currently the only (barely) acceptable alternative I can think of is to pass in strings with the variable names (or null) and then resolve these to the actual field using reflection.

PS: As you're probably wondering why I trying to do something this strange, some words of background: the doSomething method is part of a library. The invocations of doSomething are coming from generated C# code. And yes, having all these bools (~200 in the real project) as separate fields does make sense in the big picture, but the reasoning isn't really relevant for this question.

like image 635
Henrik Heimbuerger Avatar asked Jun 06 '11 09:06

Henrik Heimbuerger


People also ask

Can you have optional parameters in C?

Optional arguments are generally not allowed in C (but they exist in C++ and in Ocaml, etc...). The only exception is variadic functions (like printf ).

How do you pass optional parameters?

By Params Keyword: You can implement optional parameters by using the params keyword. It allows you to pass any variable number of parameters to a method. But you can use the params keyword for only one parameter and that parameter is the last parameter of the method.

Can out parameter be optional C#?

No. To make it "optional", in the sense that you don't need to assign a value in the method, you can use ref . A ref parameter is a very different use case.

Can parameters be optional?

Optional parameters are indicated by the Optional keyword in the procedure definition. The following rules apply: Every optional parameter in the procedure definition must specify a default value. The default value for an optional parameter must be a constant expression.


3 Answers

If you really want to have optional parameters, your only solution would be to use pointers, since they can be null, unlike ref.

private static unsafe void doSomething(bool* setTrue, bool* setFalse, bool* invert)
{
    if (setTrue  != null) *setTrue  = true;
    if (setFalse != null) *setFalse = false;
    if (invert   != null) *invert   = !*invert;
}

Uglyness all over. But hey, optional parameters !

like image 70
user703016 Avatar answered Oct 19 '22 00:10

user703016


UPDATE...

If you need to potentially manipulate all three fields in a single method call then I think you'll need to use reflection to do it relatively cleanly. This can, however, be done with some degree of type-safety using expression trees; you don't need to resort to passing in the field names as strings.

DoSomething(() => b1, () => b3, () => b5);
DoSomething(() => b1, null, () => b5);

// ...

public static void DoSomething(Expression<Func<bool>> trueFieldSelector,
                               Expression<Func<bool>> falseFieldSelector,
                               Expression<Func<bool>> invertFieldSelector)
{
    FieldInfo fieldInfo;
    object obj;

    if (GetInfo(trueFieldSelector, out fieldInfo, out obj))
        fieldInfo.SetValue(obj, true);

    if (GetInfo(falseFieldSelector, out fieldInfo, out obj))
        fieldInfo.SetValue(obj, false);

    if (GetInfo(invertFieldSelector, out fieldInfo, out obj))
        fieldInfo.SetValue(obj, !(bool)fieldInfo.GetValue(obj));
}

private static bool GetInfo(Expression<Func<bool>> fieldSelector,
                            out FieldInfo fieldInfo, out object obj)
{
    if (fieldSelector == null)
    {
        fieldInfo = null;
        obj = null;
        return false;
    }

    var me = fieldSelector.Body as MemberExpression;
    if (me == null)
        throw new ArgumentException("Select a field!", "fieldSelector");

    fieldInfo = me.Member as FieldInfo;
    if (fieldInfo == null)
        throw new ArgumentException("Select a field!", "fieldSelector");

    var ce = me.Expression as ConstantExpression;
    obj = (ce == null) ? null : ce.Value;

    return true;
}

Note that using reflection like this will be relatively slow. If it's not fast enough then you might need to dive a bit deeper into reflection, possibly using DynamicMethod to create delegates and then cache them in a dictionary for re-use. (Though I wouldn't bother with that unless you're sure that plain reflection is holding you back.)


ORIGINAL ANSWER...

Wouldn't it be a lot cleaner to have separate methods and just call them as needed rather than attempting to roll it all into a single method?

SetTrue(ref b1);
SetFalse(ref b3);
Invert(ref b5);

// ...

public static void SetTrue(ref bool field)
{
    DoCommonStuff();
    field = true;
}

public static void SetFalse(ref bool field)
{
    DoCommonStuff();
    field = false;
}

public static void Invert(ref bool field)
{
    DoCommonStuff();
    field = !field;
}

private static void DoCommonStuff()
{
    // ...
}

I'm assuming that there is some common stuff that also needs doing. If not then it would be much cleaner to simply do b1 = true, b2 = false, b3 = !b3 etc directly and avoid the method calls altogether.

like image 4
LukeH Avatar answered Oct 19 '22 00:10

LukeH


You simply can't just pass a value or a null reference as a ref argument, it must be a variable. And as you probably know, value types cannot be nullable unless you make them nullable.

C# 4.0 does not allow specifying default values for optional ref / out parameters, so I don't believe there is any feasible way to get around this with C# 3.0 either, besides forests of cumbersome method overloads.

like image 1
BoltClock Avatar answered Oct 19 '22 00:10

BoltClock