1

私はこの線に沿って何かを持っています:

public class Something
{
    private IDictionary<object,Activity> fCases;

    public IDictionary<object,Activity> Cases
    {
        get { return fCases; }
        set { fCases = value; }
    }
}

public sealed class Something<T> : Something 
{
    private IDictionary<T,Activity> fCases;

    public override IDictionary<T,Activity> Cases
    {
        get { return fCases; }
        set { fCases = value; }
    }
}

注:この場合、オーバーライドは受け入れられません

Reflectionを頻繁に使用するため、からにダウンキャストする必要がある場合がありますSomething<T>Something、プロパティが非表示になっているため、データCasesが失われていると思いCasesます。

どうすればこの状況を回避できますか?使ってみましwhere T:objectたが、それも受け入れられません。

編集:これは私が継承を必要とする理由の例です:

if (someVar is Something) {

    if (someVar.GetType().IsGenericType) 
    {
        // Construct AnotherObject<T> depending on the Something<T>'s generic argument
        Type typeArg = someVar.GetType().GetGenericArguments()[0],
            genericDefinition = typeof(AnotherObject<>),
            typeToConstruct = genericDefinition.makeGenericType(typeArgs);

        object newAnotherObject = Activator.CreateInstance(typeToConstruct);

        // Pass Something 'Cases' property to AnotherObject<T>
        constructedType.InvokeMember(
            "Cases",
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
            null,
            newActivity,
            new Object[] { someVar.Cases });
    }
}

ただし、「ケース」は非表示になっているため、常にnullになります。継承がなければ、考えられるすべてのジェネリック引数を使用して、大きなif-then-elseを作成する必要があります。そして、私を信じてください、私は本当にsomeVar is SomethingandReflectionを使用てこのすべてのオブジェクトを構築する必要があります。これは、他の大きな汎用APIに変換される大きな汎用APIであるため、お互いを認識してはならず、変換は可能な限り透過的である必要があります。

4

4 に答える 4

4

そのようにオーバーライドすることはできませんが、それには正当な理由があります。

想像:

Something x = new Something<string>();
Button key = new Button();
x.Cases[key] = new Activity();

オーバーライドが機能した場合、Button参照を にキーとして保存しようとしDictionary<string, Activity>ます。それは悪いことです。

この場合、継承は実際には適切ではないのでしょうか。あなたが達成しようとしていることについてもっと説明できれば、それは役に立ちます。おそらく、辞書をプロパティとして本当に必要としないのでしょうか? たぶん、キーで取得する方法ですか?

于 2011-03-02T19:17:59.983 に答える
2

IDictionary<TKey, TValue>インターフェイスが不変であるため、これは完全に機能しません。IDictionary<object, Activity>をとして扱うことはできませんIDictionary<T, Activity>

派生クラスで全体を公開するのではなく、次のように、公開する呼び出しを委任するだけで実行できますIDictionary<T, Activity>

public class Something
{
    protected IDictionary<object, Activity> Cases { get; set; }
}

public sealed class Something<T> : Something 
{
    public Activity GetCase(T key)
    {
        return Cases[key];
    }

    public void AddCase(T key, Activity case)
    {
        Cases.Add(key, case);
    }

    // etc. etc.
}

または、次のような独自の反変インターフェースを定義することもできます。

interface IKeyedCollection<in TKey, TValue>
{
    TValue this[TKey key] { get; set; }
    void Add(TKey key, TValue value);
}

上記のインターフェースの場合、すべてがであるため、はとして機能するIKeyedCollection<object, Activity> 可能性があります。IKeyedCollection<T, Activity>Tobject

于 2011-03-02T19:23:13.993 に答える
1

さまざまなレベルで互換性のない型を公開しようとすると、問題が発生し続けることになります。結局のところ、2 つの個別のオブジェクト (または 2 つのインターフェイスを持つ 1 つのカスタム オブジェクト) を維持する必要があるためです。完全に満足)。

IDictionary<object, Activity>のすべてのインスタンス化に追加できない値を追加できるため、これらの型には互換性がありませんIDictionary<T, Activity>。たとえば、Tが as として示されstring、開発者が をint介して別の場所でキーを使用しているとしSomethingます。これは、実装に実際の問題を引き起こしますSomething<string>

私がこれにアプローチする方法は、基本型を変更しSomethingて具体的な型を公開するのではなく、関連する API を公開することです。

public abstract class Something {
  public abstract IEnumerable<KeyValuePair> GetElements(); 
  public abstract bool TryGetValue(object key, out Activity value);
}

これによりSomething<T>、適切にサブクラス化するために必要な柔軟性が得られ、Something公開したい型について非常に表現力豊かになります。

public sealed class Something<T> : Something {
  private IDictionary<T,Activity> fCases;

  public override IDictionary<T,Activity> Cases
  {
    get { return fCases; }
    set { fCases = value; }
  }

  public override IEnumerable<KeyValuPair<object, Activity>> GetElements() {
    foreach (var cur in fCases) {
      yield return new KeyValuePair<object, Activity>(cur.Key, cur.Value);
    }
  }

  public override bool TryGetValue(object key, out Activity activity) {
    try {
      T typedKey = (T)key;
      return fCases.TryGetValue(typedKey, out activity);
    } catch (InvalidCastException) {
      activity = null;
      return false;
    }
  }
}

}

于 2011-03-02T19:15:23.717 に答える
1

リフレクションを頻繁に使用している間、ジェネリック型から「アップキャスト」する必要もありました。特定の呼び出しに互換性があることはわかっていましたが、コンパイル時の型はわかりませんでした。このように見ると、実際にはジェネリック型を「アップキャスト」しているのではなく、正しいダウンキャストを生成することにより、リフレクション中にジェネリックを使用できるようになっています

この目的のために、Delegate.CreateDelegateの行に沿ってデリゲートを作成するヘルパー メソッドを作成しましたが、一般的ではないデリゲートを作成できるようにしました。必要に応じてダウンキャストが生成されます。ブログで詳しく解説しています。

MethodInfo methodToCall = typeof( string ).GetMethod( "Compare" );
Func<object, object, int> unknownArgument
    = DelegateHelper.CreateDowncastingDelegate<Func<object, object, int>>(
          null, methodToCall );
unknownArgument( "test", "test" ); // Will return 0.
unknownArgument( "test", 1 ); // Will compile, but throw InvalidCastException.

少し後に、リフレクション中にすべてのメソッド呼び出しが汎用性の低い呼び出しとしてすぐに利用できるように、汎用性の低いラッパー クラス全体をジェネリック クラス用に作成する必要がありました。これは、シナリオでも役立つ場合とそうでない場合があります。この目的のために、emit を使用して実行時にこのラッパー クラスを生成できる (完全にはテストされていない) メソッドを作成しました。これは私のオープン ソース ライブラリで入手できます。これについてはまだ書いていないので、興味がある場合は試してみてください (まだ新しいため、失敗する可能性があります)。

于 2011-05-30T17:49:12.830 に答える