次の方法で共有


アクティビティ デリゲートの使用

アクティビティ デリゲートを使用すると、アクティビティの作成者は、特定の署名を持つコールバックを公開できます。アクティビティのユーザーは、この署名用のアクティビティベースのハンドラーを提供できます。 2 種類のアクティビティ デリゲートを使用できます。ActivityAction<T> は、戻り値を持たないアクティビティ デリゲートを定義する場合に使用され、ActivityFunc<TResult> は戻り値を持つアクティビティ デリゲートを定義する場合に使用されます。

アクティビティ デリゲートは、子アクティビティが特定の署名を持つように制限する必要がある場合に便利です。 たとえば、While アクティビティは制約のないあらゆる型のアクティビティを含めることができますが、ForEach<T> アクティビティの本体は ActivityAction<T> であるため、最終的には ForEach<T> によって実行されるその子アクティビティは、InArgument<T> が列挙するコレクションのメンバーと同じ型である ForEach<T> を持つ必要があります。

ActivityAction の使用

いくつかの .NET Framework 4.6.1 アクティビティ (Catch アクティビティや ForEach<T> アクティビティなど) では、アクティビティ アクションが使用されます。 それぞれの場合において、アクティビティ アクションは、これらのアクティビティのいずれかを使用してワークフローを作成するときに、ワークフローの作成者が目的の動作を提供するアクティビティを指定する場所を表します。 次の例では、ForEach<T> アクティビティを使用してコンソール ウィンドウにテキストを表示します。 ForEach<T> の本体は、ActivityAction<T> の型 (文字列) に一致する ForEach<T> を使用して指定します。 Handler で指定された WriteLine アクティビティには、ForEach<T> アクティビティによって反復処理されるコレクションの文字列値にバインドされる Text 引数があります。

DelegateInArgument<string> actionArgument = new DelegateInArgument<string>();

Activity wf = new ForEach<string>
{
    Body = new ActivityAction<string>
    {
        Argument = actionArgument,
        Handler = new WriteLine
        {
            Text = new InArgument<string>(actionArgument)
        }
    }
};

List<string> items = new List<string>();
items.Add("Hello");
items.Add("World.");

Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Values", items);

WorkflowInvoker.Invoke(wf, inputs);

actionArgument は、コレクション内の各項目を WriteLine にフローするために使用されます。 このワークフローが呼び出されると、次の出力がコンソールに表示されます。

HelloWorld.

このトピックの例では、オブジェクトの初期化の構文を使用します。 オブジェクトの初期化の構文は、コードでワークフロー定義を作成する場合に便利な方法です。これは、ワークフローのアクティビティの階層ビューが提供され、アクティビティ間の関係が示されるためです。 プログラムからワークフローを作成する場合に、オブジェクトの初期化の構文を使用しなければならないという要件はありません。 次の例は、機能的には前のサンプルと同じです。

DelegateInArgument<string> actionArgument = new DelegateInArgument<string>();

WriteLine output = new WriteLine();
output.Text = new InArgument<string>(actionArgument);

ActivityAction<string> body = new ActivityAction<string>();
body.Argument = actionArgument;
body.Handler = output;

ForEach<string> wf = new ForEach<string>();
wf.Body = body;

List<string> items = new List<string>();
items.Add("Hello");
items.Add("World.");

Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Values", items);

WorkflowInvoker.Invoke(wf, inputs);

オブジェクト初期化子の詳細については、コンストラクターを呼び出さずにオブジェクトを初期化する方法 (C# プログラミング ガイド) に関するページおよび「方法: オブジェクト初期化子を使用してオブジェクトを宣言する (Visual Basic)」を参照してください。

次の例では、ワークフローで TryCatch アクティビティを使用します。 ワークフローによって ApplicationException がスローされ、Catch<TException> アクティビティによってこの例外が処理されます。 Catch<TException> アクティビティのアクティビティ アクションのハンドラーは WriteLine アクティビティであり、例外の詳細は ex DelegateInArgument<T> を使用してそれにフローされます。

DelegateInArgument<ApplicationException> ex = new DelegateInArgument<ApplicationException>()
{
    Name = "ex"
};

Activity wf = new TryCatch
{
    Try = new Throw()
    {
        Exception = new InArgument<Exception>((env) => new ApplicationException("An ApplicationException was thrown."))
    },
    Catches =
    {
        new Catch<ApplicationException>
        {
            Action = new ActivityAction<ApplicationException>
            {
                Argument = ex,
                Handler = new WriteLine()
                {
                    Text = new InArgument<string>((env) => ex.Get(env).Message)
                }
            }
        }
    },
    Finally = new WriteLine()
    {
        Text = "Executing in Finally."
    }
};

ActivityAction<T> を定義するカスタム アクティビティを作成する場合は、InvokeAction<T> を使用して ActivityAction<T> の呼び出しをモデル化します。 次の例では、カスタムの WriteLineWithNotification アクティビティを定義します。 このアクティビティは、Sequence アクティビティが含まれ、その後に 1 つの文字列引数を取る WriteLine を呼び出す InvokeAction<T> が続く ActivityAction<T> で構成されます。

public class WriteLineWithNotification : Activity
{
    public InArgument<string> Text { get; set; }
    public ActivityAction<string> TextProcessedAction { get; set; }

    public WriteLineWithNotification()
    {
        this.Implementation = () => new Sequence
        {
            Activities =
            {
                new WriteLine
                {
                    Text = new InArgument<string>((env) => Text.Get(env))
                },
                new InvokeAction<string>
                {
                    Action = TextProcessedAction,
                    Argument = new InArgument<string>((env) => Text.Get(env))
                }
            }
        };
    }
}

WriteLineWithNotification アクティビティを使用してワークフローを作成する場合、ワークフローの作成者は、アクティビティ アクションの Handler で必要なカスタム ロジックを指定します。 次の例では、WriteLineWithNotification アクティビティを使用してワークフローを作成し、WriteLine として Handler アクティビティを使用します。

// Create and invoke the workflow without specifying any activity action
// for TextProcessed.
Activity wf = new WriteLineWithNotification
{
    Text = "Hello World."
};

WorkflowInvoker.Invoke(wf);

// Output:
// Hello World.

// Create and Invoke the workflow with specifying an activity action
// for TextProcessed.
DelegateInArgument<string> msg = new DelegateInArgument<string>();
Activity wf2 = new WriteLineWithNotification
{
    Text = "Hello World with activity action.",
    TextProcessedAction = new ActivityAction<string>
    {
        Argument = msg,
        Handler = new WriteLine
        {
            Text = new InArgument<string>((env) => "Handler of: " + msg.Get(env))
        }
    }
};

// Invoke the workflow with an activity action specified
WorkflowInvoker.Invoke(wf2);

// Output:
// Hello World with activity action.
// Handler of: Hello World with activity action.

1 つ以上の引数を渡すために InvokeAction<T> および ActivityAction<T> のジェネリック バージョンが複数用意されています。

ActivityFunc の使用

ActivityAction<T> はアクティビティから結果値が返されない場合に役立ち、ActivityFunc<TResult> は結果値が返される場合に使用されます。 ActivityFunc<TResult> を定義するカスタム アクティビティを作成する場合は、InvokeFunc<TResult> を使用して ActivityFunc<TResult> の呼び出しをモデル化します。 次の例では、 WriteFillerText アクティビティを定義します。 充てんテキストを提供するために、整数の引数を取って文字列の結果を持つ InvokeFunc<TResult> を指定します。 充てんテキストが取得されると、WriteLine アクティビティを使用してコンソールに表示されます。

public class WriteFillerText : Activity
{
    public ActivityFunc<int, string> GetText { get; set; }
    public InArgument<int> Quantity { get; set; }

    Variable<string> text = new Variable<string>
    {
        Name = "Text"
    };

    public WriteFillerText()
    {
        this.Implementation = () => new Sequence
        {
            Variables =
            {
                text
            },
            Activities =
            {
                new InvokeFunc<int, string>
                {
                    Func = GetText,
                    Argument = new InArgument<int>((env) => Quantity.Get(env)),
                    Result = new OutArgument<string>(text)
                },
                new WriteLine
                {
                    Text = new InArgument<string>(text)
                }
            }
        };
    }
}

テキストを提供するには、int の引数を 1 つ取って文字列の結果を持つアクティビティを使用する必要があります。 次の例では、これらの要件を満たす TextGenerator アクティビティを示します。

public class TextGenerator : CodeActivity<string>
{
    public InArgument<int> Quantity { get; set; }
    public InArgument<string> Text { get; set; }

    protected override string Execute(CodeActivityContext context)
    {
        // Provide a quantity of Random Text
        int q = Quantity.Get(context);
        if (q < 1)
        {
            q = 1;
        }

        string text = Text.Get(context) + " ";
        StringBuilder sb = new StringBuilder(text.Length * q);
        for (int i = 0; i < q; i++)
        {
            sb.Append(text);
        }

        return sb.ToString();
    }
}

TextGenerator アクティビティと WriteFillerText アクティビティを一緒に使用するには、Handler として指定します。

DelegateInArgument<int> actionArgument = new DelegateInArgument<int>();

Activity wf = new WriteFillerText
{
    Quantity = 5,
    GetText = new ActivityFunc<int, string>
    {
        Argument = actionArgument,
        Handler = new TextGenerator
        {
            Quantity = new InArgument<int>(actionArgument),
            Text = "Hello World."
        }
    }
};

WorkflowInvoker.Invoke(wf);