C#中可选ref参数的解决方法

Hen*_*ger 7 c# ref optional-parameters c#-3.0

我正在尝试编写一个方法来接受对布尔标志的引用并修改它们.布尔值都是单独声明的(即不在可索引的数据结构中),并且方法的调用者应该能够决定修改哪些布尔值.

示例代码(这适用):

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出,如预期:

Pre: False, False, False, False, False
Post: True, False, False, False, True
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.现在这些参数在方法上应该是可选的.也就是说,呼叫者可以选择例如使用setTrueinvert效果,但不能选择效果setFalse.

基本上,我想做的是:

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

然后声明这样的doSomething方法:

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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,我希望检查值为null.值是真正的bool并且不能为null(并且声明它们bool?并不能真正解决我的问题).我只想让调用者能够将null 作为引用.

虽然该方法的实现可能更复杂,但我真的希望将调用保持在一行.(即避免为此调用声明临时变量.)

一种可能性是为所有bool组合声明函数声明(8)重载,但是我需要提出一些方案来确保它们都具有唯一的签名.(我坚持使用C#3.0,所以没有命名参数.)

我错过了什么吗?有一个干净的解决方法吗?目前我能想到的唯一(几乎)可接受的替代方法是传入带有变量名称(或null)的字符串,然后使用反射将它们解析为实际字段.

PS:你可能想知道我为什么要做一些奇怪的事情,有些背景话题:这个doSomething方法是图书馆的一部分.调用doSomething来自生成的C#代码.是的,将所有这些bool(在实际项目中约200个)作为单独的字段确实在大图中是有意义的,但是推理与这个问题并不真正相关.

Luk*_*keH 4

更新...

如果您需要在单个方法调用中潜在地操作所有三个字段,那么我认为您需要使用反射来相对干净地完成它。然而,这可以通过使用表达式树来实现某种程度的类型安全;您不需要将字段名称作为字符串传递。

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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,像这样使用反射会相对较慢。如果它不够快,那么您可能需要更深入地研究反射,可能使用DynamicMethod创建委托,然后将它们缓存在字典中以供重复使用。(尽管我不会为此烦恼,除非你确定简单的反射会阻碍你。)


原来的答案...

拥有单独的方法并根据需要调用它们而不是尝试将其全部合并到一个方法中不是更干净吗?

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()
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

我假设还需要做一些常见的事情。如果没有,那么直接简单地执行、等操作并完全避免方法调用会更干净。b1 = trueb2 = falseb3 = !b3