1

Here is a highly upvoted solution to easily restrict a nested class constructor to the parent class only. I am trying to achieve exactly this but in the context of a parent class containing a nested generic class. Here is the original snippet for clarity:

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

  public class JournalEntry
  {
    static JournalEntry()
    {
       _newJournalEntry = value => new JournalEntry(value);
    }
  private JournalEntry(object value)
  {
     ...

Here is where I'm currently at (I replaced Func<object,JournalEntry> with a simple Func<JournalEntry> for simplicity sake.)

public class Journal
{
    private static Func<JournalEntry> _new;

    public JournalEntry<T> NewJournalEntry<T>()
    {
        return (JournalEntry<T>)_new();            
    }

    public Journal(){}

    public class JournalEntry {}

    public class JournalEntry<T>:JournalEntry
    {
        static JournalEntry()
        {
            _new = () => new JournalEntry<T>();
        }

        private JournalEntry()
        {

        }
    }
}

Here is the the use case:

Journal j = new Journal();
Journal.JournalEntry<string> stringEntry = j.NewJournalEntry<string>();
//Fails with null reference exception

Per this comment from the original snippet,

System.Runtime.CompilerServices.RuntimeHelpers.RunClassConst‌​ructor can save the day. :)

I gave it a shot:

Journal j = new Journal();
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Journal.JournalEntry<string>).TypeHandle);
Journal.JournalEntry<string> stringEntry = j.NewJournalEntry<string>();
//Passes

The above code works. The following works:

Journal j = new Journal();
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Journal.JournalEntry<string>).TypeHandle);

Journal.JournalEntry<string> stringEntry = j.NewJournalEntry<string>();
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(Journal.JournalEntry<int>).TypeHandle);

Journal.JournalEntry<int> intEntry = j.NewJournalEntry<int>();

But attempting to ask for another JournalEntry<string> fails with:

Additional information: Unable to cast object of type JournalEntry[System.Int32] to type JournalEntry[System.String].

How do I solve this dilemma such that I can make any number of instances of JournalEntry<> with any type?

Community
  • 1
  • 1
NWoodsman
  • 423
  • 4
  • 10

4 Answers4

2

Your approach can't work, because every time the type initializer for a JournalEntry<T> would run (and it would run once for each T), it would overwrite the previous value of _new, breaking future usages of other Ts.

Instead, you could do something like this:

public class Journal
{
    public JournalEntry<T> NewJournalEntry<T>()
    {
        System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(JournalEntry<T>).TypeHandle);
        return JournalEntryFactory<T>._new();
    }

    private static class JournalEntryFactory<T>
    {
        public static Func<JournalEntry<T>> _new;
    }

    public Journal() { }

    public class JournalEntry { }

    public class JournalEntry<T> : JournalEntry
    {
        static JournalEntry()
        {
            JournalEntryFactory<T>._new = () => new JournalEntry<T>();
        }

        private JournalEntry()
        {

        }
    }
}

This way, there's a separate _new for each T.

BTW, I was the one who asked the question you referred to. For what it's worth, I think this approach is a bit hackish and error-prone. If it's possible in your case, I strongly suggest you use an interface-based approach as in the accepted answer. Here's how it would look like in your case:

public interface IJournalEntry<T>
{
}

public class Journal
{
    public IJournalEntry<T> NewJournalEntry<T>()
    {
        return new JournalEntry<T>();
    }

    public Journal() { }

    private class JournalEntry<T> : IJournalEntry<T>
    {
        public JournalEntry()
        {

        }
    }
}
Community
  • 1
  • 1
Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • One particular bit of information not immediately apparent in your original accepted answer with the interface approach: make the nested class definition *private* with a *public* constructor, and miraculously only the outer class can call the constructor, as pointed out by a commenter there. Can I request a final clean snippet how you used the interface approach? – NWoodsman Mar 13 '17 at 04:31
1

You need multiple new methods. I recommend keeping them in a Dictionary, something like this:

public class Journal
{
    private static Dictionary<Type, Func<JournalEntry>> _newFuncs = new Dictionary<System.Type, System.Func<UserQuery.Journal.JournalEntry>>();

    public JournalEntry<T> NewJournalEntry<T>()
    {
        var newFunc = _newFuncs[typeof(T)];
        return (JournalEntry<T>)newFunc();
    }

    public Journal() { }

    public class JournalEntry { }

    public class JournalEntry<T> : JournalEntry
    {
        static JournalEntry()
        {
            _newFuncs.Add(typeof(T), () => new JournalEntry<T>());
        }

        private JournalEntry()
        {

        }
    }
}
DavidG
  • 113,891
  • 12
  • 217
  • 223
0

You can modify your class in order to have _new as a Dictionary instead, which will allow for multiple types to be created:

public class Journal
{
    private static IDictionary<Type, Func<JournalEntry>> _new = new Dictionary<Type, Func<JournalEntry>>();

    public JournalEntry<T> NewJournalEntry<T>()
    {
        return (JournalEntry<T>)_new[typeof(T)]();
    }

    public Journal() { }

    public class JournalEntry { }

    public class JournalEntry<T> : JournalEntry
    {
        static JournalEntry()
        {
            _new.Add(typeof(T), () => new JournalEntry<T>());
        }

        private JournalEntry()
        {

        }
    }

}
StfBln
  • 1,137
  • 6
  • 11
0

Can't do exactly what you're asking very easily (a generic class is actually a set of classes, so you'd need several delegates for _new, and some way to pick one) but here is a very simple way to accomplish what you wish, without the hacky static constructor and delegate, and without using a separate interface.

Essentially we create a factory method that is protected, then create a private derivation of the class with a public version of the method. The protected factory method can only be called from our private class.

public class Journal
{
    public class JournalEntry { }

    public class JournalEntry<T> : JournalEntry
    {
        protected JournalEntry()
        {
        }

        static protected JournalEntry<T> NewJournalEntry()
        {
            return new JournalEntry<T>();
        }
    }

    private class Maker<T> : JournalEntry<T>
    {
        new static public JournalEntry<T> NewJournalEntry()
        {
            return JournalEntry<T>.NewJournalEntry();
        }
    }

    public JournalEntry<T> NewJournalEntry<T>()
    {
        return Maker<T>.NewJournalEntry();
    }
}

You can then create a new instance like this (exactly as you ask):

Journal j = new Journal();
Journal.JournalEntry<string> stringEntry = j.NewJournalEntry<string>();

But you can't instantiate directly:

Journal.JournalEntry<string> wontWork = new Journal.JournalEntry<string>();  //is inaccessible due to its protection level
John Wu
  • 50,556
  • 8
  • 44
  • 80