X++ の拡張モデルのクラス

この記事では、X++ で新しいクラス拡張モデルについて説明します。

オーバーレイは非常に侵入的な機能なので、使用しないことをお勧めします。 オーバーレイに代わる方法は、拡張です。 拡張子を使用すると、既存のコンポーネントを新しいモデルで拡張できます。 拡張子は維持しやすいですが、カスタマイズ時に拡張できる量は限られています。 メタデータを拡張する豊富な方法があります。 たとえば、テーブルに新しいフィールドを追加できます。 この記事では、X++ コードを拡張する方法について説明し、これらのモデルを再コンパイルせずに他のモデルで定義されているコンポーネントにメソッドと状態を追加できるようにします。 X++ の同様のコード拡張メカニズムが存在し、C# で対応する機能の後でモデル化されます。 このメカニズムにより、クラスは、命名規則に従い public static メソッドをホストすることにより、拡張クラスとして指定することができます。 既存の機能では、拡張メソッドに渡される最初の引数の型は、拡張する型です。 この記事に記載する内容はその方向の次のステップであり、役に立つありのままの拡張についての説明です。 オブジェクト指向のプログラミングでは、拡張 という用語には、明確に定義された意味があります。 「クラス B がクラス A を拡張する」と表現する場合、B は A が B の親のクラスであることを A から継承し、通常のオブジェクト指向ルールが暗黙的に適用されます。 実際、この用語は、この関係を表現するクラス宣言で使用される X++ 構文でも使用されます。 同時に複数のモデルから寄せられたメタデータについて拡張という用語を使用して説明します。 拡張という用語の多用を避ける目的で、クラス強化 という代替用語を使用して、基本モデルのクラス A とそれに依存するモデルのクラス B の間の関係を指定します。ここで、B はそのモデルのコンテキストでクラス A に追加の機能を提供します。 ただし、拡張クラスという用語が一般的であるため、引き続き使用します。

有効なクラスの概念

強化されたコンポーネントのパブリック メンバー、およびそのコンポーネントを強化するすべてのクラスの拡張機能のすべてのパブリック メンバーで構成されるクラスに条件を設けるために役立ちます。 このクラスは、指定されたモデルの有効なクラスと呼ばれます。 次の図は、基本モデルで定義された、MyArtifact および MyArtifactの拡張クラスを持つ 2 つの依存モデルで定義されたコンポーネント MyModel を示しています。

ベースモデル MyModel で定義されているコンポーネント MyArtifact、および MyArtifact の拡張クラスを持つ 2 つの依存モデル。

この例では、有効なクラスはすべてのオリジナルのメソッドおよび拡張クラスからのすべてのパブリック コンポーネントを含む拡張モデルのクラスです。 有効なクラスは、特定のモデルで定義されたクラスの拡張のみを含むため、すべてのモデルで同じではありません。 次の図は、MyExtensionModel モデルの MyArtifact の有効クラスを示しています。

MyExtensionModel の MyArtifact の有効クラス。

MyModel という名前のモデル内の MyClass という名前のクラスを使用して、クラス拡張について説明します。

class MyClass
{
    public int mycState;
    public str mycMethod(int _arg)
    {
        // ...
    }
}

MyModel の上に構築される (つまり、MyModel に依存する) 拡張モデル (MyExtensionModel) に拡張クラスを導入することで、MyClass に新しいメソッドと状態を追加できます。

拡張子のクラス宣言

拡張クラスは、ExtensionOf 属性で修飾されており、また拡張子の接尾辞を持つ名称も持つ最終クラスです。 (この名前付けの制限は、後で削除される可能性があります。) 拡張クラスの名前は、それ以外の場合重要ではありません。 このクラスは、次の例に示すように、ExtensionOf 属性のパラメーターで指定されたコンポーネントを補強します。

[ExtensionOf(classStr(MyClass))]
final class MyClass_Extension
{
    private void new()
    {
    }
}

クラスはランタイム システムによってインスタンス化されるため、拡張クラスから派生する意味がありません。 したがって、拡張クラスは final としてマークする必要があります。 拡張クラス MyClass_Extension は、指定されたクラス (MyClass) を拡張しません。 したがって、MyClass_ExtensionMyClass からメソッドをオーバーライドすることはできません。 補強クラスを指定するには、classStr コンパイル時関数を使用する必要があり、これは次の 2 つの目的に使用できます。

  • MyClass クラスが存在しない場合、コンパイル エラーを生成します。
  • どのような種類のコンポーネントが拡張されるかをコンパイラに知らせるために使用するコンパイル時関数。 コンポーネント名単独では、拡張するために指定されたコンポーネントを一意に識別しません。 たとえば、フォームはテーブル、クラス、および列挙として同じ名前を持つことができます。

任意の数の拡張クラスが、特定のモデル内で付与されたコンポーネントを強化することができます。 拡張子クラスは、ランタイム システムでのみプログラマが直接参照することはありません。

拡張子のクラス継承

拡張されたクラスから継承するクラスも、有効なクラスを継承します。 つまり、拡張機能を持つクラスから継承するクラスは、拡張クラスで定義されているメソッドを継承します。

コンストラクター

X++ はインスタンス コンストラクターおよび静的コンストラクターの両方をサポートしています。

インスタンス コンストラクター

インスタンス コンストラクターは、new という名前のメソッドです。 コンストラクターは、拡張オブジェクトの状態を初期化するのに便利です。 拡張クラスで定義されているインスタンス コンストラクターはパラメーターを指定できません。 拡張クラスのインスタンスが作成され、ランタイム システムが使用シナリオで必要なコンストラクターを呼び出します。 これらのコンストラクターは、コードによって明示的に呼び出されることはありません。 拡張機能クラスのインスタンス メソッドまたはインスタンスの状態にアクセスする前に、拡張機能クラスに提示されているコンストラクターが必ず一度呼び出されます。 ただし、そのような参照が行われない場合、コンストラクタは呼び出されません。

静的コンストラクター

静的コンストラクターは、typenew と呼ばれるパラメーターなしの静的メソッドです。 静的コンストラクターは、拡張クラスで定義することができます。 ランタイム システムが拡張機能の種類を最初に参照する前に必ず一度コンストラクターが呼び出されることが保証されます。 拡張子のセットの間での静的コンストラクションの呼び出しの特定の任意の順序を想定することはできません。 つまり、静的コンストラクター内の他のクラスから静的データを参照する場合には注意する必要があります。

メソッド

拡張クラスで定義されているパブリック メソッドは、拡張クラスが定義されているモデルのコンテキストで、拡張クラスに追加の機能を提供します。 この方法で、パブリック メソッドだけが公開されます。 パブリック メソッドの実装に役立てるためにプライベート メソッドを定義することができますが、これらのプライベート メソッドは有効なクラスの一部ではありません。 拡張クラスは最終的版であるため、メソッドを 保護された ものとしてマークすることはできません。

インスタンス メソッド

次の例では、あるクラス内の ExtensionMethod という名前の拡張メソッドを定義し、MyClass を補強します。

[ExtensionOf(classStr(MyClass))]
final class MyClass_Extension
{
    private void new()
    {
    }
    public int ExtensionMethod(int arg)
    {
    }
}

パブリック インスタンス メソッド (ExtensionMethod) は、拡張クラスで定義されます。 したがって、拡張クラスが定義されているモデルのコンテキストで MyClass で定義されているかのように使用できます。 次の例は、モデル内のメソッドを呼び出す方法を示しています。

MyClass c = new MyClass();
print c.ExtensionMethod(32);

拡張クラスで定義されているインスタンス メソッドは、強化されたコンポーネントのインスタンス メソッドとして使用されることに注意してください。 拡張メソッドは、拡張をする成果物からのみパブリック メンバーと保護されたメンバーにアクセスできます。 この動作は仕様です。 コンポーネントは、private、または internal キーワードを通じて明示的に非表示にされる状態およびメソッドと直接やり取りできるようにする必要はありません。 それ以外の場合、明示的に非表示な状態とメソッドを直接操作すると、それらのコンポーネントの重要な実装の前提条件を無効にすることで障害が発生する可能性があります。 メソッドとメソッド本体のステートメントは、this キーワードを使用できます。 このコンテキストでは、this の型は強化されたコンポーネントの有効なクラスです。

静的メソッド

拡張クラスでパブリックおよび静的として定義されているメソッドは、強化されているコンポーネントの静的メソッドとして使用できます。

[ExtensionOf(classStr(MyClass))]
final class MyClass_Extension
{
    private void new()
    {
    }
    public int method1(int arg)
    {
    }
    public static real CelsiusToFahrenheit(real celsius)
    {
        return (celsius * 9.0 / 5.0) + 32.0;
    }
}

次の例は、モデル内のメソッドを呼び出す方法を示しています。

var temp = MyClass::CelsiusToFahrenheit(20.0);

静的メソッドは、パブリックの静的メソッドおよび強化されたコンポーネントの有効なクラスの状態にアクセスできます。 興味深い副作用として、グローバル クラスの静的拡張メソッドは接頭語なしで利用できる関数として言語で使用可能になります。

行政単位 (区画)

成果物に対する静的およびインスタンス メソッドを提供することに加えて、インスタンス状態と静的状態を追加できます。

インスタンスの状態

コンポーネントの特定のインスタンスに関連する状態であるインスタンスの状態は、拡張クラスで指定できます。 次の例では、state という名前の状態を定義しています。

[ExtensionOf(classStr(MyClass))]
final class MyClass_Extension
{
    public int state;
    private void new()
    {
    }
}

次の例は、コード内で state を使用する方法を示しています。

MyClass c = new MyClass();
c.state = 12;

静的な状態

静的な状態は、タイプのインスタンスではなく、タイプに対して適用されます。 次の例では、拡張 クラスで staticState という静的メンバーを定義して、 MyClass のクラスを拡張します。

[ExtensionOf(classStr(MyClass))]
final class MyClass_Extension
{
    public int state;
    public static int staticState;
    static void TypeNew()
    {
        staticState = 77;
    }
}