1

クラス階層に基づくデータ構造にビジター パターンを実装しようとしています。C# では、(まだ) 型をオンにすることはできません。代わりに次のようなことを考えていました。

public MyAlgorithm : Func<IBase, X> {
    // default:
    public X apply(IBase data) {}

    // case param1 is ConcreteSubclass
    public X apply(ConcreteSubclass data) {}

    // case param1 is ConcreteOtherClass
    public X apply(ConcreteOtherClass data) {}
}

そして、レイトバウンドディスパッチで呼び出します:

public ICanApplyAlgorithmOn {
    public void apply(Func<IBase> alg);
    public TResult apply<TResult>(Func<IBase,TResult> alg);
    public TResult apply<TParam1,TResult>(Func<IBase, TParam1, TResult> alg);
    // etc.
}

public abstract Base : ICanApplyAlgorithmOn {
    // etc.
    public TResult apply(Func<IBase, X> alg) {
        // Hopefully I am correct in deducing that this will call the various apply(...) implementations instead of always method(IBase)
        dynamic me = this;
        return alg(me);
    }
    // etc.
}

ただし、MyAlgorithmdelegate から継承できないため、これは機能しませんFunc<...>

私が見た唯一の解決策は、次のような多くの独自のインターフェースを定義することです。より良い方法はありますか?

public interface IAlgorithm { public void apply(IBase data); }
public interface IAlgorithm<TReturn> { public TReturn apply(IBase data); }
public interface IAlgorithm<TP1, TReturn> { public TReturn apply(IBase data, TP1 param1); }
// etc.
4

3 に答える 3

4
  • 訪問者パターンは、二重ディスパッチを手動で実現する方法です。
  • を使用dynamicすることで、複数の発送が可能になります。

引数の実行時の型に基づいて関数を選択するだけの場合は、これら 2のオプションのいずれかを選択するだけで十分です。これらを組み合わせても意味がありません。

dynamic訪問者の代わりに使用するソリューションは次のとおりです。

class MyAlgorithm
{
    public X Apply(IBase data)
    {
        try
        {
            return ApplyImpl((dynamic) data);
        }
        catch (RuntimeBinderException ex)
        {
            throw new ArgumentException(
                string.Format("{0} is not implemented for type {1}.", typeof (MyAlgorithm).Name, data.GetType().Name),
                ex);
        }
    }

    private X ApplyImpl(ConcreteSubclass sub)
    {
        // Your implementation here.
        return null;
    }

    private X ApplyImpl(ConcreteOtherClass sub)
    {
        // Your implementation here.
        return null;
    }
}

次のように使用できます。

var algorithm = new MyAlgorithm();
var data = new ConcreteSubclass();
algorithm.Apply(data);

または、次のようなデリゲートを使用することもできます。

Func<IBase, X> func = algorithm.Apply;
func(data);
于 2015-09-29T16:59:37.037 に答える
1

2 つのインターフェイスが必要です。1 つはビジター用、もう 1 つはビジターブル クラス用です。

public interface IAlgorithmVisitor<X>
{
    public X Visit(IBase data);
    public X Visit(ConcreteSubclass data);
    public X Visit(ConcreteOtherClass data);
}

public interface IAlgorithmVisitable<X>
{
    X Accept(IAlgorithmVisitor<X> visitor);
}

Acceptアルゴリズム クラスは、次のようにメソッドを実装できるようになりました。

public X Accept(IAlgorithmVisitor<X> visitor)
{
    return visitor.Visit(this);
}

Visitメソッドのオーバーロード メカニズムは、現在の型に従って、適切なオーバーロードを自動的に呼び出すことに注意してください。適切なメソッドのオーバーロードはコンパイル時に解決されます! (動的な遅延バインディングは必要ありません。)


さて、このようなアルゴリズムのコレクションを反復処理できます

IAlgorithmVisitor<int> visitor = new ConcreteAlgorithmVisitor<int>();
foreach (var algorithm in intAlgorithms) {
    int result = algorithm.Accept(visitor);
    //TODO: do something with result.
}

ただし、何らかの有用なことを行うのはビジターのタスクであるため、 Acceptorメソッドから結果を返すことはまれです。Visitこれは、反復子または受け入れオブジェクトのタスクではありません。これにより、まったく異なることを実行する訪問者を作成できます。

おそらく、Strategy パターンはVisitor パターンよりもニーズに適しているでしょう。

于 2015-09-29T17:01:42.837 に答える
0

何をアーカイブしようとしているのかは少しわかりませんが、ランタイムの型バインディングを高速にするには、次のコードを使用できます。

Dictionary<Type, Func<object, TReturn>>次のようにa を使用できます。

public class AlgorithmClass<TReturn>
{
    private Dictionary<Type, Func<object, TReturn>> mMethods;

    public AlgorithmClass<TReturn>(Dictionary<Type, Func<object, TReturn>> methods)
    {
        mMethods = methods
    }

    public TReturn Invoke(object argument)
    {
        Type type = argument.GetType();

        //This line supports inheritance and co/contra-variance.
        //If you want to archive full performance and not support those features you can just use mMethods.TryGetValue(type, out Func<object, TReturn>);
        var kvps = mMethods.Where(x => x.Key.IsAssignableFrom(type));

        if(!kvp.Any())
        {
            throw new MissingMethodException("There is no method which can take " + type.Name + " as an argument");
        }

        if(kvp.Count() > 1)
        {
            throw new ArgumentException("There is more than one method which can take " + type.Name + " as an argument");
        }

        return kvp.First().Value(argument);
    }
}

コードで、次のようにクラスを使用できます。

AlgorithmClass<ReturnType> algorithm = new AlgorithmClass(new Dictionary<Type, Func<object, ReturnType>>
    {
        {typeof(int), MethodForIntType},
        {typeof(string), MethodForStringType},
        {typeof(MyClass), MethodForMyClassType}
    });

ReturnType MethodForIntType(object anInt)
{
    code...
}

ReturnType MethodForStringType(object aString)
{
    code...
}

ReturnType MethodForMyClassType(object aMyClass)
{
    code...
}

dynamic実行時にorSystem.Reflectionのメソッドを使用Binderすると、プログラムの速度が低下します (ただし、 をobject引数として使用するには、メソッドの先頭でボックス化、ボックス化解除、およびキャストが必要です)。

于 2015-09-29T17:04:25.033 に答える