web-dev-qa-db-ja.com

コンストラクターのメソッド、悪いですか?

Windowsフォームがあり、テキストファイルをチェックして特定の側面があることを確認するクラスがあります。今、私はコンストラクターにメソッドを持っています、そしてそれは少し奇妙に思えます。コンストラクターを空白のままにして、start()タイプのメソッドを実装して呼び出す必要がありますか?これまでのところ、私のコードは次のようになっています

public class Seating
{
    private int segments = 0;
    public Seating()
    {
        checkInvoice();
        getSegmentCount();          
    }
}
20
Spooks

コンストラクターにメソッドがあり、少し奇妙に思えます

さて、なぜ他にコンストラクターが存在するのでしょうか?コンストラクターにメソッドを含めることは完全に正当です。失敗する可能性のあるメソッドであっても(したがって、オブジェクトの作成が中断されます)。これがあなたの特定のケースに適切であるかどうかを言うのは難しいです。

一般的には問題ありません。

27
Konrad Rudolph

コンストラクターで非仮想メソッドを呼び出すことに問題はありません。コンストラクターが起動するまでに、親オブジェクトは完全に構築されています。ただし、not仮想メソッドを呼び出したい場合は、サブクラスによってオーバーライドされる可能性があります。サブクラスは、完全に構築されていない場合にサブクラス内でコードを実行します。

8
Adam Robinson

一般的なルールは、非常に簡単で、コンストラクターで例外をスローする可能性が低いメソッドを使用することです。メソッドが何らかの理由(ファイルアクセスまたは欠落、DBの問題、null参照...)で失敗する可能性がある場合、コンストラクターが失敗しないことを期待する必要があるため、コンストラクターに含めるべきではありません(特にパラメーターのないコンストラクター、ただし一般的なガイダンス指定しません)。 var seating = new Seating();がエラーの原因であると人々は期待すべきではありません。

前述のように、インスタンスメソッドとしてStart()/Initialize()種類のメソッドを追加できます。必要な2つのメソッドを呼び出した後、クラスの新しいインスタンスを返す新しい静的メソッドを追加して、コンストラクターをプライベートにすることもできます。さらに進んで、このメソッドを新しいFactoryクラスに含めることができます(コンストラクターを内部にします)。あなたはそれをする他の方法も考えることができます。

1つは、これらのメソッドで計算または取得された値がコンストラクターパラメーターである(パラメーターなしのコンストラクターがない)場合、コンストラクターはそれらのパラメーターのみをそれぞれのフィールド/プロパティに割り当てることです。同じアセンブリまたは他の「サービス」専用アセンブリ内のファクトリメソッドまたはサービスメソッドは、メソッドの呼び出し、パラメータの取得、コンストラクタへの受け渡し、およびクラスの新しいインスタンスの返送を担当できます。これは私の個人的なお気に入りです。

一般的に言って、この種の問題は、クラスの実行が多すぎることを示しているため、機能を他のクラス(既存または新規)に分割することをお勧めします。そのため、最後の解決策が提案され、2つのメソッド自体が他の「サービス」アセンブリに含まれるようになりますが、前述のように、必要に応じて他の多くの方法でそれを行うことができます。

更新:

コンストラクターに関するMicrosoftのガイドラインは次のとおりです。
コンストラクター使用ガイドライン

ページからの引用:

コンストラクターで実行される作業の量を最小限に抑えます。コンストラクターは、1つまたは複数のコンストラクターパラメーターをキャプチャする以上のことを行うべきではありません。これにより、ユーザーがインスタンスの特定の機能を使用するまで、以降の操作を実行するコストが遅延します。

アップデート2

上記のページは、 Constructor Design という名前で生きているドキュメント(つまり、更新可能)に移動しました。

7
Meligy

一般的に、コンストラクターはオブジェクトの状態のみを設定する必要があります。メソッドの呼び出しを意味する可能性があります。非常に長いメソッドを実行するのは良い考えではないと思います。クラスのユーザーは、コンストラクターが高価なメソッドになることを期待していません。

仮想メソッドについて言及しなかったわけではありませんが、コンストラクターでこれらを呼び出さないでください。

すばやく元に戻す方法の例を次に示します。

_public class MyBase
{
        protected MyBase()
        {
                this.VirtualMethod();
        }

        protected virtual void VirtualMethod()
        {
                Console.WriteLine("VirtualMethod in MyBase");
        }
}

public class MyDerived : MyBase
{
        private readonly string message = "Set by initializer";

        public MyDerived(string message)
        {
                this.message = message;
        }

        protected override void VirtualMethod()
        {
                Console.WriteLine(this.message);
        }
}
_

さて、あなたがこのコードを他の場所に持っているとしましょう:

_MyDerived d = new MyDerived("Called from constructor");
_

コンソールには何が表示されると思いますか? 「初期化子で設定」と言った場合は、正解です。

これが理由です:

  • すべてのフィールド初期化子は、コンストラクターのコードの前に実行されます。
  • C#コンパイラは、ユーザー定義の前に基本コンストラクターへの呼び出しを追加します。この場合、
    MyBaseVirtualMethod()を呼び出します。 dの実行時型はMyDerivedであるため、MyDerivedVirtualMethod()のオーバーライドは
    実行されました。そして今、MyDerivedのコンストラクターの本体は
    まだ実行されていますが、this.messageには、
    イニシャライザー。
  • これで、MyDerivedのコンストラクターの本体が実行されます。
  • 後でこのインスタンスでVirtualMethod()を呼び出すと、出力されます。
    「コンストラクターから呼び出されました」。
6
Bryan

コンストラクターでの仮想メソッド呼び出しは実行できません(メソッドを事実上非仮想にする封印されたクラスの小さな例外を除いて)。

非仮想メソッドは問題ない場合がありますが、すべてのメンバーが事前に初期化されていることを確認する必要があります。非仮想メソッドが仮想メンバーを呼び出さない場合もあります。

5
Lucero

ここには、コンストラクターの使用に役立つ設計ガイドラインがいくつかあります。

コンストラクター設計(MSDN)

テキストは次の場所から削除されます。

フレームワーク設計ガイドライン:再利用可能な.NETライブラリの規則、イディオム、およびパターン

これは価値のある購入です。

私が何年も前に噛まれたのは: "コンストラクター内のオブジェクトの仮想メンバーを呼び出さないでください。"

2
Kev