Visibility of nested class constructor
Asked Answered
B

7

17

Is there a way to limit the instantiation of the nested class in C#? I want to prevent nested class being instantiated from any other class except the nesting class, but to allow full access to the nested class from other code.

Bergess answered 29/4, 2010 at 11:24 Comment(4)
Sounds like you are reinventing the Singleton design pattern.Hinds
@Bastiaan: you mean implementing. You don't re-invent design patterns....Earlearla
Declare members that you don't want to be accessible, including the constructor, internal. It is the default.Jemmie
Problem with internal is that it still leaves those members accessible to other types within the assembly. What C# needs is a "parent" visibility that only allows access from the type enclosing the nested type.Ezra
S
37

Usually I create an interface for the functionality you want to expose to other classes, then make the nested class private and implement that interface. This way the nested class definition can stay hidden:

public class Outer
{
    private class Nested : IFace
    {
        public Nested(...)
        {
        }
        //interface member implementations...
    }

    public IFace GetNested()
    {
        return new Nested();
    }
}
Sociable answered 29/4, 2010 at 11:35 Comment(0)
F
14

If you need to meet one of the following requirements:

  • You want the nested class to be sealed,
  • You don't want to copy all the nested class's method signatures to an interface like in Lee's answer,

I found a solution similar to the one posted by ak99372, but without using a static initializer:

public class Outer
{
    private interface IPrivateFactory<T>
    {
        T CreateInstance();
    }

    public sealed class Nested
    {
        private Nested() {
            // private constructor, accessible only to the class Factory.
        }

        public class Factory : IPrivateFactory<Nested>
        {
            Nested IPrivateFactory<Nested>.CreateInstance() { return new Nested(); }
        }
    }

    public Nested GetNested() {
        // We couldn't write these lines outside of the `Outer` class.
        IPrivateFactory<Nested> factory = new Nested.Factory();
        return factory.CreateInstance();
    }
}

The idea is that the Nested class's constructor is accessible only to the Factory class, which is nested one level deeper. The Factory class explicitly implements the method CreateInstance from the private interface IPrivateFactory, so that only those who can see IPrivateFactory can call CreateInstance and get a new instance of Nested.

Code outside the Outer class can't freely create instances of Nested without asking Outer.GetNested(), because

  1. Outer.Nested's constructor is privated, so they can't call it directly
  2. Outer.Nested.Factory can be instantiated, but can't be cast to IPrivateFactory, so its CreateInstance() method can't be called.

Note that I wouldn't recommend using that pattern heavily in production code, but it's a trick I find useful to have up my sleeve on rare occasions.

Furthermost answered 2/2, 2014 at 15:45 Comment(0)
N
8

In short, no, you cannot do that. There is an accessibity modifier "public" which means "accessible by anything inside me or outside me" and there is an accessibility modifier "private" which means "accessible by anything inside me". There is no modifier which means "accessible to the thing immediately outside me but not to anything outside it", which is what you would need to mark the constructor as. That's simply not a concept that the designers of the type system thought would be useful.

Can you describe why you want this crazy kind of accessibility? Perhaps there is a better way to get what you want.

Nightingale answered 29/4, 2010 at 14:9 Comment(2)
Off the top of my head (because it's my concern with this): Implementing IAsyncResult.Ezra
I want a public sealed nested class which only the containing class itself can instantiate so that I know that all instances are created by the containing class. My requirements match this answer which is nifty. But it’d be nice if this could be done more succinctly.Wheelock
C
4

Since there is nothing in C# syntax you'll have to implement something like "a contract" between them. You can take advantage of the fact that nested class can access private fields of its parent:

public class ParentClass
{
  private static Func<FriendClass> _friendContract;

  public class FriendClass
  {
      static FriendClass()
      {
          _friendContract= () => new FriendClass();
      }

      private FriendClass() { }
  }

  ///Usage
  public FriendClass MethodUse()
  {
      var fInstance = _friendContract();
      //fInstance.DoSomething();
      return fInstance;
  }
}

Of course you can adjust the contract to handle different parameters

 private static Func<Arg1,Arg2,FriendClass> _friendContract;
Carruthers answered 23/1, 2014 at 21:50 Comment(1)
for me the static constructor of the inner class doesn't get called and I get a nullreferenceexceptionNjord
P
3

With the new static abstract interface members of C# 11 you can limit the instantiation of nested classes quite neatly:

public class Outer
{
    protected interface INestedFactory<T> where T : INestedFactory<T>
    {
        public static abstract T CreateInstance();
    }

    public class SomeNested : INestedFactory<SomeNested>
    {
        private SomeNested() { }

        static SomeNested INestedFactory<SomeNested>.CreateInstance()
        {
            return new SomeNested();
        }
    }

    protected void CreateNested<T>() where T : INestedFactory<T>
    {
        T.CreateInstance();
    }
}
Perez answered 25/4, 2022 at 18:8 Comment(0)
I
2

For the answer proposed by Joshua Smith I found it necessary to force the static constructor of FriendClass to run, achieved by calling an empty static Initalize() method on FriendClass from the static constructor of ParentClass.

Ilonailonka answered 27/4, 2016 at 11:32 Comment(1)
You should comment Joshua's answer rather than posting a new answer which isn't really one.Jap
C
0
public class Outer
{
    public class Nested
    {
        readonly Outer Outer;

        public Nested(Outer outer /* , parameters */)
        {
            Outer = outer;

            // implementation
        }

        // implementation
    }

    public Nested GetNested(/* parameters */) => new Nested(this /* , parameters */);
}

Note that you can access private members of Outer from Nested.

Cervelat answered 7/11, 2017 at 2:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.