32
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

株式会社ラグザイアAdvent Calendar 2023

Day 25

C#でメソッドチェーンをうまく作る方法 前編

Last updated at Posted at 2023-12-24

はじめに

この記事は株式会社ラグザイア Advent Calendar 2023 の最終日の記事です。

C#でメソッドチェーンを作る方法をまとめていきたいと思います。
前後編になる予定です。

この方法を使うと、メソッドチェーンを書いている時にIntelliSenseに適切な候補が表示されるようになります。

前編ではメソッドチェーンを作る時のチュートリアルを書いてます。わかりやすいようにところどころで図説します。

手法の説明

メソッドチェーンとは

メソッドチェーンとは以下の図のようにあるインスタンスのメソッドを呼び出した後、その戻り値が同じインスタンスを返すため、同じメソッドを呼び出せるだけでなく他に定義されているメソッドも呼び出せる仕組みのことを言います。まるで鎖(チェーン)のようにメソッドが連なるから「メソッドチェーン」と呼ばれるのでしょう。

image.png

ここで呼び出せるメソッドは同じインスタンスを戻り値として返すことで実現できます。図で説明すると、AメソッドとBメソッドとCメソッドは同じインスタンスを返します。


class X
{
    X A()
    {
        return this; //または return new X(...);
    }
    
    X B()
    {
        return this; //または return new X(...);
    }
    
    X C()
    {
        return this; //または return new X(...);
    }
}

呼び出す時はこんな感じになります。


    new X().A().A().A();
    new X().A().A().B();
    new X().A().A().C();
    
    new X().A().B().A();
    new X().A().B().B();
    new X().A().B().C();
    
    new X().A().C().A();
    new X().A().C().B();
    new X().A().C().C();

分岐先の候補を別々にする

image.png

上の図のようにAメソッドをコールした後にBメソッドをコールすると、次の候補がCメソッドとDメソッドになるコードは以下の通りです。

class X
{
    X A()
    {
        return this; //または return new X(...);
    }
    
    Y B()
    {
        return new Y(...);
    }
}

class Y
{
    ? C()
    {
        return new ?(...);
    }
    
    ? D()
    {
        return new ?(...);
    }
}

呼び出す時はこんな感じになります。


    new X().A().A().A();
    
    new X().A().A().B().C();
    new X().A().A().B().D();
    
    new X().A().B().C();
    new X().A().B().D();

メソッドチェーンを終える時は必ず特定のメソッドを呼び出させる

image.png

メソッドチェーンを終える時に必ず特定のメソッドを呼び出したいことが多々あります。

例で説明

Buildメソッド

例えば Buildメソッド。様々なライブラリで見かけるので例は一つだけ上げます。単純なメソッドチェーンを構成した後にBuildメソッドを実行することで、メソッドチェーンで設定した設定各種を適用した何かを返すような使い方ですね。

namespace K2IB;

:
public static class MauiProgram
{
    private static readonly Logger Logger = LogManager.GetCurrentClassLogger();

    public static MauiApp CreateMauiApp()
    {
        try
        {
            var builder = MauiApp.CreateBuilder();
            return builder
                .UseSkiaSharp(true)
                .UseMauiApp<App>()
                .UseMauiCommunityToolkit()
                .UsePrism(prism => prism
#if WINDOWS10_0_17763_0_OR_GREATER
                .ConfigureModuleCatalog(moduleCatalog =>
                {
                    :
                })
#endif
                .OnAppStart(async navigationService =>
                {
                    :
                })
                .RegisterTypes(containerRegistry =>
                {
                    :
                }))
                .UseBarcodeReader()
                .ConfigureMauiHandlers(h =>
                {
                    :
                })
                .ConfigureFonts(fonts =>
                {
                    :
                }).Build();
        }
        catch (Exception ex)
        {
            Crashes.TrackError(ex);
            throw;
        }
    }

    :
}

ToSqlメソッド(拙作ライブラリで実装)

私の拙作ライブラリHomuraではSQLクエリビルダを実装しており、同様にメソッドチェーンの仕組みがあります。メソッドチェーンを書いた後、ToSqlメソッドでstring型のSQLクエリ文字列を吐き出すようになっていて、それをADO.NETのDbCommandクラスのCommandTextプロパティにセットするような動作イメージです。

詳説

上記のようなメソッドチェーンを終える時に必ず特定のメソッドを呼び出したいことがあることがわかってもらえたと思うので、戻って図の内容を実装してみます。

再掲

image.png

悪い例

以下は悪い例です。


class X
{
    Y A()
    {
        return new Y();
    }

    string Finish()
    {
        return "FINISHED!!?";
    }
}

class Y
{
    X B()
    {
        return new X();
    }

    X C()
    {
        return new X();
    }

    X D()
    {
        return new X();
    }
}

上のように書くと new X().Finish(); と書けてしまうので良くありません。

以下の図のようになります。Finishメソッドと終了記号が2箇所に増えてしまいます。

image.png

良い例


class X
{
    Y A()
    {
        return new Y();
    }
}

class Y
{
    Z B()
    {
        return new Z();
    }

    Z C()
    {
        return new Z();
    }

    Z D()
    {
        return new Z();
    }
}

class Z : X
{
    string Finish()
    {
        return "FINISHED!!";
    }
}

Xクラスを継承したZクラスを用意して、そこにFinishメソッドを実装してあげます。次にYクラスの各メソッドでZクラスのインタンスをnewしたものを返すようにします。

次のように使用できます。

new X().A().B().Finish();
new X().A().C().Finish();
new X().A().D().Finish();

new X().A().B().A().B().Finish();
new X().A().B().A().C().Finish();
new X().A().B().A().D().Finish();

new X().A().C().A().B().Finish();
new X().A().C().A().C().Finish();
new X().A().C().A().D().Finish();

new X().A().D().A().B().Finish();
new X().A().D().A().C().Finish();
new X().A().D().A().D().Finish();

new X().A().B().A().B().A().B().Finish();
:

おわりに

C#でメソッドチェーンをうまく作る方法の前編を書きました。

これで基礎的なメソッドチェーンの作り方は学べたと思います。

次のステップとしては、後編を読んで、応用を学んでみるのがおすすめです。

32
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?