【问题标题】:How do I make a constructor only accessible by base class?如何使构造函数只能由基类访问?
【发布时间】:2012-05-24 13:10:41
【问题描述】:

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

【问题讨论】:

  • 我不确定这是否可能,知道子类的基类有点违背我对继承的理解
  • @DanF 你是对的,但是知道Child1Child2BaseClassFactory 类是正常​​的,并且会有同样的问题。

标签: c# .net inheritance constructor


【解决方案1】:

我认为你有两个选择:

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

【讨论】:

  • 嵌套类只能在 BaseClass 中可见,对吧?不确定这是预期的结果。
  • @lcfseth,是的,如果你将它们标记为private。而且我认为这正是我们想要的(注意工厂方法返回BaseClass)。
  • 没错,我也需要外面的课程。
【解决方案2】:

我想我只是自己解决了它。在阅读了带有嵌套类的 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)
    {
    }
}

【讨论】:

  • 如何防止Child2 实例化Child1?我以为您想保护自己免受自己程序集中的错误实例化? (否则,内部构造函数的解决方案要简单得多。)
  • 实际上,C# 是否允许在公共构造函数上使用不可访问的类型?
【解决方案3】:

问题的答案是“否”

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

【讨论】:

  • @Jon - 它的选项,但不是多级继承中的解决方案......也考虑这种情况......
【解决方案4】:

可以通过让基本构造函数接受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();
}

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

【讨论】:

    【解决方案5】:

    从派生类的类型初始化器传递并注册一个工厂委托,然后你就完成了工作:

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

    Simulate variadic templates in c#

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-25
      • 2012-02-25
      • 2014-08-29
      • 1970-01-01
      • 2011-02-24
      • 2014-03-28
      • 1970-01-01
      • 2021-09-04
      相关资源
      最近更新 更多