次の方法で共有


チュートリアル: C での動的オブジェクトの作成と使用#

動的オブジェクトは、コンパイル時ではなく、実行時にプロパティやメソッドなどのメンバーを公開します。 動的オブジェクトを使用すると、静的な型または形式に一致しない構造体を操作するオブジェクトを作成できます。 たとえば、動的オブジェクトを使用して HTML ドキュメント オブジェクト モデル (DOM) を参照できます。このモデルには、有効な HTML マークアップ要素と属性の任意の組み合わせを含めることができます。 各 HTML ドキュメントは一意であるため、特定の HTML ドキュメントのメンバーは実行時に決定されます。 HTML 要素の属性を参照する一般的なメソッドは、その属性の名前を要素の GetProperty メソッドに渡すことです。 HTML 要素id<div id="Div1">属性を参照するには、最初に<div>要素への参照を取得してから、divElement.GetProperty("id")を使用します。 動的オブジェクトを使用する場合は、 id 属性を divElement.idとして参照できます。

動的オブジェクトを使用すると、IronPython や IronRuby などの動的言語に簡単にアクセスできます。 動的オブジェクトを使用して、実行時に解釈される動的スクリプトを参照できます。

遅延バインディングを使用して動的オブジェクトを参照します。 遅延バインディング オブジェクトの型を dynamicとして指定します。詳細については、「 動的」を参照してください。

System.Dynamic名前空間のクラスを使用して、カスタム動的オブジェクトを作成できます。 たとえば、 ExpandoObject を作成し、実行時にそのオブジェクトのメンバーを指定できます。 DynamicObject クラスを継承する独自の型を作成することもできます。 その後、 DynamicObject クラスのメンバーをオーバーライドして、実行時の動的機能を提供できます。

この記事には、次の 2 つの独立したチュートリアルが含まれています。

  • テキスト ファイルの内容をオブジェクトのプロパティとして動的に公開するカスタム オブジェクトを作成します。
  • IronPython ライブラリを使用するプロジェクトを作成します。

[前提条件]

  • .NET デスクトップ開発ワークロードがインストールされている Visual Studio 2022 バージョン 17.3 以降のバージョン。 このワークロードを選択すると、.NET 7 SDK が含まれます。

次の手順では、一部の Visual Studio ユーザー インターフェイス要素の名前や場所がコンピューターに異なる場合があります。 これらの要素は、使用している Visual Studio エディションと使用する設定によって決まります。 詳細については、「IDEのカスタマイズ」を参照してください。

  • 2 番目のチュートリアルでは、 IronPython for .NET をインストールします。 ダウンロード ページ に移動して、最新バージョンを入手します。

カスタム動的オブジェクトを作成する

最初のチュートリアルでは、テキスト ファイルの内容を検索するカスタム動的オブジェクトを定義します。 動的プロパティは、検索するテキストを指定します。 たとえば、呼び出し元のコードで dynamicFile.Sampleが指定されている場合、動的クラスは、"Sample" で始まるファイルのすべての行を含む文字列の汎用リストを返します。 検索では大文字と小文字が区別されません。 動的クラスでは、2 つの省略可能な引数もサポートされています。 最初の引数は、動的クラスが行の先頭、行の末尾、または行内の任意の場所で一致を検索することを指定する検索オプションの列挙値です。 2 番目の引数は、動的クラスが検索する前に各行の先頭と末尾のスペースをトリミングすることを指定します。 たとえば、呼び出し元のコードで dynamicFile.Sample(StringSearchOption.Contains)が指定されている場合、動的クラスは 1 行の任意の場所で "Sample" を検索します。 呼び出し元のコードで dynamicFile.Sample(StringSearchOption.StartsWith, false)が指定されている場合、動的クラスは各行の先頭にある "Sample" を検索し、先頭と末尾のスペースを削除しません。 動的クラスの既定の動作は、各行の先頭で一致を検索し、先頭と末尾のスペースを削除することです。

カスタム動的クラスを作成する

Visual Studio を起動します。 [ 新しいプロジェクトの作成] を選択します。 [ 新しいプロジェクトの作成 ] ダイアログで、[C#] を選択し、[ コンソール アプリケーション] を選択し、[ 次へ] を選択します。 [新しいプロジェクトの構成] ダイアログで、DynamicSampleに「」と入力し、[次へ] を選択します。 [追加情報] ダイアログで、ターゲット フレームワーク.NET 7.0 (Current) を選択し、[作成] を選択します。 ソリューション エクスプローラーで、DynamicSample プロジェクトを右クリックし、追加>Class を選択します。 [ 名前 ] ボックスに「 ReadOnlyFile」と入力し、[ 追加] を選択します。 ReadOnlyFile.csまたはReadOnlyFile.vb ファイルの先頭に、次のコードを追加して、System.IOSystem.Dynamic名前空間をインポートします。

using System.IO;
using System.Dynamic;

カスタム動的オブジェクトは、列挙型を使用して検索条件を決定します。 クラス ステートメントの前に、次の列挙型定義を追加します。

public enum StringSearchOption
{
    StartsWith,
    Contains,
    EndsWith
}

次のコード例に示すように、 DynamicObject クラスを継承するように class ステートメントを更新します。

class ReadOnlyFile : DynamicObject

ReadOnlyFile クラスに次のコードを追加して、ファイル パスのプライベート フィールドと、ReadOnlyFile クラスのコンストラクターを定義します。

// Store the path to the file and the initial line count value.
private string p_filePath;

// Public constructor. Verify that file exists and store the path in
// the private variable.
public ReadOnlyFile(string filePath)
{
    if (!File.Exists(filePath))
    {
        throw new Exception("File path does not exist.");
    }

    p_filePath = filePath;
}
  1. GetPropertyValue クラスに次のReadOnlyFile メソッドを追加します。 GetPropertyValueメソッドは、入力として検索条件を受け取り、その検索条件に一致するテキスト ファイルから行を返します。 ReadOnlyFile クラスによって提供される動的メソッドは、GetPropertyValue メソッドを呼び出してそれぞれの結果を取得します。
public List<string> GetPropertyValue(string propertyName,
                                     StringSearchOption StringSearchOption = StringSearchOption.StartsWith,
                                     bool trimSpaces = true)
{
    StreamReader sr = null;
    List<string> results = new List<string>();
    string line = "";
    string testLine = "";

    try
    {
        sr = new StreamReader(p_filePath);

        while (!sr.EndOfStream)
        {
            line = sr.ReadLine();

            // Perform a case-insensitive search by using the specified search options.
            testLine = line.ToUpper();
            if (trimSpaces) { testLine = testLine.Trim(); }

            switch (StringSearchOption)
            {
                case StringSearchOption.StartsWith:
                    if (testLine.StartsWith(propertyName.ToUpper())) { results.Add(line); }
                    break;
                case StringSearchOption.Contains:
                    if (testLine.Contains(propertyName.ToUpper())) { results.Add(line); }
                    break;
                case StringSearchOption.EndsWith:
                    if (testLine.EndsWith(propertyName.ToUpper())) { results.Add(line); }
                    break;
            }
        }
    }
    catch
    {
        // Trap any exception that occurs in reading the file and return null.
        results = null;
    }
    finally
    {
        if (sr != null) {sr.Close();}
    }

    return results;
}

GetPropertyValue メソッドの後に、次のコードを追加して、TryGetMember クラスのDynamicObject メソッドをオーバーライドします。 TryGetMember メソッドは、動的クラスのメンバーが要求され、引数が指定されていない場合に呼び出されます。 binder引数には参照されるメンバーに関する情報が含まれており、result引数は指定されたメンバーに対して返された結果を参照します。 TryGetMember メソッドは、要求されたメンバーが存在する場合にtrueを返すブール値を返します。それ以外の場合は、falseを返します。

// Implement the TryGetMember method of the DynamicObject class for dynamic member calls.
public override bool TryGetMember(GetMemberBinder binder,
                                  out object result)
{
    result = GetPropertyValue(binder.Name);
    return result == null ? false : true;
}

TryGetMember メソッドの後に、次のコードを追加して、TryInvokeMember クラスのDynamicObject メソッドをオーバーライドします。 TryInvokeMember メソッドは、動的クラスのメンバーが引数を使用して要求されたときに呼び出されます。 binder引数には参照されるメンバーに関する情報が含まれており、result引数は指定されたメンバーに対して返された結果を参照します。 args引数には、メンバーに渡される引数の配列が含まれています。 TryInvokeMember メソッドは、要求されたメンバーが存在する場合にtrueを返すブール値を返します。それ以外の場合は、falseを返します。

TryInvokeMember メソッドのカスタム バージョンでは、最初の引数が前の手順で定義したStringSearchOption列挙型の値であることが想定されています。 TryInvokeMember メソッドは、2 番目の引数がブール値であると想定しています。 一方または両方の引数が有効な値である場合は、結果を取得するために GetPropertyValue メソッドに渡されます。

// Implement the TryInvokeMember method of the DynamicObject class for
// dynamic member calls that have arguments.
public override bool TryInvokeMember(InvokeMemberBinder binder,
                                     object[] args,
                                     out object result)
{
    StringSearchOption StringSearchOption = StringSearchOption.StartsWith;
    bool trimSpaces = true;

    try
    {
        if (args.Length > 0) { StringSearchOption = (StringSearchOption)args[0]; }
    }
    catch
    {
        throw new ArgumentException("StringSearchOption argument must be a StringSearchOption enum value.");
    }

    try
    {
        if (args.Length > 1) { trimSpaces = (bool)args[1]; }
    }
    catch
    {
        throw new ArgumentException("trimSpaces argument must be a Boolean value.");
    }

    result = GetPropertyValue(binder.Name, StringSearchOption, trimSpaces);

    return result == null ? false : true;
}

ファイルを保存して閉じます。

サンプル テキスト ファイルを作成する

ソリューション エクスプローラーで、DynamicSample プロジェクトを右クリックし、[追加>新しい項目] を選択します。 [ インストールされているテンプレート ] ウィンドウで、[ 全般] を選択し、[ テキスト ファイル ] テンプレートを選択します。 [名前] ボックスの既定の名前TextFile1.txt のままにして、[追加] を選択します。 次のテキストを TextFile1.txt ファイルにコピーします。

List of customers and suppliers

Supplier: Lucerne Publishing (https://www.lucernepublishing.com/)
Customer: Preston, Chris
Customer: Hines, Patrick
Customer: Cameron, Maria
Supplier: Graphic Design Institute (https://www.graphicdesigninstitute.com/)
Supplier: Fabrikam, Inc. (https://www.fabrikam.com/)
Customer: Seubert, Roxanne
Supplier: Proseware, Inc. (http://www.proseware.com/)
Customer: Adolphi, Stephan
Customer: Koch, Paul

ファイルを保存して閉じます。

カスタム動的オブジェクトを使用するサンプル アプリケーションを作成する

ソリューション エクスプローラーでProgram.cs ファイルをダブルクリックします。 Main プロシージャに次のコードを追加して、ReadOnlyFile クラスのインスタンスを作成します。 このコードでは、遅延バインディングを使用して動的メンバーを呼び出し、文字列 "Customer" を含むテキスト行を取得します。

dynamic rFile = new ReadOnlyFile(@"..\..\..\TextFile1.txt");
foreach (string line in rFile.Customer)
{
    Console.WriteLine(line);
}
Console.WriteLine("----------------------------");
foreach (string line in rFile.Customer(StringSearchOption.Contains, true))
{
    Console.WriteLine(line);
}

ファイルを保存し、 Ctrl+F5 キーを押して、アプリケーションをビルドして実行します。

動的言語ライブラリを呼び出す

次のチュートリアルでは、IronPython の動的言語で記述されたライブラリにアクセスするプロジェクトを作成します。

カスタム動的クラスを作成するには

Visual Studio で、 [ファイル]>[新規]>[プロジェクト] の順に選択します。 [ 新しいプロジェクトの作成 ] ダイアログで、[C#] を選択し、[ コンソール アプリケーション] を選択し、[ 次へ] を選択します。 [新しいプロジェクトの構成] ダイアログで、DynamicIronPythonSampleに「」と入力し、[次へ] を選択します。 [追加情報] ダイアログで、ターゲット フレームワーク.NET 7.0 (Current) を選択し、[作成] を選択します。 IronPython NuGet パッケージをインストールします。 Program.cs ファイル 編集します。 ファイルの先頭に次のコードを追加して、IronPython ライブラリとMicrosoft.Scripting.Hosting名前空間からIronPython.HostingSystem.Linq名前空間をインポートします。

using System.Linq;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;

Main メソッドで、次のコードを追加して、IronPython ライブラリをホストする新しい Microsoft.Scripting.Hosting.ScriptRuntime オブジェクトを作成します。 ScriptRuntime オブジェクトは、IronPython ライブラリ モジュール random.py を読み込みます。

// Set the current directory to the IronPython libraries.
System.IO.Directory.SetCurrentDirectory(
   Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) +
   @"\IronPython 2.7\Lib");

// Create an instance of the random.py IronPython library.
Console.WriteLine("Loading random.py");
ScriptRuntime py = Python.CreateRuntime();
dynamic random = py.UseFile("random.py");
Console.WriteLine("random.py loaded.");

random.py モジュールを読み込むコードの後に、次のコードを追加して整数の配列を作成します。 配列は、random.py モジュールの shuffle メソッドに渡され、配列内の値がランダムに並べ替えられます。

// Initialize an enumerable set of integers.
int[] items = Enumerable.Range(1, 7).ToArray();

// Randomly shuffle the array of integers by using IronPython.
for (int i = 0; i < 5; i++)
{
    random.shuffle(items);
    foreach (int item in items)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("-------------------");
}

ファイルを保存し、 Ctrl+F5 キーを押して、アプリケーションをビルドして実行します。

こちらも参照ください