开发者

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;
   开发者_StackOverflow社区 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.


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 !


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.


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.


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


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;
}
...


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.


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);
    }
}


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.


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);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜