7

ブール値フラグへの参照を取得して変更するメソッドを作成しようとしています。ブール値はすべて個別に宣言され (つまり、インデックス可能なデータ構造ではありません)、メソッドの呼び出し元は、どのブール値が変更されているかを判断できる必要があります。

サンプルコード (これは機能します):

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

期待どおりの出力:

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

ここまでは順調ですね。現在、これらのパラメーターはメソッドのオプションである必要があります。つまり、呼び出し元は、たとえばsetTrueandinvert効果を使用することを選択できますが、setFalseone を使用することはできません。

基本的に、私がやりたいことはこれです:

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

そして、次の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;
}

値が null かどうかを確認したくないことに注意してください。値は実際のブール値であり、null にすることはできません (そして、それらを宣言してbool?も実際には問題は解決しません)。呼び出し元に参照としてnull を与える機能を与えたいだけです。

メソッドの実装はもっと複雑かもしれませんが、呼び出しを 1 行に抑えたいと思います。(つまり、この呼び出しのためだけに一時変数を宣言する必要がなくなります。)

1 つの可能性は、bool のすべての組み合わせが指定されているかどうかにかかわらず、関数の (8 つの) オーバーロードを宣言することですが、その場合、それらすべてに一意のシグネチャがあることを確認するためのスキームを考え出す必要があります。(私は C# 3.0 に固執しているため、名前付きパラメーターはありません。)

何か不足していますか?クリーンな回避策はありますか?現在、私が考えることができる唯一の(かろうじて)受け入れられる代替手段は、変数名(またはnull)を含む文字列を渡し、リフレクションを使用してこれらを実際のフィールドに解決することです。

PS: なぜ私がこんな奇妙なことをしようとしているのか疑問に思っているかもしれませんが、背景の言葉:doSomethingメソッドはライブラリの一部です。の呼び出しはdoSomething、生成された C# コードからのものです。はい、これらすべての bool (実際のプロジェクトでは ~200) を個別のフィールドとして持つことは、全体像では理にかなってますが、その理由はこの質問にはあまり関係ありません。

4

9 に答える 9

4

アップデート...

1 回のメソッド呼び出しで 3 つのフィールドすべてを潜在的に操作する必要がある場合は、比較的きれいに行うためにリフレクションを使用する必要があると思います。ただし、これは、式ツリーを使用してある程度のタイプ セーフで行うことができます。フィールド名を文字列として渡す必要はありません。

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

このようなリフレクションを使用すると、比較的遅くなることに注意してください。十分に高速でない場合は、リフレクションをもう少し深く掘り下げる必要があるかもしれません。DynamicMethodデリゲートを作成し、再利用のためにディクショナリにキャッシュする可能性があります。(単純な反省があなたを妨げていると確信していない限り、私はそれを気にしませんが。)


元の答え...

すべてを 1 つのメソッドにまとめようとするよりも、個別のメソッドを用意して、必要に応じて呼び出す方がはるかにクリーンではないでしょうか?

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()
{
    // ...
}

私は、行う必要がある一般的なものがいくつかあると想定しています。そうでない場合は、、などを直接実行して、メソッド呼び出しを完全に回避する方がはるかにクリーンです。b1 = trueb2 = falseb3 = !b3

于 2011-06-06T09:12:33.467 に答える
4

オプションのパラメーターが本当に必要な場合は、 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;
}

醜さ。しかしねえ、オプションのパラメータ!

于 2011-06-06T09:18:16.787 に答える
1

関数専用の新しい型を作成しないのはなぜですか? 簡単できれいです。

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;
}
...
于 2011-06-06T09:20:24.650 に答える
1

ブール値のパラメーターを保持するクラスを作成するとどうなりますか。State多分それまたはそれに似たものと呼んでください。それはブールを持っていますか?フィールドとして。

次に、状態オブジェクトを doSomethingMethod に渡すことができます。これは、生成された適切なメソッドを呼び出し、ref によってすべてのパラメーターを渡します。

このメソッドの良い点は、使用するブール値を設定するだけで、ラッパー メソッドが何をすべきかを判断することです。たとえば、一部のパラメーターが設定されていない場合、生成されたメソッドにそれらを渡しません。

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
}

次に、他のメソッドでこれをきれいに呼び出すことができます

class Program
{
    static void Main(string[] args)
    {
        var state = new MyBooleanState() { Invert = false };
        doSomething(state);
        Console.WriteLine("Invert is now: {0}", state.Invert);
    }
}
于 2011-06-06T09:20:38.317 に答える
1

or引数は割り当て可能な変数でなければならないため、 orパラメーターを使用してdoSomething(ref b1, null, ref b5);(つまり、null を引数として渡す)ことはできません。refoutrefout

配列またはリストを使用してパラメーターとしてメソッドに渡すこともできますが、後でこのリストをアンパックして個々の値を割り当てる必要があると思います。

別のオプションは、ブール値をラップする独自のブール型を作成することですが、これにはブール値へのすべての参照を変更する必要があります。

このスタックオーバーフローの投稿では、 を使用して値型を参照型に変更する方法について説明していますFunc。あなたのソリューションに適しているかどうかはわかりませんが、良いアプローチだと思います。

于 2011-06-06T09:21:33.183 に答える
1

値または null 参照をref引数として渡すことはできません。変数である必要があります。おそらくご存じのとおり、値型を null 許容にしない限り、値型を null 許容にすることはできません。

C# 4.0 では、オプションのref/ outparametersにデフォルト値を指定することは許可されていないため、面倒なメソッドのオーバーロードの森以外に、C# 3.0 でもこれを回避する実行可能な方法はないと思います。

于 2011-06-06T09:15:26.030 に答える
1

メソッドに渡す独自のコンテナー タイプを作成する場合はどうでしょうか。これは非常に簡単な解決策です。

于 2011-06-06T09:18:31.950 に答える
0

あなたがやろうとしていることは間違っています:

  • 変更や再構築を少なくする方法でコードを記述しようとしていますが、将来的にはメンテナンスと複雑さが増します。

通常、これは別の方法で行う必要があります。

  • 何かが複雑になり、読めなくなるのを見つけたら、リストラを行います。待つ時間が長くなるほど、リストラが難しくなり、ある時点で行き詰まってしまうからです。

ここで、リストラなしでは何も存在しないため、ベストプラクティスではない回答で質問に回答します。

ダミーのrefパラメーターを使用します。

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

これはおそらく機能しますが、はっきりとわかるように、これはハックです...

これを呼び出す必要があるすべての場所でダミー変数を宣言する必要があると不平を言うかもしれませんが、それは真実ではありません。ハックをハックして全体をハックすることで、コードの呼び出しを簡素化できますが、それを行うのはクールではありません。

    public static class dummy
    {
        public static bool boolean;
    }


//...

   doSomething(ref b1, ref dummy.boolean, ref b5, ref dummy.boolean, ref b7);
于 2011-06-06T10:47:57.687 に答える
0

これは、コード ジェネレーターにとって切実な問題のように思えます。必要なすべての関数のオーバーロードを作成するか、小さなコード ジェネレーターから必要なすべてのラッパーを作成します。

個人的には、「間に」レイヤーを追加して、すべてのブール値を配列にダンプします。

于 2011-06-06T09:14:57.463 に答える