73

ネストされたクラスのメンバーは、それを囲むクラスからはアクセスできるが、他のクラスからはアクセスできないように指定することはできますか?

これが問題の図です(もちろん、私の実際のコードはもう少し複雑です...):

public class Journal
{
    public class JournalEntry
    {
        public JournalEntry(object value)
        {
            this.Timestamp = DateTime.Now;
            this.Value = value;
        }

        public DateTime Timestamp { get; private set; }
        public object Value { get; private set; }
    }

    // ...
}

クライアント コードが のインスタンスを作成できないようにしたいのですが、作成できる必要がありますJournalEntryJournalコンストラクターをパブリックにすると、誰でもインスタンスを作成できます...しかし、プライベートにJournalするとできなくなります!

JournalEntry既存のエントリをクライアント コードに公開できるようにするため、クラスはパブリックである必要があることに注意してください。

任意の提案をいただければ幸いです!


更新: ご意見をお寄せいただきありがとうございます。私は最終的にIJournalEntry、プライベート クラスによって実装されたパブリック インターフェイスを選択しましたJournalEntry(私の質問の最後の要件にもかかわらず...)。

4

7 に答える 7

85

実際、クライアント コードの変更やインターフェイスの作成を必要としない、この問題に対する完全で簡単な解決策があります。

このソリューションは、実際には、ほとんどの場合、インターフェイス ベースのソリューションよりも高速であり、コーディングも簡単です。

public class Journal
{
  private static Func<object, JournalEntry> _newJournalEntry;

  public class JournalEntry
  {
    static JournalEntry()
    {
      _newJournalEntry = value => new JournalEntry(value);
    }
    private JournalEntry(object value)
    {
      ...
于 2009-11-03T06:29:26.577 に答える
58

クラスがそれほど複雑でない場合は、公開されているインターフェイスを使用して実際の実装クラスをプライベートにするか、クラスの保護されたコンストラクターを作成して、実際に公開されているパブリック コンストラクターから派生しJornalEntryたプライベート クラスを作成することができます。によってインスタンス化されます。JornalEntryInstanceJornalEntryJournal

public class Journal
{
    public class JournalEntry
    {
        protected JournalEntry(object value)
        {
            this.Timestamp = DateTime.Now;
            this.Value = value;
        }

        public DateTime Timestamp { get; private set; }
        public object Value { get; private set; }
    }

    private class JournalEntryInstance: JournalEntry
    { 
        public JournalEntryInstance(object value): base(value)
        { }
    }
    JournalEntry CreateEntry(object value)
    {
        return new JournalEntryInstance(value);
    }
}

実際のクラスが複雑すぎてそのいずれかを行うことができず、コンストラクターが完全に非表示になっていないことを回避できる場合は、コンストラクターを内部にして、アセンブリでのみ表示されるようにすることができます。

それも実行不可能な場合は、いつでもコンストラクターをプライベートにして、リフレクションを使用してジャーナル クラスから呼び出すことができます。

typeof(object).GetConstructor(new Type[] { }).Invoke(new Object[] { value });

考えてみると、別の可能性は、内部クラスから設定された包含クラスでプライベートデリゲートを使用することです

public class Journal
{
    private static Func<object, JournalEntry> EntryFactory;
    public class JournalEntry
    {
        internal static void Initialize()
        {
            Journal.EntryFactory = CreateEntry;
        }
        private static JournalEntry CreateEntry(object value)
        {
            return new JournalEntry(value);
        }
        private JournalEntry(object value)
        {
            this.Timestamp = DateTime.Now;
            this.Value = value;
        }

        public DateTime Timestamp { get; private set; }
        public object Value { get; private set; }
    }

    static Journal()
    {
        JournalEntry.Initialize();
    }
        
    static JournalEntry CreateEntry(object value)
    {
        return EntryFactory(value);
    }
}

これにより、遅いリフレクションに頼ったり、追加のクラス/インターフェースを導入したりする必要なく、目的の可視性レベルが得られるはずです

于 2009-11-03T02:18:20.500 に答える
28

入れ子になっJournalEntry型を非公開にします。パブリック メンバーは、外側の型にのみ表示されます。

public class Journal
{
    private class JournalEntry
    {
    }
}

オブジェクトを他のクラスで使用できるようにする必要がある場合はJournalEntry、パブリック インターフェイスを介してそれらを公開します。

public interface IJournalEntry
{
}

public class Journal
{
    public IEnumerable<IJournalEntry> Entries
    {
        get { ... }
    }

    private class JournalEntry : IJournalEntry
    {
    }
}
于 2009-11-03T02:37:04.750 に答える
3

この場合、次のいずれかを実行できます。

  1. コンストラクタを内部にします - これにより、このアセンブリの外部にあるものは新しいインスタンスを作成したり、...
  2. パブリック インターフェイスを使用するようにクラスをリファクタリングしJournalEntry、実際のJournalEntryクラスをプライベートまたは内部にします。その後、実際の実装が隠されている間、インターフェイスをコレクション用に公開できます。

上記で有効な修飾子として internal について言及しましたが、要件によっては、private の方が適切な代替手段になる場合があります。

編集:申し訳ありませんが、プライベートコンストラクターについて言及しましたが、質問のこの点についてはすでに対処しています。読み方が間違っていてすみません!

于 2009-11-03T02:07:34.063 に答える