2016-07-26 4 views
0

の一般的な派生クラスへの一般的な訪問者を適用しますそのオブジェクトにVisit関数は、正しい汎用的なインスタンス化を使用して?私は、その答えはおそらく、オブジェクトが、基底から派生した他の型ではないことを確認するために、型チェックされた動的なダウンキャストを必要とすることを理解しています。私は答えが反映されていると仮定していますが、これはまた問題ありません。反射がなくてもそれを行う方法があれば好きです。は現在、私はこのような何かを持っている非ジェネリック基底クラス

回答に抽象メソッドBaseDerivedが含まれていても問題ありません。私はそれを追加するクラスを十分にコントロールしています。しかし、終わりには、DerivedタイプのTで正しくインスタンス化された汎用関数を呼び出す必要があります。

申し訳ありませんが、これは簡単な質問です。私はC++のバックグラウンドから来ています。私の本能はCRTPやそれ以外のものを使うことです。これはC#では不可能です。

EDIT:

ここで私が行うことができるために必要なものの例です:

(わかりやすくするために編集)
Base GetSomeDerivedInstance() { ...; return new Derived<...>(); } 
var b = GetSomeDerivedInstance(); 

// This is the line that needs to work, though it doesn't necessarily 
// need to have this exact call signature. The only requirement is that 
// the instantiated generic is invoked correctly. 
Visitor.Visit(b); 
+0

正確にはわかりませんが、一般的な制約を探していると思います。https://msdn.microsoft.com/en-us/library/d5x73970.aspx –

+0

コードはそのまま使用する必要があります。 'public class Foo:Base {}; Visitor.Visit (new Derived ()); 'うまくコンパイルできます。 – Jacob

+0

ヤコブ:正確ではありません。私はもっ​​と次のようなことをしようとしています: 'Base b = createDerived(); Visitor.Visit(b); ' – Lucretiel

答えて

1

私の意見では、二重ディスパッチとその他のクラスを含む回答は、継承があなたのために行うべきことを行うためにリフレクションを使うよりも優れています。

通常、これはビジターからの適切な訪問メソッドを呼び出す訪問可能クラスに対して「受け入れ」メソッドを定義することを意味します。

class Base 
{ 
    public virtual void Accept(Visitor visitor) 
    { 
     visitor.Visit(this); // This calls the Base overload. 
    } 
} 

class Derived<T> : Base 
{ 
    public override void Accept(Visitor visitor) 
    { 
     visitor.Visit(this); // this calls the Derived<T> overload. 
    } 
} 

public class Visitor 
{ 
    public void Visit(Base @base) 
    { 
     ... 
    } 

    public void Visit<T>(Derived<T> derived) 
    { 
     ... 
    } 
} 

その後、あなたは小さな変更で、あなたがあなたの質問に言及したものを行うことができます:あなたの訪問の方法は、あなたが何らかの理由で変更することはできません静的なクラスである場合は、

Base b = createDerived(); 
b.Accept(new Visitor()); 

ができ常にこれを右の静的メソッドを呼び出す仮インスタンスのvisitorクラスにラップします。

+0

@base構文は何ですか?私はそれに精通していない。 – Lucretiel

+0

このソリューションに関する私の懸念は、Base.Acceptによって導入されたVisitor、Base、Derived間の循環依存性です。これは心配する価値があるのか​​、それとも非問題なのでしょうか? – Lucretiel

+0

@Lucretielでは、@構文はキーワードから識別子を曖昧にするために使用されます。これは、 'base'がキーワードであるためです。 –

1

以下は、「anyvalueと呼ばれる変数を使用します。その型は実行時にしか知られていません。次に、任意の値の型に基づいてDerivedクラスのインスタンスを作成します。そのインスタンスを取得したら、リフレクションを使用して正しいVisitメソッドを取得できます。少し混乱して何

var anyvalue = 5; // This value could have come from anywhere. 
...  
var derivedType = typeof (Derived<>).MakeGenericType(anyvalue.GetType()); 
var dvalue = Activator.CreateInstance(derivedType); 
var method = typeof(Visitor).GetMethod("Visit"); 
var genericMethod = method.MakeGenericMethod(new[] { anyvalue.GetType() }); 
genericMethod.Invoke(null, new [] { dvalue }); 

が、これは骨格の一例であり、あなたは、実行時の型を取得する以外には元の値を使用していないということです。実際の実装では、コンストラクタがその値を使用してDerivedインスタンスの内部状態を設定すると仮定します。それは質問された質問の一部ではないので、ここでは扱いません。

UPDATE:

私は、これはあなたがやりたいと思います。私はいくつかの実行時の値を持つようにitemarrayを作成しました。彼らはどこかに作られなければならない。したがって、それらがオブジェクトとして渡されるかどうかにかかわらず、型指定子でどこかに構築されなければなりませんでした。また、これはDerived <>が唯一の派生クラスであることを前提としています。それ以外の場合、これは安全なコードではありません。

var itemarray = new Base[] { new Derived<int>(), new Derived<string>() }; 

foreach (var baseObject in itemarray) 
{ 
    var derivedType = baseObject.GetType(); 
    var visitMethod = typeof(Visitor) 
     .GetMethod("Visit") 
     .MakeGenericMethod(derivedType.GetGenericArguments()); 
    visitMethod.Invoke(null, new[] { baseObject }); 
} 

アクセプトのアプローチは、もう少し扱いやすいようです。私の目標は、あなたのアプローチについての判断を下すことなく、あなたが求めた質問に答えることでした。私はこのアプローチを何度か使用する必要がありました。私は約9年前にエンティティフレームワークを書いた。私はあなたがやろうとしていることを正確にやっているのに本当に苦労しました。ジェネリックではない基本クラスを作成したので、ジェネリックタイプにかかわらず基本的な機能を共有することができました。それは挑戦的なことを証明した。今でも同じようにやっているのかどうかはわかりません。私はおそらくあなたがやっているようにいくつかのパターンを調査するだろう。

+0

あなたが提案したリフレクション解決の問題は、呼び出し時でさえ、 'Derived'がインスタンス化された正確な型を知らないということです。 – Lucretiel

+0

私はあなたのコメントを理解していません。この例では、コンパイル時に型を知る必要はありません。 MakeGenericTypeで使用される型は型指定子を持たないDerived <>であることに注意してください。パラメータで使用される変数のタイプは、実行時に抽出されます。ここではコンパイル時の要件はありません。 – rhaben

+0

'anyvalue.GetType()'の使い方はどうですか?これは、私がこの解決策を呼び出すときに 'T'が' int'(あるいはある種の固定型)であることを知っていることを意味しています。 – Lucretiel

0

は、次のような何か行うことができる必要があります:

Base foo = new Derived<int>(); 

var method = typeof(Visitor).GetMethod("Visit", BindingFlags.Public | BindingFlags.Static); 
method.MakeGenericMethod(foo.GetType().GenericTypeArguments.First()).Invoke(null, new[] {foo}); 
0

おそらく、あなたはこのような何かを意味しているの?

public class Derived<T> 
{ 
} 

public abstract class Derivable<T> 
{ 
    public Derived<T> CreateDerived() 
    { 
     return new Derived<T>(); 
    } 
} 

public class Foo : Derivable<Foo> 
{ 
} 

class Visitor 
{ 
    public static void Visit<T>(Derived<T> obj) 
    { 
     Console.Out.WriteLine("Called!"); 
    } 
} 

void Main() 
{ 
    var obj = new Foo(); 
    var derived = obj.CreateDerived(); 
    Visitor.Visit(derived); 
} 

Derived<T>の作成がT固有である場合は、CreateDerivedメソッドの抽象を作成し、各Tのためにそれを実装すると思います。または、ベースクラスにしたくない場合はIDerivable<T>インターフェイスを代わりに使用してください。

関連する問題