0

基本ライブラリで定義されている次のクラス/インターフェイスがあるとします。

public interface IWorkContext {
    T Node<T>() where T : class, IHierachy<T>;
    IHierachyItem Node();
}

public interface IHierachyItem {
    int Id { get; set; }
    string Title { get; set; }
}

public interface IHierachy<T> : IHierachyItem where T : IHierachy<T> {
    T Parent { get; set; }
    IList<T> Children { get; set; }
}

public class WorkContext {
    public static IWorkContext Current {
        get { return DependencyResolver.Current.GetService<IWorkContext>(); }
    }
}

これは、別のライブラリ(上記のベースライブラリを参照する)内で次の意味を持ちます。

public class DefaultWorkContext : IWorkContext {
    // Nhibernate session
    private readonly ISession _session;

    public DefaultWorkContext(ISession session) {
        _session = session;
    }

    public T Node<T>() where T : class, IHierachy<T> {
        return _session.Get<T>(2);
    }

    public IHierachyItem Node() {
        return Node<SiteMapNode>();
    }
}

SiteMapNodeが同じライブラリに存在する(そしてデータベーステーブルにマップされている)場合:

public class SiteMapNode : IHierachy<SiteMapNode> {
    public virtual int Id { get; set; }
    public virtual SiteMapNode Parent { get; set; }
    public virtual string Title { get; set; }
    public virtual IList<SiteMapNode> Children { get; set; }

    public SiteMapNode() {
        Children = new List<SiteMapNode>();
    }
}

ノードにアクセスして親を取得するには、次のように言うことができます。

var node = WorkContext.Current.Node();
var parentNode = ((IHierachy<SiteMapNode>)node).Parent;

var node2 = WorkContext.Current.Node<SiteMapNode>();
var parentNode2 = node2.Parent;

ただし、オプション1にはケースが必要であり、オプション2ではデフォルトのタイプを渡す必要があるため、どちらのアプローチも好きではありません。

IDとタイトルを取得するのと同じ方法で親と子にアクセスできるように、このサンプルをリファクタリングすることは可能ですか。

問題を十分に明確に説明できたと思います。助けていただければ幸いです。ありがとう

4

1 に答える 1

1

の代わりに明示的な型を使用すると、問題がより明確になる可能性がありますvar。で変数を宣言しvarても、変数の型は動的になりません。コンパイラは、変数がどの特定の型である必要があるかを判断するだけです(また、どの型を決定するかが明確にわからないため、危険な場合があります)。

したがって、特定の型で変数を宣言すると(その型が何であるかを知っているかどうかに関係なく)、適切にキャストしない限り、宣言された型が知っているものにのみアクセスできます。

結局のところ、ある時点で型引数(山かっこ)または明示的なキャストとして型を指定しない限り、目的を正確に達成する方法はないと思います。非特定の継承/実装タイプを便利な特定のタイプに変換したいようですが、それには、特定のタイプになるようにコンパイラーに指示する必要があります。

ただし、アプローチを変更することで、希望するものに近づくことができます。非ジェネリックを使用した場合はどうなりますIHierarchyか?(スペルも修正してください)もし...

public inetrface IHierarchy : IHierarchyItem
{
    IHierarchy Parent { get; set; }
    IList<IHierarchy> Children { get; set; }
}

...次に、任意のIHierarchy node変数がアクセスできます...またnode.Parent、が必要であるため、に知られています。node.Childrennode.Idnode.TitleIHierarchyItemIHierarchy

このアプローチは、階層の側面を簡単に処理し、WorkContext.Current(などの)戻り値のポリモーフィズムを可能にしますが、の定義されたメンバーの外部のクラスに固有のものにアクセスするには、そこから明示的にキャストする必要がありますIHierarchy。あなたの目的にどれだけの問題があるのか​​は明らかではありません。

また、その上にジェネリックIHierarchy<T> : IHierarchyを重ねて、キャストせずに特定のタイプで処理できるようにすることもできます。ジェネリック型引数のないプロパティでの名前の衝突を回避するために、(クラスの実装で)暗黙的にではなく明示的に一方または両方のインターフェイスメンバーを定義する必要がある場合があります。

追加するために編集:

たとえば、次のようになります。

public interface IHierarchy<T> : IHierarchy // Implies IHierarchyItem
    where T : IHierarchy<T>
{ ... } // As you had it.

次に、実装クラスで:

public SiteMapNode : IHierarchy<SiteMapNode> // Implies IHierarchy
{
    private SiteMapNode _Parent;
    private IList<SiteMapNode> _Children;

    // Implicit implement of IHierarchyItem and members of SiteMapNode itself.
    int Id { get; set; }
    string Title { get; set; } 

    // Implicit implementation of IHierarchy<SiteMapNode> members
    // These are also members of SiteMapNode itself.

    SiteMapNode Parent
    {
        get { return _Parent; }
        set { _Parent = value; }
    }

    IList<SiteMapNode> Children
    {
        get return _Children;
        set _Children = value;
    }

    // Explicit implementation of IHierarchy members
    // The interface prefix is required to distinguish these from the
    // type-specific members above to declare different return types.

    IHierarchy IHierarchy.Parent
    {
        get { return _Parent; } // Might need (IHierarchy) cast
        set { Parent = (SiteMapNode)value; }
    }

    IList<IHierarchy> IHierarchy.Children
    {
        get { return _Children; } // Might need (IList<IHierarchy>) cast
        set { _Children = (IList<SiteMapNode>)value; }
    }
}

(または、IHierarchyの実装でより精巧になり、より健全性チェックを行うようにします。)必要な他の明示的なキャストも見逃した可能性があります。この方法でリストを直接キャストできるとは思いませんが、IHierarchy <T>にIHierarchyを継承させることで、SiteMapNodeが有効なIHierarchyであり、リストの要素が両方のリストタイプで機能するようになると思います)。そのリストキャストが機能しない場合は、カスタムのジェネリックコレクションクラスを作成して、子を未指定のIHierarchyとジェネリックIHierarchy<T>の両方として管理する必要があります。

パフォーマンス上の理由から、これらのオブジェクトの不特定のIHierarchyキャストを追加IHierarchy _CastParent;して保存し、繰り返し再キャストする必要がないようにすることができます。IList<IHierarchy> _CastChildren;常に特定のタイプにキャストすることをお勧めします(未指定から設定する場合)が、実際に必要になるまで、特定のタイプから未指定の参照へのキャストを延期する場合があります。

もちろん、これはすべて、この余分な複雑さが実際に必要なものに役立つ場合にのみ発生します。これにより、階層オブジェクトを未指定の型(後でより具体的にキャストされるか、まったくキャストされない可能性があります)として、またはキャストせずに処理するための型の知識を保持する特定または汎用の階層型として処理できます。不特定のIHierarchy戻り型から変換する場合は、それらを型固有の階層として処理するために、ある時点で特定の型にキャストする必要があります。

于 2012-09-13T20:25:38.873 に答える