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?

4 Answers 4

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()
        {

        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

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?
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()
        {

        }
    }
}

Comments

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()
        {

        }
    }

}

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.