7

私は、多くのクラスがよく知られているが順序付けられていないさまざまなタイプのオブジェクトのコンテナーである状況にいます。たとえば、コンテナーは次のようになります。

public class Container
{
    public A A { get; private set; }
    public B B { get; private set; }
    public C C { get; private set; }

    public bool StoreIfKnown(object o)
    {
        // TODO...
    }
}

oそのため、タイプの場合はプロパティにA格納する必要があり、プロパティに入力するなどです。ABB

F# では、StoreIfKnownメソッドは次のように記述できます (構文エラーを許してください。私の F# はあまり良くなく、かなり錆びています)。

match o with
| ?: A a -> A <- a; true
| ?: B b -> B <- b; true
| ?: C c -> C <- c; true
| _ -> false

しかし、C#では、唯一の方法はかなり冗長であるようです:

if (o is A)
{
    this.A = (A)o;
    return true;
}

if (o is B)
{
    this.B = (B)o;
    return true;
}

// etc.

return false;

テスト/キャストパターンを回避するためにキーワードを使用して実行できますがas、これはより高速ですが、さらに冗長です。

C#でこれを行うエレガントな方法はありますか?

4

5 に答える 5

11

「o」およびヘルパー クラスで拡張メソッドを作成して、次のようなプログラミング モデルを有効にすることができます。

o.Match<A>( a => { this.A = a; return true; } )
 .Match<B>( b => { this.B = b; return true; } )
 .Else( () => { return false; } )

ただし、ここで DSL のようなハッキングを行いすぎないように注意してください。自分だけが理解できる API になってしまわないように注意してください。

こちらもご覧ください

http://blogs.msdn.com/lucabol/archive/2008/07/15/ac-library-to-write-functional-code-part-v-the-match-operator.aspx

于 2009-10-02T12:17:47.073 に答える
9

Brian のソリューションほど気の利いたものではありませんが、新しい DSL を定義する必要はありません。次のコードが繰り返されていることに気付くでしょう。

if (o is {DataType})
{
    {Property} = ({DataType})o;
    return true;
}

そのテンプレートを独自のメソッドにプルするのは簡単で、次のようになります。

public class Container
{
    public A A { get; private set; }
    public B B { get; private set; }
    public C C { get; private set; }

    private bool TestProp<T>(object o, Action<T> f)
    {
        if (o is T)
            return false;

        f((T)o);
        return true;
    }

    public bool StoreIfKnown(object o)
    {
        return
            TestProp<A>(o, x => A = x) ||
            TestProp<B>(o, x => B = x) ||
            TestProp<C>(o, x => C = x) ||
            false;
    }
}

参照型を使用している場合は、次の調整を行うことで型推論を利用できます。

    private bool TestProp<T>(T o, Action<T> f)
    {
        if (o == null)
            return false;

        f(o);
        return true;
    }

    public bool StoreIfKnown(object o)
    {
        return
            TestProp(o as A, x => A = x) ||
            TestProp(o as B, x => B = x) ||
            TestProp(o as C, x => C = x) ||
            false;
    }
于 2009-10-02T14:38:46.543 に答える
8

私は、タイプチェック、ガード句、および全体からの結果の返還を可能にする小さなマッチビルダー(ブライアンの答えに触発された)で遊んでいます。型推論を使用するため、型を指定する必要があるのは、実際に指定したい場所だけです。

したがって、タイプCに必要なIsActiveプロパティがあると想像するとtrue、次のようになります。

var stored = Match.Against(o)
    .When<A>().Then(a => { this.A = a; return true; })
    .When<B>().Then(b => { this.B = b; return true; })
    .When<C>(c => c.IsActive).Then(c => { this.C = c; return true; })
    .Otherwise(a => false);

特に、実際に一致する前に派生型に対して述語を実行できるため、これはかなり読みやすいと思います。これは必要なものです。

型推論を機能させるには、バックグラウンドでいくつかの部分的に指定されたビルダー クラスが必要なため、コードは非常に長くなります。そのため、ここに実際に投稿することはできません。しかし、興味のある方はコメントでお知らせください。ブログに貼り付けて、ここにリンクを貼ります。

于 2009-10-02T15:40:41.187 に答える
2

Bart de Smet はかつて、ここからパターン マッチングに夢中になりました(パート 8 まで続きます)。この内容をすべて理解できた場合、C# でのパターン マッチングについて疑問が残ることはありません。存在する場合、おそらくスタックオーバーフローでは回答できません:)

于 2009-10-02T15:52:24.100 に答える
1

2016 年 8 月および C# 7.0 のプレビューの時点で、パターン マッチングのサポートは限定されています。Visual Studio “15” Preview 4を使用して試すことができます。

MSDN ブログによると、次の 2 つの場所でパターンを使用できます。

  • is 式の右辺

  • switch ステートメントcase 節

考えられるパターンは次のとおりです。

  • c (c は C# の定数式) の形式の定数パターン。入力が c と等しいことをテストします。

  • T x (T は型、x は識別子) の形式の型パターン。入力が型 T であることをテストし、型 T の新しい変数 x に入力の値を抽出します。

  • var x (x は識別子) の形式の var パターン。これは常に一致し、入力の値を入力と同じ型の新しい変数 x に入れるだけです。

Visual Studio 15 をインストールしていないため、コードを正しく書き直したかどうかはわかりませんが、そう遠くないはずです。

public class Container
{
    public A A { get; private set; }
    public B B { get; private set; }
    public C C { get; private set; }

    public bool StoreIfKnown(object obj)
    {
        switch (obj)
        {
            case A a:
                this.A = a
                // I don't put "break" because I'm returning value from a method
                return true;
            case B b:
                this.B = b
                return true;
            case C c:
                this.C = c
                return true;
            default:
                WriteLine("<other>");
                return false;
        }
    }
}
于 2016-09-16T15:01:10.977 に答える