systembankスタッフのブログ

システムバンクスタッフのブログです。

インターフェイス その1

 インターフェイスってそもそも

マイクロソフトC#プログラミングガイドに書いてある定義を読むと何故かよくわからない気持ちになります。

クラスまたは構造体で実装できる関連機能のグループの定義が含まれますって書いてあるんですがちょっと何言ってるかわからないのが正直なところ。(機械翻訳じゃなかったら訳した方、上から目線な物言い済みません)

原文を読んでみると、「別々のクラスや構造体にある関連する機能をまとめてグループにしたもの、このグループをインタフェースっていうんですよ」って感じだろうか、これでもよくわかんないか。

同じように機能をまとめるC#のプログラミング機構に抽象クラスってのもありますが、どうやって使い分ければよいのか、話してみようと思います。

インターフェイスの書き方

    interface IReader
    {
        string Execute();
    }

 クラスの代わりにinterfaceと書いて頭にIつけて宣言する。これでインタフェースになります。インターフェイス内のメソッド、プロパティは全てpublicなのでpubilcとかprivateとかは書かないです。インターフェイス自身を外部に公開する場合はpublicをつけて宣言します。

使い方の簡単な実装例

これをどうやって使うかというと

    class DbReader : IReader
    {
        public string Execute()
        {
            var factory = DbProviderFactories.GetFactory("Npgsql");
            using (DbConnection connection = factory.CreateConnection())
            {
                connection.ConnectionString 
                    = Properties.Settings.Default.ConnectionString;
                connection.Open();

                using (DbCommand command = connection.CreateCommand())
                {
                    command.CommandText = "SELECT STRINGVALUE FROM TABLE1";
                    using (DbDataReader dataReader = command.ExecuteReader())
                    {
                        if (!dataReader.HasRows) return string.Empty;
                        
                        string returnValue = string.Empty;
                        while(dataReader.Read())
                        {
                            returnValue 
                                += string.Format("{0}\r\n", dataReader["STRINGVALUE"]);
                        }
                        return returnValue;
                    }
                }
            }
        }
    }

冗長な書き方になってしまいましたが、IReaderのExecuteメソッド実装したDbReaderクラスです。インターフェイスを利用するクラスが1つだけだと前段で話した「別々のクラス云々」ではないのでもう一つクラスを実装します

    class FileReader : IReader
    {
        public string Execute()
        {
            using (StreamReader streamReader 
                = new StreamReader(Properties.Settings.Default.FileName))
            {
                string returnValue = string.Empty;

                while(!streamReader.EndOfStream)
                {
                    returnValue += string.Format("{0}\r\n", streamReader.ReadLine());
                }

                return returnValue;
            }
        }
    }

IReaderインターフェイスを持つDbReaderクラス、FileReaderクラスを作成しました。 2つのクラスはIReaderインターフェイスを持っていること以外は関連性がありません。 これが別々のクラスにある関連する機能をグループにまとめる行為です。 IReaderを使う側が存在しないと何がどうインターフェイスなのかわからないのでその部分も実装します。

    static class ReaderFactory
    {
        public static IReader CreateReader(string readerName)
        {
            switch (readerName)
            {
                case "DbReader": return new DbReader();
                case "FileReader": return new FileReader();
                default: throw new InvalidOperationException("readerNameが無効です。");
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length > 0)
            {
                IReader reader = ReaderFactory.CreateReader(args[0]);
                Console.WriteLine(reader.Execute());
            }
            else
            {
                Console.WriteLine("Readerをコマンドライン引数に書いてください");
            }

        }
    }    

IReaderインターフェイスの何れかを生成するファクトリクラスを作って、IReaderインターフェイスを使うプログラム側ではファクトリクラスを使ってIReaderを使う形にするのが常套です。

こうすることでインターフェイスを使う側のプログラムはどんな具体的なクラスが実体化したかを意識する必要がなくなります、言い方をかえると同じインターフェイスを共有してればどんなクラスであっても使う側のプログラムは影響を受けないことになります、クラス自体が何かは問うことはありません。

次回

今回はここまでにします。

この例だと抽象クラス、具体クラスを用いて同じことを実装できるのですがなぜ抽象クラスではなくインターフェイスなのかを次回の書こうと思います。

 

 

NULLの判定 その3

空のOptional

Optionalを返却するメソッドがあるのでまずそれを移植します。

            public static Optional<T> Empty => new Optional<T>(null);

こんな感じでいいと思います。

フィルタ

フィルタメソッドも移植します。

        public Optional<T> Filter(Predicate<T> predicate)
        {
            if (value == null || !predicate(value)) return Empty;

            return new OptionalT<T>(value);
        }

返却値は自分自身でも良かった気がするのですが、ドキュメントを読むと別のインスタンスに思えたのでこれで。

マップ

値がある場合別の値を返却するマップメソッドも移植します。

        public Optional<TResult> Map<TResult>(Func<T, Optional<TResult>> mapper)
            where TResult : class
        {
            if (value == null) return Optional<TResult>.Empty;
            var result = mapper(value);

            return result;
        }

        public Optional<TResult> Map<TResult>(Func<T, TResult> mapper)
            where TResult : class
        {
            if (value == null) return Optional<TResult>.Empty;

            return Optional.OfNullable(mapper(value));
        }
    

この二つのメソッドの使い分けが正直わからないですがOptionalのメソッドチェーンを構成するために必要な関数ですね。

Javaではメソッド名が違いますがオーバーロードして同じメソッド名にしています。

NULLの場合の別の値

値がない場合の値を返却するメソッドを移植します。

        public T OrElse(T other)
        {
            return value != null ? value : other;
        }

        public T OrElse(Func<T> func)
        {
            return value != null ? value : func();
        }

これもオーバーロードしています。2番目のメソッドの仕様が厳密にいうと違う気がしますが、これでも十分な気がするのでこんなんです。

ここまで

ここまであれば最低限使い物になるかと思います。

ここでのOptionalはあくまでサンプルで、Javaの仕様にあるような例外判定はプログラム内に組み込んでいません。

実際に使用してみようと思う方は必ず例外発生処理を加えて使ってください。

    class Optional<T>
        where T : class
    {
        private T value;

        public Optional(T value)
        {
            this.value = value;
        }

        public bool HasValue => value != null;

        public void IfPresent(Action<T> action)
        {
            if (value != null)
            {
                action(value);
            }
        }

        public void IfNotPresent(Action action)
        {
            if (value == null)
            {
                action();
            }
        }

        public Optional<T> Filter(Predicate<T> predicate)
        {
            if (value == null || !predicate(value)) return Empty;

            return new Optional<T>(value);
        }

        public Optional<TResult> Map<TResult>(Func<T, Optional<TResult>> mapper)
            where TResult : class
        {
            if (value == null) return Optional<TResult>.Empty;
            return mapper(value);
        }

        public Optional<TResult> Map<TResult>(Func<T, TResult> mapper)
            where TResult : class
        {
            if (value == null) return Optionall<TResult>.Empty;

            return Optional.OfNullable(mapper(value));
        }

        public T OrElse(T other)
        {
            return value != null ? value : other;
        }

        public T OrElse(Func<T> func)
        {
            return value != null ? value : func();
        }

        public static Optional<T> Empty => new Optional<T>(null);
    }

    static class Optional
    {
        public static Optional<T> OfNullable<T>(T value)
           where T : class
        {
            return new Optional<T>(value);
        }
    }

NULLの判定 その2

値を持っているか確認するプロパティの追加

JavaのOptionalに実装されているisPresent()に相当する機能を実装します。

C#にはプロパティがあるので値を保持しているか判定するプロパティを追加します。

また、C#にはnull非許容型クラスのコンテナ「Nullable<T>」クラスがあり、このクラスにHasValueプロパティがあるのでこちらに倣って追加します。

      public bool HasValue
        {
            get { return value != null; }
        }

C#7.0ではもう少し簡潔にかけるのでこうなります。

        public bool HasValue => value != null;

値がある場合の処理を記述するメソッドを追加

同じくifPresent(Consumer<? super T> consumer)に相当する機能を実装します。

ConsumerはC#のActionに相当するので引数を変更して追加します。

        public void IfPresent(Action<T> action)
        {
            if (value != null)
            {
                action(value);
            }
        }

これで値がnull以外のときのみactionで指定した処理が実行され、それ以外は何もしないようにできました。

今のところでの使い方はざっくり下記のようになります。

    class Program
    {
        static void Main(string[] args)
        {
            var argsOpt = new Optional<string[]>(args);
            DoSomething(GetSomething(argsOpt));
        }

        private static void DoSomething(Optional<string> opt)
        {
            opt.IfPresent(s => Console.WriteLine("{0}ですよ。", s));

            if (!opt.HasValue)
            {
                Console.WriteLine("nullですよ。");
            }
        }

        private static Optional<string> GetSomething(Optional<string[]> argsOpt)
        {
            string s = null;
            argsOpt.IfPresent(args => s = string.Join(",", args));

            return new Optional<string>(s);
        }
    }

値が無い場合のメソッドも追加

DoSomethingメソッド内でHasValueを使っているということは値を扱う側でNULL判定をしていると同義なのでメソッドを追加します。

Javaのドキュメントを見たけどこのメソッドが無いような。見間違いだったら済みません。

    public void IfNotPresent(Action action)
        {
            if (value == null)
            {
                action();
            }
        }

これを使ってサンプルを書き換えるとDoSomethingメソッドはこうなります。

        private static void DoSomething(Optional<string> opt)
        {
            opt.IfPresent(s => Console.WriteLine("{0}ですよ。", s));
            opt.IfNotPresent(() => Console.WriteLine("nullですよ。"));
        }

生成メソッドの追加

コンストラクタをそのまま使っているとGeneric引数を記述するのがメンドイのでクラスとメソッドを追加します。

    static class Optional
    {
        public static Optional OfNullable(T value)
           where T : class
        {
            return new Optional(value);
        }
    }

これを使ってサンプルプログラムを書き換えます。

    class Program
    {
        static void Main(string[] args)
        {
            var argsOpt = Optional.OfNullable(args);
            DoSomething(GetSomething(argsOpt));
        }

        private static void DoSomething(Optional opt)
        {
            opt.IfPresent(s => Console.WriteLine("{0}ですよ。", s));
            opt.IfNotPresent(() => Console.WriteLine("nullですよ。"));
        }

        private static Optional<string> GetSomething(Optional<string[]> argsOpt)
        {
            string s = null;
            argsOpt.IfPresent(args => s = string.Join(",", args));

            return Optional.OfNullable(s);
        }
    }
    class Optiona<T>
        where T : class
    {
        private T value;

        public Optional(T value)
        {
            this.value = value;
        }

        public bool HasValue => value != null;

        public void IfPresent(Action<T> action)
        {
            if (value != null)
            {
                action(value);
            }
        }

        public void IfNotPresent(Action action)
        {
            if (value == null)
            {
                action();
            }
        }
    }

    static class Optional
    {
        public static Optional<T> OfNullable<T>(T value)
           where T : class
        {
            return new Optional<T>(value);
        }
    }
    

これで利用側プログラムからNULLを判定する部分が消えすっきりしたプログラムに変わったと思います。

次回

JavaのOptionalにはまだいろいろメソッドがあるのでそれを実装してみます。

NULLの判定 その1

 はじめに

サンプルプログラムではなく実際に使うプログラムを記述していくとnullのチェックがどうしても必要になりますよね。

初心者のうちは何をnull判定すべきで何をnull判定すべきではないか、どのメソッドにそれを実装すべきなのかなど、いろいろ悩むところだらけになりますよね。少なくとも私はそうでした。

ここではその話ではなくJavaにあるコンテナクラスでC#にもあったらいいなと思ったOptionalを実際に実装しようと思います。

必要な知識は、C#の初歩の部分と、このブログで前に話したラムダ式くらいがあれば自分で実装できると思います。

その前に当たり前のnull判定

Optionalの前に普通のnull判定を書いてみます。

    public void DoSomething(string s) 
    { 
        if (s == null) 
        { 
            Console.WriteLine("nullですよ。"); 
        } 
        else 
        { 
            Console.WriteLine(string.Format("{0}ですよ。", s)); 
        }
    }

 なんだかんだあるかもしれませんが、こうなることがほどんどだと思います。

JavaのOptional

JavaSEのドキュメントを見ると値を1つ保持ことができるコンテナオブジェクトで、値がnullじゃないときに動作させる関数、値がnullじゃないときに動作させる関数が用意されているようです。

目的はnullを許容する値をコンテナに入れてコンテナを介してアクセスすることで保守性や安全性を高めることに見えます。

これと同じような動きをC#で実現してみようと思います。

最新のC# 8.0はnullに関する機能強化がたくさんあるので必要ないかもですが、社内ではC#7.0系もしくは5.0系なのですよ。

作り出し

まず値を一つ持つコンテナクラスを実装します、クラス名はもちろんOptionalで。

    
class Optional<T>
        where T : class
    {
        private T value;

        public Optional(T value)
        {
            this.value = value;
        }
    }

 はい、できました。

Optional<T>としたのでジェネリックです。where以下はジェネリック型制約で T この場合 Tはclassであることとなります。nullを許容する値のコンテナなのでTはclassとしています。

補足ですが制約にclassではなくstructとするとnull非許容型に対するコンテナになります。今回の目的では意味がなくなりますので制約はあくまでclassです。

このコンテナにはコンストラクタが1つかつその引数がジェネリック引数T型となっています。

コンテナオブジェクトなので引数が無いコンストラクタでNEWと「何をコンテナするの?」ってなって意味がなくなりますので。

そしてコンテナをNewするとそのときの引数はプライベート変数に格納されて、外部からはアクセスできないようにしておきます。

次回

これで値にアクセスできないコンテナオブジェクトができたので、次回は値を提供するメソッドを作ってみます。

 

MVNE? MVNO ? 格安SIMを買った話

 前置き

本日は出社日ではないので別の話題を。

自分のクルマのナビがT-Connectに対応しているので車載用のWiFiを探しておりました。もちろんお金はかけたくないのでゲオに行ったわけです。

とりあえずSIMカードを刺せてWiFiが飛べばなんでもいいと思っていたらdocomoの2012年製のモバイルルーターが1,280円!! 買いました。

おじさんになると7年前なんて昨日みたいなもんだしね。でも付属していたバッテリーはそんなことはなくヘロヘロになっていましたね、バッテリーをメルカリとかヤフオクとかで探してみると本体より高い、これは買いたくない。

クルマの中で使うから充電しながらだからとりあえずは問題ないと。

で、docomoモバイルルーターを買ったわけですがせっかく格安品を見つけたのにdocomoでSIMを追加したくないなと。

なので格安SIMを導入することにしたわけですよ。

SIM提供会社選び

いままで興味がなかったわけではないんですが積極的に調べてもいなかったのですがたくさんあるんですね。

実際に使ってみようかと調べただけのは

BIGLOBEモバイル 

家のISPBIGLOBEなのでとりあえず調べてみました。動画とか音楽とか無制限のプランがあるみたいでスマホに入れるにはいいかと思いましたが、今回はモバイルルーターWiFi飛ばす以外なんにもできません。ということで次へ

楽天モバイル

 嫁が楽天好きなんで使えばポイントたまるんじゃないかなと思って調べてみました。でも楽天って第四の携帯電話会社ってなるんですよね、なんか少し延期したみたいですけれど。その時格安SIMの扱いがどうなるかわからなかったので(調べるのがめんどくさくなってきたので)とりあえずこれは止め。

AEONモバイル

近くに発寒イオンあるしそこに行けばすぐSIMくれるんじゃないかいって感じで調べてみると最低利用期間の設定がないみたい、縛られないのはとても気持ちが良いのでがぜん気持ちはAEONに。

あとau系列、softbank系列はそもそも買ったモバイルルーターが使えないからダメ。

動かせました

発寒イオンにいって手続きするとSIMを渡す処理に2時間くらいかかるので後で連絡しますってことだったので家に帰るとすぐ電話が来て「SIMカードお渡しする準備ができました」って早すぎだし、30分経ってないよ、それなら店で待っていたよ。

どうやらAEONモバイルのホームページを見ながらAPNとやらの設定をルーターを突っ込んでみたけれど、設定済み端末をセットで購入する分にはいいけど、これパソコンとかの知識結構必要ですね。

それでインターネットで調べているときに、MVNEとかMVNOとかの用語が出てきてなんなんだろと思って調べてみるとMVNEがMVNOの事業者を支援する事業者でMVNE事業者もMVNO事業者として通信サービスを提供しているみたい。

MVNE事業者がやっているMVNOだと通信に有利なのか?よくわからない。

 

ラムダ式への道 その4 到着

 アクションとファンクション

.Net Frameworkのライブラリにあらかじめ用意されているdelegateにアクション(Action)とファンクション(Func)があります。

それぞれこんな感じで使用します。

    class SomeClass
    {
        private Func<int, string> _someFunction;
        public SomeClass(Func<int, string> function)
        {
            _someFunction = function;
        }

        public string DoSomething(int value)
        {
            return _someFunction(value);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var some = new SomeClass((v) => (v * v).ToString());
        
            var value = 10;
            Console.WriteLine(some.DoSomething(value));
        }
    }    

前回のサンプルコードのdelegate宣言をFuncに置き換えました。Func<int, string>でintが引数型、stringが返却型の関数を表しています。

引数が無い場合は

        Func now = () => DateTime.Now.ToString();
        var s = now();

引数部分を()と書いた後にラムダ演算子になります。

先のプログラムをActionを使用する形に変更してみます

    class SomeClass
    {
        private Action _someAction;
        public SomeClass(Action action)
        {
            _someAction = action;
        }

        public void DoSomething(int value)
        {
            _someAction(value);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var some = new SomeClass((v) => Console.WriteLine((v * v).ToString()));
        
            var value = 10;
            some.DoSomething(value);
        }
    }

アクション、ファンクションは引数が4つまで用意されています。 また引数にC# 7.0で導入されたタプルを使用できるので、それを引数に利用するとdelegateを 新たに作成する必要はほどんどないかと思います。

ラムダ式への道 その3

 ようやくラムダ式

C# 3.0から匿名関数の記法をより簡便にした記法が導入されました。それがラムダ式です。

式というくらいなので式を記述するように書けます。むろん今までの記法もそのまま使用することもできますが、もう全然楽なので今までの書き方は覚える必要が無いです。

サンプルコード

 前回のソースコードラムダ式を用いる形に変更します。

    delegate string SomeFunction(int value);
    class SomeClass
    {
        private SomeFunction _someFunction;
        public SomeClass(SomeFunction function)
        {
            _someFunction = function;
        }

        public string DoSomething(int value)
        {
            return _someFunction(value);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var some = new SomeClass((v) => { return (v * v).ToString(); });
        
            var value = 10;
            Console.WriteLine(some.DoSomething(value));
        }
    }

 SomeClass内のコンストラクタ部分のdelegate(int i)の部分がシンプルな記法に代わりました。さらに => の中の関数本体分が1行で済む場合はさらに省略できます。

            var some = new SomeClass((v) => (v * v).ToString());

{ }も省略でき、returnも省略されたのでとてもすっきりした記述になります。ただその向こうでは前回、前々回に説明したような動作になっていることを理解しておくと飲み込みやすいかと思います。

次回

ラムダ式を使いやすくするためにC# 3.0以降で拡張されたそのほかの使い方を説明しようと思います。