How do I make a constructor only accessible by base class?

天大地大妈咪最大 提交于 2019-12-23 07:47:42

问题


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

回答1:


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).




回答2:


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



回答3:


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...




回答4:


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.




回答5:


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#



来源:https://stackoverflow.com/questions/10738163/how-do-i-make-a-constructor-only-accessible-by-base-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!