いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

C#3.0の追加機能-02(拡張メソッド)


C# 3.0では、既存のクラスにメソッドを追加して拡張することが
できます。その際に、そのクラスのサブクラスを作成するという事は
必要なく、別のクラスを作成し、その中にstaticなメソッドを作成します。
その際の決まり事は以下の点です。

  1. 拡張メソッドを持つクラスは、staticクラスとする事。
  2. 拡張メソッドは必ずstaticメソッドにする事。
  3. 最低必ず一つの引数を受け取るようにすること。(レシーバー自身が入ります。)
  4. 宣言する拡張メソッドの第一引数は必ず以下の書式とすること。
    public static 戻り値型 拡張メソッド名(this 拡張するクラスの型 o)


pythonやっている人は、メソッドを宣言する際に定義するselfと同じようなものと
思うと分かりやすいです。


上記のようにすることで、そのクラスにメソッドが拡張されます。
拡張メソッドを利用するには、作成した拡張クラスをusingでインポートすればオッケイです。


以下簡単なサンプルです。

// vim:set ts=4 sw=4 et ws is nowrap ft=cs:
using System;

namespace Gsf.Samples.Tmp{
    
    internal static class MyExtensions{

        public static void PrintMyType(this object o){
            Console.WriteLine(o.GetType().Name);
        }
    }

    public class MethodExtensionSample : IExecutor{

        public void Execute(){

            100.PrintMyType();
            "hoge".PrintMyType();
            new object().PrintMyType();

        }
    }
}


まあ、実際あまり使う事は無いとは思いますが、個人的に便利だと思い
自分のライブラリには拡張メソッドとして用意しているのが以下のものです。
Rubyのto_iみたいなメソッドや10.times{}みたいなものがあったら便利だなと思いまして。

// vim:set ts=4 sw=4 et ws is nowrap ft=cs:
using System;
using System.Collections.Generic;

namespace Gsf.Lib.Extensions{

    using System.Linq;

    public static class ObjectExtensions{

        public static int ToInteger(this object o){
            int result = 0;

            if(o == null){
                return result;
            }

            try{
                result = int.Parse(o.ToString());
            }catch{
                //
                // パースに失敗した場合、カウントを表すプロパティを持っているオブジェクトか
                // どうかを調査し、持っている場合はその件数を結果として返す。
                // 
                var p = from prop in o.GetType().GetProperties()
                        where Array.Exists(new[]{"Count", "Length", "Size"}, targetPropName => { return prop.Name == targetPropName; })
                        select prop;

                if(p.Count() != 0){
                    result = p.First().GetValue(o, null).ToInteger();
                }
            }

            return result;
        }
    }

    public static class IntegerExtensions{

        public static IEnumerable<int> Times(this int i){
            for(int x = 0; x < i; x++){
                yield return x;
            }
        }
    }

    public static class StringExtensions{

        public static bool IsNullOrEmpty(this string s){
            return string.IsNullOrEmpty(s);
        }

        public static bool IsNotNullOrEmpty(this string s){
            return !string.IsNullOrEmpty(s);
        }
    }
}

namespace Gsf.Samples.Tmp{

    using Gsf.Lib.Extensions;

    public class MethodExtensionSample2 : IExecutor{

        public void Execute(){
            //
            // object#ToInteger()の確認.
            //
            Console.WriteLine("hoge".ToInteger());
            Console.WriteLine(100.ToInteger());
            Console.WriteLine(10.0d.ToInteger());
            Console.WriteLine(new List<string>(new[]{"hoge", "hehe", "fuga"}).ToInteger());
            Console.WriteLine(new{ Name="gsf_zero1", Age=28 }.ToInteger());
            object o = null;
            Console.WriteLine(o.ToInteger());

            //
            // string#IsNotNullOrEmpty()の確認.
            //
            Console.WriteLine("hoge".IsNotNullOrEmpty());
            string s = null;
            Console.WriteLine(s.IsNotNullOrEmpty());

            //
            // string#IsNullOrEmpty()の確認.
            //
            Console.WriteLine("hoge".IsNullOrEmpty());
            Console.WriteLine(s.IsNullOrEmpty());

            //
            // int#Times()の確認.
            //
            foreach(int i in 100.Times()){
                Console.WriteLine(i);
            }

            foreach(int i in "hoge".ToInteger().Times()){
                Console.WriteLine(i);
            }
        }
    }
}

================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。