8

如果我想要一个只能从子类访问的构造函数,我可以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)
    {
    }
}
4

5 回答 5

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可以使用构造函数,但没有其他外部类型可以做到这一点(甚至看到子类)。

于 2012-05-24T13:19:01.743 回答
3

我想我只是自己解决了。在阅读了嵌套类的 svicks 解决方案后,我想why not use an protected nested class as an argument?

外部没有人能够创建 Arg 的实例,而我的子类中的公共构造函数只能由可以创建Arg<T>实例的 BaseClass 使用。

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)
    {
    }
}
于 2012-05-24T13:34:15.897 回答
2

问题的答案是“否”

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

于 2012-05-24T13:17:18.330 回答
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();
}

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

于 2012-05-24T16:29:32.520 回答
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 { });
    }
}

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

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

在 C# 中模拟可变参数模板

于 2018-05-22T18:57:19.850 回答