views:

263

answers:

5

Hi,

Is it possible to specify that members of a nested class can be accessed by the enclosing class, but not other classes ?

Here's an illustration of the problem (of course my actual code is a bit more complex...) :

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; }
    }

    // ...
}

I would like to prevent client code from creating instances of JournalEntry, but Journal must be able to create them. If I make the constructor public, anyone can create instances... but if I make it private, Journal won't be able to !

Note that the JournalEntry class must be public, because I want to be able to expose existing entries to client code.

Any suggestion would be appreciated !


UPDATE: Thanks everyone for your input, I eventually went for the public IJournalEntry interface, implemented by a private JournalEntry class (despite the last requirement in my question...)

+1  A: 

In this case you could either:

  1. Make the constructor internal - this stops those outside this assembly creating new instances or...
  2. Refactor the JournalEntry class to use a public interface and make the actual JournalEntry class private or internal. The interface can then be exposed for collections while the actual implementation is hidden.

I mentioned internal as a valid modifier above however depending on your requirements, private may be the better suited alternative.

Edit: Sorry I mentioned private constructor but you've already dealt with this point in your question. My apologies for not reading it correctly!

Paul Mason
A: 

I would make the JournalEntry constructor internal:

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

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

    // ...
}
jrista
+2  A: 

If your class is not too complex you could either use an interface which is publically visibal and make the actual implementing class private or you could make a protected constructor for the JornalEntry class and have a private class JornalEntryInstance derived from JornalEntry with a public constructor which is actually instantiated by your Journal.

    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);
       }

    }

If your actual class is too complex to do either of that and you can get away with the constructor being not completely invisible, you can make the constructor internal so it is only visible in the assembly.

If that too is infeasible you can always make the constructor private and use reflection to call it from your journal class:

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

Now that I think about it, another possibility would use a private delegate in the containing class which is set from the inner class

    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);
        }

    }

This should give you your desired visibility levels without needing to resort on slow reflection or introducing additional classes / interfaces

Grizzly
The interface is a good solution, don't know why I didn't think of it... Thanks !
Thomas Levesque
+3  A: 

Make JournalEntry a private nested type. Any public members will be visible only to the enclosing type.

public class Journal
{
    private class JournalEntry
    {
    }
}

If you need to make JournalEntry objects available to other classes, expose them via a public interface:

public interface IJournalEntry
{
}

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

    private class JournalEntry : IJournalEntry
    {
    }
}
280Z28
+1  A: 

Actually there is a complete and simple solution to this problem that doesn't involve modifying the client code or creating an interface.

This solution is actually faster than the interface-based solution for most cases, and easier to code.

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

  public class JournalEntry
  {
    static
    {
      _newJournalEntry = (object) => new JournalEntry(object);
    }
    private JournalEntry(object value)
    {
      ...
Ray Burns
Nice and original approach... I have to remember that one. Thanks !
Thomas Levesque