7

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.

Henrik Heimbuerger
  • 9,924
  • 6
  • 56
  • 69

9 Answers9

4

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.

LukeH
  • 263,068
  • 57
  • 365
  • 409
  • The problem is that any combination of the bools is possible and the combination chosen by the caller influences the result, so I'd actually need nine of these helper methods. This might be a good solution nevertheless. I could probably write them once, then use them everywhere by accepting a 'result' (basically the possible result states 0..8), calling the actual working method (your `DoCommonStuff`) outside and passing the result state in. – Henrik Heimbuerger Jun 06 '11 at 09:19
  • And yes, I probably simplified my example code a bit too much. But thanks, you might have guided me to the right path! – Henrik Heimbuerger Jun 06 '11 at 09:21
  • I meant eight helper methods, not nine. 2^3, duh. – Henrik Heimbuerger Jun 06 '11 at 09:34
  • @hheimbuerger: Do all the boolean fields live in the same class as your `doSomething` method, or are they spread about all over the place? – LukeH Jun 06 '11 at 09:39
  • @LukeH They live in one place, but it's not the same class as the `doSomething` method. – Henrik Heimbuerger Jun 06 '11 at 10:02
  • @hheimbuerger: I've updated my answer to include a reflection-based answer that allows you to manipulate all three fields with a single method call. – LukeH Jun 06 '11 at 11:12
4

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 !

user703016
  • 37,307
  • 8
  • 87
  • 112
  • 1
    I was thinking about this as well. I guess it's better than the 'pass strings with variable names in, then use reflection to resolve them' approach. But yeah, I'd also like to avoid unsafe -- or at least be totally sure there is no better way to do this first. – Henrik Heimbuerger Jun 06 '11 at 09:23
1

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.

Community
  • 1
  • 1
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
1

What about creating your own container type, to pass into your method? This should be quite simple solution.

Oleg
  • 798
  • 3
  • 7
  • Can I store references (`ref bool`) in a container, though? And are they (the references, not the values) nullable then? – Henrik Heimbuerger Jun 06 '11 at 09:24
  • @hheimbuerger, I believe they won't become nullable. But your object will be a reference type so will be nullable. – Oleg Jun 06 '11 at 09:30
  • @Oleg So you're thinking of eight different container types with all the combinations of flags given and not given, like Dennis Bischof. – Henrik Heimbuerger Jun 06 '11 at 09:36
  • 1
    @hheimbuerger, No. doSomething(YourContainer c1, YourContainer c2, YourContainer c3) { if(c1 != null){ c1.Value= true; } if(c2!=null) {c2.Value=false;} if(c3!=null) {c3.Value = !c3.Value}} – Oleg Jun 06 '11 at 09:43
  • doSomething(new YourContainer(ref yourBool), null, null); – Oleg Jun 06 '11 at 09:44
  • @Oleg That would change the `Value` bool in YourContainer, but how do I get the new state back into the original field `yourBool` afterwards? Or is `YourContainer.Value` a reference? How do I declare that? – Henrik Heimbuerger Jun 06 '11 at 10:05
  • @hheimbuerger The `YourContainer` class would be a class and thus a reference. Basically, `class YourContainer{public T Value{get;set;}}` would let you have `void doSomething(YourContainer c1 = null, YourContainer c2 = null) { bool result1 = resultOfSomething(); if (c1 != null) c1.Value = result1; }` which is, effectively, an *optional* `ref bool` parameter. – binki Dec 09 '13 at 17:11
  • Is there such a container as this in the framework or in a suggestible nuget package? This feels like something that would exist somewhere already… – binki Jan 06 '15 at 19:58
1

Why you don't write a new type only for your functions? It's easy and clean.

private static void doSomething(MyNewTypeWith3Bools object)
{    
    object.SetTrue = true;
    object.SetFalse = false;
    object.Invert = !object.Invert;
}

private static void doSomething(MyNewTypetWith2Bools object)
{    
    object.SetTrue = true;
    object.Invert = !object.Invert;
}
...
Dennis Bischof
  • 355
  • 2
  • 6
  • 1
    But that would only change the state of the fields inside the new types (`MyNewTypeWith3Bools`, ...). How do I transfer the values back to the original fields afterwards? If I wouldn't need to change the actual state of the object, I could just pass them bools in as the value types they are (`bool` parameters instead of `ref bool`). – Henrik Heimbuerger Jun 06 '11 at 09:32
  • 1
    Also, I'd actually need eight of these types: MyNewTypeWithBools1And2, MyNewTypeWithBools1And3, MyNewTypeWithBools2And3, etc. – Henrik Heimbuerger Jun 06 '11 at 09:33
1

How about if you create a class to hold the boolean parameters. Maybe call it State or something similar. It will have bool? as fields.

Then you can pass your state object to your doSomethingMethod which will call the appropriate generated method and will pass all the parameters by ref.

The good thing about this method is you only need to set the booleans you are going to use and the wrapper method will figure out what to do. e.g. if some of the parameters are not set it's not going to pass them to the generated method.

public class MyBooleanStateClass
{ 
  public bool? setTrue { get; set; }
  public bool? setFalse { get; set; }
  public bool? Invert { get; set; }
}
private static void doSomething(MyBooleanStateClass state)
{
   if(state.setTrue.HasValue())
     // do something with setTrue
   // repeat for all the others
}

Then you other method can call this cleanly

class Program
{
    static void Main(string[] args)
    {
        var state = new MyBooleanState() { Invert = false };
        doSomething(state);
        Console.WriteLine("Invert is now: {0}", state.Invert);
    }
}
marto
  • 4,170
  • 1
  • 28
  • 39
  • Not sure how I would be going about transfering the results back into the original fields from the `State` instance then. – Henrik Heimbuerger Jun 06 '11 at 09:25
  • @hheimbuerger. It was a bad example. I changed it so it shows how you can use the variable again after the method. – marto Jun 06 '11 at 09:40
  • I still need to get the results back into the original fields, though. I have ~200 of these bools being used by the rest of the code, so accessing them from the `state` class is just not an option. (Yes, I know, my requirements are really weird. :)) – Henrik Heimbuerger Jun 06 '11 at 09:43
1

You will never be able to do doSomething(ref b1, null, ref b5); (i.e. pass null as an argument) with ref or out parameters because the ref or out argument has to be an assignable variable.

You could use an array or list to pass in as a parameter to the method but I assume then you will need to un-pack this list afterwards to assign the individual values.

Another option is to create your own boolean type to wrap the boolean value but this would require all your references to boolean to be changed.

This stackoverflow post discusses changing a value type to a reference type using a Func. Not sure if its feasible for your solution but I think its a nice approach.

Community
  • 1
  • 1
IndigoDelta
  • 1,481
  • 9
  • 11
0

This seems like a problem that just screams for a code generator. Create overloads for all the functions you need or just create all the wrappers you need from a little code generator.

Personally I would add a 'in between' layer to dump all the booleans into an array.

CodingBarfield
  • 3,392
  • 2
  • 27
  • 54
0

What you are trying to do is wrong:

  • you are trying to write code in a way to make fewer changes and restructuring but in the future you will have more maintenance and complexity

Usually this should go in the other way:

  • do restructuring when you see something is becoming or will become complex and unreadable because more you wait more difficult it will become and at some point you will get stuck

Now to answer your question with a non best practice answer, because without restructuring there are none:

Use dummy ref parameters:

doSomething(ref b1, ref dummy, ref b5, ref dummy, ref b7);

This will probably work, but as you clearly see it is a hack...

You may complain that you will need to declare dummy variables in all places where you need to call this, but that is not true. You can hack on hacks and hack all over to simplify invoking code, even though it is not cool to do that:

    public static class dummy
    {
        public static bool boolean;
    }


//...

   doSomething(ref b1, ref dummy.boolean, ref b5, ref dummy.boolean, ref b7);
Marino Šimić
  • 7,318
  • 1
  • 31
  • 61