2015年10月05日

C#エンジニア養成読本 (Software Design plus)



C#の基本中の基本からの入門編から最新のC#6.0までを網羅した解説書。

これからC#を始める人も、一通り文法知っている人、最新の情報を集めたい人にもお勧めです。

共著ですが、どなたも有名な方々で、C#関係で調べものをしたり書籍を探したりすると必ず目にする人たちばかりです。
posted by RR at 23:34 | Comment(0) | 書籍 | このブログの読者になる | 更新情報をチェックする

2015年10月15日

メソッドから複数の値を返すいくつかの方法

メソッドから異なる型や複数の値を返却したい場合がある。
たとえば、処理結果と処理成否とを返したい場合など。

下のメソッドはint型の値を二つとり、加算結果を戻値として返却する。
       public int Add(int x, int y)
       {
           return x + y;
       }
これは数値が大きい場合とオーバーフローして不正な値が返却される不具合がある。
checkedを付与すると演算処理時にOverflowExceptionの有無でオーバーフロー発生有無を判断できるが、 この加算処理結果と処理成否との2種類の値をどう返却するかが問題となる。
        public int Add(int x ,int y)
        {
            try
            {
                return checked(x + y);
            }
            catch (OverflowException)
            {
                // どうするか?
            }
        }

処理成否を例外で返却
メソッド内でOverflowExceptionを処理せずに、そのまま例外を呼び元側に伝搬させる方法。
呼び元側で都度Try-Catchを記述するのがしんどいという問題は残る。
       public int Add(int x ,int y)
        {
            return checked(x + y);
        }

結果も成否も戻値で
演算結果がint型で処理成否がbool型だとしてもobjectなら返却できるので文法上は可能。
呼び元で返却値の型チェックの処理が必要になるのでかなり煩雑。大概は代案があるはず。
       public object Add(int x ,int y)
        {
            try
            {
                return checked(x + y);
            }
            catch (OverflowException)
            {
                return false;
            }
        }

特殊な値とする
例えば、必ず処理結果がプラスの値と条件付けられるような場合にマイナス値を返すとか。
若しくは失敗時にnull値とするなど。特殊値が取れるならかなりマシな解決法。
        public int Add(int x ,int y)
        {
            try
            {
                return checked(x + y);
            }
            catch (OverflowException)
            {
                return -1;
            }
        }

        public Nullable<int> Add(int x ,int y)
        {
            try
            {
                return checked(x + y);
            }
            catch (OverflowException)
            {
                return null;
            }
        }

算術結果と成否とを返却する
返却用に処理結果と処理成否とをメンバとするクラスや構造体を定義し、それを返却する。
        public RetValue Add(int x ,int y)
        {
            try
            {
                return new RetValue { Value = checked(x + y), HasFail = false };
            }
            catch (OverflowException)
            {
                return new RetValue { Value = 0, HasFail = true }; ;
            }
        }

    struct RetValue
    {
        public int Value { get; set; }
        public bool HasFail { get; set; }
    }

こうしたクラス定義(構造体定義)が増えるのはウザったいが、4.0以降ならDynamicが使える。
匿名クラスにすれば余計な定義が不要。
        public dynamic Add(int x ,int y)
        {
            try
            {
                return new { Value = checked(x + y), HasFail = false };
            }
            catch (OverflowException)
            {
                return new { Value = 0, HasFail = true }; ;
            }
        }

成否を戻値で返し、演算結果はOUTパラメータで渡す
戻値は1つだが、パラメータはいくつでも定義が可能である。OUTパラメータ(若しくはrefパラメータ)を使うことにより、こちらで値をいくつでも返却できる。今回の例の場合なら、多分これが最適解。
        public bool TryAdd(out int sum, int x ,int y)
        {
            try
            {
                sum = checked(x + y);

                return true;
            }
            catch (OverflowException)
            {
                sum = 0;

                return false;
            }
        }

フィールドとかグローバル変数とかコンテキストとかに結果を置いとく
大概はリファクタ対象だが、たまにこういう実装が正しい場合があったりする。

        public int Sum { get; set; }
        public bool HasError { get; set; }

        public void Add(int x, int y)
        {
            try
            {
                Sum = checked(x + y);
                HasError = false;
            }
            catch (OverflowException)
            {
                HasError = true;
            }
        }
posted by RR at 00:01 | Comment(0) | Tips | このブログの読者になる | 更新情報をチェックする

2015年10月22日

C# メソッドの引数

C#でメソッドを定義する場合は、引数としてどんな型のパラメータをいくつ取るのかを決める必要がある。

引数なし
引数を取らない場合は空のカッコを指定する。
public void Method1()
{
    // DoSometing
}

値渡し
C#でメソッドに引数を指定する場合、基本は値渡しである。
つまり、メソッドの呼び元の値のコピーが引き渡される。
値型の場合はその値そのものコピーが、参照型の場合は参照のコピー(”ポインタの値のコピー”)が渡る。
なので、メソッド内で値を書き換えても、呼び元の値は変わらない。
public void Method1(int val)
{
    val = 10;
}
public void Method2(string val)
{
    val = "method";
}

public void Tester()
{
    // 値型のサンプル
    int value = 0;

    Method1(value);

    int assert1 = value; // 値は0

    // 参照側のサンプル
    string str = "tester";

    Method2(str);

    string assert2 = str; // 値は"tester"
}

配列の例
C#の配列はSystem.Arrayクラスを暗黙に継承した参照型。
値自体、つまりコピーされた参照が変更されても呼び元に影響ないが、
参照が示す先のオブジェクトが変更された場合は、呼び元でも同じアドレスを指しているので、値が変更される。
public void Method1(int[] array)
{
    // 配列への参照(ポインタを書き換え)
    array = new int[]{5,6,7};
}

public void Method2(int[] array)
{
    // 配列の参照が指すオブジェクト自体を変更
    array[0] = 10;
}

public void Tester
{
    int[] array = { 1, 2, 3 };

    Method1(array);

    int[] assert1 = array; // 中身は同じで 1,2,3

    Method2(array);

    int[] assert2 = array: // 0番目が書き変って 10,2,3
}

参照渡し ref / out
基本は値渡しだが、参照(ポインタ)を渡すことも可能。
メソッド定義時にref/outを指定する。
呼び元でも明示的にref/outの指定が必要。
public void Method1(ref int val)
{
    val = 10;
}
public void Method2(out int val)
{
    val = 10; // outパラメタは必ずメソッド内で値を割り当てる必要がある。
}

public void Tester
{
     // ---refのサンプル--------
     // 必ず値を割り当てる必要あり
     int value1 = 0;

     // 呼び元でもref指定
     Method1(ref value1);

     // 値は10
     int assert1 = value1;

     // ---outのサンプル--------
     // 初期化は不要
     int value2; 

     // 呼び元でもout指定
     // メソッドが抜けた時点で値が割り当てられていることが文法上保証される
     Method2(out value2);

     // 値は10
     int assert2 = value2;
}

可変長
引数の型が同じ場合にparamsという予約語を付与することにより可変長の指定が可能。
public void Method(params int[] array)
{
    int sum = array.Sum();
}

public void Tester
{
    // 引数は何個でも
    Method();
    Method(1);
    Method(1,2,3);
}

名前付き
複数の引数をとる場合、順番は記述順に寄るが、明示的に名前を付与することにより順番変えられる。
public void Method(string path,string file)
{
    var full = Path.Combine(path,file);
}

public void Tester
{
    // 引数の順番を指定できる(以下3つは同じ)
    Method("c:\\log","error.log");
    Method(path:"c:\\log",file:"error.log");
    Method(file:"error.log",path:"c:\\log");
}

省略可能
引数にデフォルト値を定義しておくことにより、未指定時にこれを使うように定義することができる。
public void Method(string name,string extension=".txt")
{
    var file = name + extension;
}

public void Tester
{
    // 明示的指定がない場合はデフォルト値が使用される
    Method("error",".log");
    Method("manual");
}

デリゲート
デリゲート、つまり、処理自体も引数に指定することができる。
ActionやFuncといった定義済みデリゲートを使えば簡単に処理を受け渡しできる。
public void Method(Action action)
{
    Console.WriteLine("Start");

    action();

    Console.WriteLine("Finish");
}

public void Tester
{
    // デリゲートも指定できるので処理自体を渡せる
    Method(()=>Console.WriteLine("DoSomething"));
}

ジェネリック
メソッド定義時にメソッドの型を指定するのだけれど、型を呼び元に指定してもらう方法。
メソッドの後ろにwhereで型に条件付与することができる。
// ここでは whereによりTがクラス(つまり参照型)であると限定している
public void Method<T>(T val) where T : class
{
    var hoge = val;
}

public void Tester
{
    // 参照型(クラスなら何でもOK)
    Method("aaa");
  Method(new Exception());
    //Method(10);  // 値型だとコンパイルエラー
}

拡張メソッド
静的クラスに静的メソッドとして定義する。
thisの後に拡張対象のクラスを指定する。
public static class Extensions
{
    // 静的メソッドとして定義し引数にthis指定する
    public static void ExtendedMethod(this string s)
    {
        Console.WriteLine("extended :" + s);
    }
}

public void Tester
{
   "test".ExtendedMethod();
}
posted by RR at 22:26 | Comment(0) | 文法 | このブログの読者になる | 更新情報をチェックする

2015年10月25日

.NET Framework型とBuild-In型 (Int32とintとかObjectとobject)とか

C#は基本的には.NET Framewrok上で動作するプログラムを記述するための言語であり、この.NET Frameworkが持つ基本データ型に相当するC#独自のBuild-In型が定義されている。

これらは基本的には同じものであり、同等に使用することができる。
      // 同じようなもの。代替も混在も可能 
      int n = Int32.Parse("10");
      Int32 m = int.Parse("20");

一覧

C#の型.NET Frameworkの型説明
boolSystem.Booleantrueまたはfalseのどちらかとなる論理値。既定値はfalse。
byteSystem.Byte0〜255の値を保存する符号なしバイト。既定値は0。
sbyteSystem.SByte-128〜127の値を保存する符号付きバイト。既定値は0。
charSystem.Char16ビットの符号なしUnicode文字。既定値はnull。
decimalSystem.Decimal丸め計算の対象にならない小数。金融計算によく使用される。既定値は0.0m。
doubleSystem.Double倍精度浮動小数点数型。既定値は0.0d。
floatSystem.Single単精度浮動小数点数型。既定値は0.0f。
intSystem.Int3232ビットの符号付き整数型。既定値は0。
uintSystem.UInt3232ビットの符号なし整数型。既定値は0。
longSystem.Int6464ビットの符号付き整数型。既定値は0。
ulongSystem.UInt6464ビットの符号なし整数型。既定値は0。
objectSystem.Objectクラスインスタンスへの参照。既定値はnull。
shortSystem.Int1616ビットの符号付き整数型。既定値は0。
ushortSystem.UInt1616ビットの符号なし整数型。既定値は0。
stringSystem.String文字列オブジェクトへの参照。既定値はnull。
例外
大体は代替可能だが以下の制限がある。

C#6.0以前 列挙体の型指定に.NET Frame型は使えない
    // コロンの後で型を指定できる 
    public enum Signal : int
    {
        Blue,Yellow,Red
    }
    // これはコンパイルが通らない
    //public enum Signal : Int32
    //{
    //    Blue,Yellow,Red
    //}
C#6.0 以降では新規に追加されたnameof演算子で差異がある
    // これはOK
    string a = nameof(Int32);
    // これはコンパイル通らない
    //string b = nameof(int);
posted by RR at 04:04 | Comment(0) | 文法 | このブログの読者になる | 更新情報をチェックする