8

If I want a constructor that is only accessible from child classes I can use the protected key word in the constructor.

Now I want the opposite.

My child class should have an constructor that can be accessed by its base class but not from any other class.

Is this even possible?

This is my current code. the problem is that the child classes have a public constructor.

public abstract class BaseClass
{
    public static BaseClass CreateInstance(DataTable dataTable)
    {
        return new Child1(dataTable);
    }
    public static BaseClass CreateInstance(DataSet dataSet)
    {
        return new Child2(dataSet);
    }
}

public class Child1 : BaseClass
{
    public Child1(DataTable dataTable)
    {
    }
}

public class Child2 : BaseClass
{
    public Child2(DataSet dataSet)
    {
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189
  • 5
    I'm not sure this is possible, a base class knowing about child classes kind of goes against my understanding of inheritance – Dan F May 24 '12 at 13:18
  • 1
    @DanF You're right, but a `BaseClassFactory` class that does know about `Child1` and `Child2` would be normal, and would have the same problem. –  May 24 '12 at 13:20

5 Answers5

13

I think you have two options:

  1. Make the child constructor internal. This means it will be accessible from all types in the same assembly, but that should be enough in most cases.
  2. Make the child classes nested in the base class:

    public abstract class BaseClass
    {
        public static BaseClass CreateInstance(DataTable dataTable)
        {
            return new Child1(dataTable);
        }
    
        private class Child1 : BaseClass
        {
            public Child1(DataTable dataTable)
            {
            }
        }
    }
    

    This way, BaseClass can use the constructor, but no other outside type can do that (or even see the child class).

Ken Kin
  • 4,503
  • 3
  • 38
  • 76
svick
  • 236,525
  • 50
  • 385
  • 514
3

I think I just solved it by myself. After reading svicks solution with nested classes, I thought why not use an protected nested class as an argument?

Nobody from outside is able to create an instance of Arg and the public contructors from my child classes can only be used by BaseClass which can create Arg<T> instances.

public abstract class BaseClass
{
    protected class Arg<T>
    {
        public T Value { get; set; }
        public Arg(T value) { this.Value = value; }
    }

    public static BaseClass CreateInstance(DataTable dataTable)
    {
        return new Child1(new Arg<DataTable>(dataTable));
    }

    public static BaseClass CreateInstance(DataSet dataSet)
    {
        return new Child2(new Arg<DataSet>(dataSet));
    }
}


public class Child1 : BaseClass
{
    public Child1(Arg<DataTable> arg) : this(arg.Value) { }
    private Child1(DataTable dataTable)
    {
    }
}

public class Child2 : BaseClass
{
    public Child2(Arg<DataSet> arg) : this(arg.Value) { }
    public Child2(DataSet dataSet)
    {
    }
}
Jürgen Steinblock
  • 30,746
  • 24
  • 119
  • 189
  • 1
    How does this protect against `Child2` instantiating a `Child1`? I thought you wanted to protect yourself even against wrong instantiations in your own assembly? (Otherwise, the solution of an internal constructor is much simpler.) –  May 24 '12 at 13:39
  • Actually, does C# even allow an inaccessible type on a public constructor? –  May 24 '12 at 13:48
2

Answer to the question is "NO"

There is no such thing exists in the OOP that allow child class constructor to visible only to the Base Class of it...

Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
0

One could enforce the desired behavior at run-time by having the base constructor accept a ref parameter, and do something like (not threadsafe):

private int myMagicCounter;

public DerivedClass makeDerived(whatever) // A factory method
{
  DerivedClass newThing;
  try
  {
    ... do whatever preparation
    newThing = new DerivedClass(ref myMagicCounter, whatever);
  }
  finally
  {
    ... do whatever cleanup
  }
  return newThing;
}

BaseClass(ref int magicCounter, whatever...)
{
  if (magicCounter != myMagicCounter)
    throw new InvalidOperationException();
  myMagicCounter++;
  if (magicCounter != myMagicCounter)
    throw new InvalidOperationException();
}

Note that it will be impossible for a derived class constructor call to get control without having done the factory method's preparation, or to return control to its caller without doing the factory method's cleanup. There will, however, be nothing to prevent the derived-class constructor from passing its partially-constructed instance to outside code which may do whatever it likes with it for an arbitrary amount of time before returning control to the factory method.

svick
  • 236,525
  • 50
  • 385
  • 514
supercat
  • 77,689
  • 9
  • 166
  • 211
0

Pass and register a factory delegate from the type initializer of derived classes then you just get the job done:

public abstract class BaseClass {
    static readonly Dictionary<Type, Delegate>
            m_factories = new Dictionary<Type, Delegate> { };

    public static BaseClass CreateInstance(DataTable dataTable) {
        var type = typeof(Child1);
        RuntimeHelpers.RunClassConstructor(type.TypeHandle);
        return (Child1)m_factories[type].DynamicInvoke(dataTable);
    }

    public static BaseClass CreateInstance(DataSet dataSet) {
        var type = typeof(Child2);
        RuntimeHelpers.RunClassConstructor(type.TypeHandle);
        return (Child2)m_factories[type].DynamicInvoke(dataSet);
    }

    protected static void AddFactory<TArgs, T>(Func<TArgs, T> factory) {
        m_factories.Add(typeof(T), factory);
    }
}

public class Child1:BaseClass {
    Child1(DataTable dataTable) {
    }

    static Child1() {
        BaseClass.AddFactory((DataTable dt) => new Child1(dt));
    }
}

public class Child2:BaseClass {
    Child2(DataSet dataSet) {
    }

    static Child2() {
        BaseClass.AddFactory((DataSet ds) => new Child2(ds));
    }
}

public static class TestClass {
    public static void TestMethod() {
        var child2 = BaseClass.CreateInstance(new DataSet { });
        var child1 = BaseClass.CreateInstance(new DataTable { });
    }
}

If all of the derived classes inherited from the base class directly then don't you worry about the collision of registration -- no body can access a constructor from another class.

For TArgs of Func<TArgs, T> you might want to declare it like variadic generic arguments although it's just not a feature of C♯, Tuple is one of the approaches to simulate it. For more information on this topic, you might want to have a look at:

Simulate variadic templates in c#

Ken Kin
  • 4,503
  • 3
  • 38
  • 76