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

次の 2 つのオプションがあると思います。

  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

私はそれを自分で解決したと思います。ネストされたクラスを使用したsvicsソリューションを読んだ後、私は思ったwhy not use an protected nested class as an argument?

外部から誰も Arg のインスタンスを作成できず、子クラスのパブリック コンストラクターは、インスタンスを作成できる 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)
    {
    }
}
于 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 { });
    }
}

すべての派生クラスが基本クラスから直接継承されている場合、登録の衝突について心配する必要はありません。別のクラスからコンストラクターにアクセスするボディはありません。

可変引数のジェネリック引数のように宣言したい場合がありますが、これは C♯ の機能ではありませんが、それTArgsをシミュレートするアプローチの 1 つです。このトピックの詳細については、以下を参照してください。Func<TArgs, T>Tuple

C# で可変個引数テンプレートをシミュレートする

于 2018-05-22T18:57:19.850 に答える