如何使构造函数只能由基类访问?

时间:2012-05-24 13:10:41

标签: c# .net inheritance constructor

如果我想要一个只能从子类访问的构造函数,我可以在构造函数中使用protected关键字。

现在我想要反其道而行之。

我的子类应该有一个构造函数,可以通过其基类访问,但不能从任何其他类访问。​​

这甚至可能吗?

这是我目前的代码。问题是子类有一个公共构造函数。

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

5 个答案:

答案 0 :(得分:13)

我认为你有两个选择:

  1. 制作子构造函数internal。这意味着它可以从同一个程序集中的所有类型访问,但在大多数情况下这应该足够了。
  2. 使子类嵌套在基类中:

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

    这样,BaseClass可以使用构造函数,但没有其他外部类型可以这样做(甚至可以看到子类)。

答案 1 :(得分:3)

我想我自己就解决了这个问题。在阅读了带有嵌套类的svicks解决方案之后,我想到了why not use an protected nested class as an argument?

没有人能够创建一个Arg实例,而我的子类中的公共结构只能由BaseClass使用,而BaseClass可以创建Arg<T>个实例。

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

答案 2 :(得分:2)

回答问题是“不”

OOP中不存在允许子类构造函数仅对其基类可见的东西......

答案 3 :(得分:0)

可以通过让基础构造函数接受ref参数并执行类似(而不是线程安全)的操作来在运行时强制执行所需的行为:

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();
}

请注意,派生类构造函数调用不可能在没有完成工厂方法准备的情况下获得控制权,或者在不进行工厂方法清理的情况下将控制权返回给调用者。但是,没有什么可以阻止派生类构造函数将其部分构造的实例传递给外部代码,外部代码可以在将控制权返回到工厂方法之前,在任意时间内对其执行任何操作。

答案 4 :(得分:0)

从派生类的类型初始化程序传递并注册工厂委托,然后您就可以完成工作了:

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

如果所有派生类直接从基类继承,那么您不必担心注册的冲突 - 没有主体可以从另一个类访问构造函数。

对于TArgs Func<TArgs, T>,您可能希望将其声明为可变参数泛型参数,尽管它不是C♯的一个特性,Tuple是模拟它的方法之一。有关此主题的更多信息,您可能需要查看:

Simulate variadic templates in c#