次の方法で共有


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

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

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

拡張子のクラス継承

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

コンストラクター

次に、拡張クラスのコンストラクタに関する制限と要件について説明します。

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

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

静的コンストラクター

静的コンストラクターは、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); // Call the extension method from MyClass_Extension on the instance of the class

表示されているように、拡張クラスで定義されているインスタンス メソッドは、強化されたコンポーネントのインスタンス メソッドとして使用されることに注意してください。 拡張メソッドは、拡張をする成果物からのみパブリック メンバーと保護されたメンバーにアクセスできます。 この動作は仕様です。 コンポーネントは、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;
    }
}