2014年06月13日

クラス

クラス 振る舞い(メソッド)と状態(フィールド)とを構成要素とする
    class SampleClass
    {
        private int _number;

        public int GetNumber()
        {
            return _number;
        }
    }
インナークラス クラスの中にも定義可能。使用用途はあんまりない。
    class ParentClass
    {
        class ChildClass
        {
        }
    }
静的クラス シングルトンとなる。メンバも静的のみ。インスタンス化せずに使用できる。
    static class StaticClass
    {
        public static int Number { get; set; }

        public static int Add(int x , int y)
        {
            return x + y;
        }
    }
抽象クラス new演算子などでインスタンス化できない。継承元クラスとして使われることが前提。
    abstract class AbstractSampleClass
    {
        protected int _number;
    }
シールドクラス 継承できない。
    sealed class SealdedSampleClass
    {
        public int Number { get; set; }
    }
posted by RR at 05:31 | 基本文法 | このブログの読者になる | 更新情報をチェックする

初期化 コンストラクタと初期化子と諸々

コンストラクタ インスタンス生成時に実行される。戻値なし・クラス名と同名・引数任意で定義
    class Member
    {
        public int ID { get; set; }
        public string Name { get; set; }
        // コンストラクタ
        public Member() 
        {
            ID = 0;
            Name = string.Empty;
        }
        // オーバーロード(引数違い)も定義可能
        public Member(int id,string name)
        {
            ID = id;
            Name = name;
        }
        // オーバーロードされたコンストラクタの起動はthisを使用する
        public Member(int id) : this(id,string.Empty)
        {

        }
親クラスのコンストラクタ 明示的指定無い場合は親クラスのデフォルトコンストラクタが起動される。明示的に指定する場合はbaseを使用する。
    class MemberEx : Member
    {
        public string Address{ get; set; }

        public MemberEx(int id): base(id)
        {
            Address = string.Empty;
        }
    }
静的コンストラクタ いづれかのクラスメンバへの初回アクセス時に実行される。
    public static class Member2
    {
        public static string Name { get; private set; }

        static Member2()
        {
            Name = "Sample";
        }
    }
オブジェクト初期化子 インスタンス生成時に公開プロパティ・フィールドを介して初期化を行う。
            // これまでの方法:コンストラクタ引数や公開プロパティを介す
            var mem1 = new Member(1, "AAA");
            var mem2 = new Member();
            mem2.ID = 2;
            mem2.Name = "BBB";

            // 上記と同等
            var mem3 = new Member() { ID = 3, Name = "CCC" };
            var mem4 = new Member() { ID = 4, Name = "DDD" };

            // 括弧()は略記も可能
            var mem5 = new Member{ ID = 5, Name = "EEE" };
            var mem6 = new Member{ ID = 6, Name = "FFF" };
コレクション初期化子 オブジェクト初期化子のコレクション版
            var dict = new Dictionary<int, string>();
            dict.Add(1, "AAA");
            dict.Add(2, "BBB");
            // 上記と同等
            var dict2 = new Dictionary<int, string>()
            {
                {3,"CCC"},
                {4,"DDD"},
                {5,"EEE"}
            };
匿名型 クラス定義を動的に行う
        // この場で定義
        var shop1 = new { Name = "AAA", Prefecture = "Tokyo" , ID = 3};
        // 以下ようにメンバにアクセス可能
        var shopName1 = shop1.Name; 
Tuple 組オブジェクトを作成する。
        // ジェネリックで型定義を行う方法
        var shop2 = new Tuple<string, string, int>("BBB", "Osaka", 5);

        // 静的メソッドを使う方法
        var shop3 = Tuple.Create("CCC", "Kyoto", 5);

        // 以下のように定義順にメンバにアクセス
        var shopName2 = shop2.Item1;
        var shopName3 = shop3.Item1;
posted by RR at 06:07 | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年06月14日

解放処理 デストラクタとIDisposableインタフェース

デストラクタ インスタンスが破棄される時に呼ばれる。
    class DestructorSample
    {
         // デストラクタ
         ~DestructorSample() 
        {
        }
    }
インスタンスが破棄される契機は実行環境であるCLRの責任範疇なので何時実行されるか不定。

IDisposableインタフェース
IDisposableインタフェースはメンバとして引数なし戻型voidのDisposeメソッドが一つ定義されている。
このインタフェースを実現(継承)しているクラスはusingステートメントを使用できる。

実装パターン
    class DisposableClass : IDisposable
    {
        bool disposed = false;

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);           
        }

        protected virtual void Dispose(bool disposing)
        {
            if(!disposed)
            {
                if(disposing)
                {
                    // マネージドの解放
                }
                // アンマネージドの解放
                disposed = true;
            }
        }

        ~DisposableClass()
        {
            Dispose(false);
        }
    }
使用する側
using (var dst = new DisposableClass())
{
} // このスコープを抜ける時点でdstのDisposeメソッドが呼ばれる
posted by RR at 05:28 | 基本文法 | このブログの読者になる | 更新情報をチェックする

継承

継承
・値型は継承できない。 ・単一継承のみで多重継承はできない。(複数のクラスを継承できないが、何かを継承しているクラスを継承することは可能)
・親クラスを明示的に指定していない場合はObjectクラスが親クラスとなる。
・派生クラスで拡張が予定されている場合はvirtualを指定し派生クラス側ではoverrideを指定する。

vertual指定がないメソッドを上書きする場合はnewを指定する。
    public class BaseClass
    {
        public virtual void Test1()
        {
            Console.WriteLine("BaseClass Test1");
        }

        public void Test2()
        {
            Console.WriteLine("BaseClass Test2");
        }
    }

    public class ExtendedClass : BaseClass
    {
        // オーバーライド
        public override void Test1()
        {
            Console.WriteLine("ExtendedClass Test1");
        }

        // 基底クラスにあるメソッドの隠蔽
        new public void Test2()
        {
            Console.WriteLine("ExtendedClass Test2");
        }

        private void Test3()
        {
            // 自クラス定義のメソッドを実行
            this.Test1();
            // 親クラス定義のメソッドを実行
            base.Test1();
        }
    }

抽象クラス
インスタンス化できない。継承元クラスになることを予定している。
通常のフィールドやメソッドの他、処理を持たない抽象メソッドも定義可能。
    public abstract class AbstructSampleA
    {
        public string name = "AbstructSampleA";
       
        public string GetString1()
        {
            return name;
        }

        // 抽象メソッド 処理を持たず派生クラスでの実装を予定
        public abstract string GetString();
    }

インターフェース
インスタンス化できない。継承(実現という)されることを予定している。
定義できるのはシグネチャのみ。
クラスは複数のインタフェースを継承(実現)することが可能。
    public interface ISampleA
    {
        void SampleMethodA();
    }
    public interface ISampleB
    {
        void SampleMethodB();
    }

    public class ImplementSample : ISampleA , ISampleB
    {
        public void SampleMethodA(){}

        public void SampleMethodB(){}
    }

拡張メソッド
静的メソッドをインスタンスメソッドと同様に呼び出す仕組み。
継承関係ではないけど、そうみえなくもない。
   public class SampleClass
    {
        public string GetSampleString()
        {
            // インスタンスメソッドのように呼べるが別クラスに定義されている
            return this.Test();
        }
    }

    static class SampleClassExtension
    {
        // 引数にthisと型を指定する
        public static string Test(this SampleClass sampleClass)
        {
            return "SampleClassExtension";
        }
    }
posted by RR at 14:22 | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年06月15日

インデクサ

クラスや構造体のインスタンスに対し配列のように添え字でアクセス可能とする
    class OldCalendar
    {
        private string[] names = new string[] { "睦月","如月","弥生","卯月","皐月"};

        public string this[int index]
        {
            get { return names[index-1]; }
            //set { names[index-1] = value; }
        }
    }

    // 使用する側
    var calendar = new OldCalendar();
    // クラスに対し添え字でアクセス
    var feb = calendar[2];
ジェネリクスによる汎用的実装サンプル
    class Collection<T>
    {
        private T[] array;

        public Collection(int length)
        {
            array = new T[length];
        }

        public T this[int index]
        {
            get { return array[index]; }
            set { array[index] = value; }
        }
    }
posted by RR at 04:03 | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年06月16日

プロパティ

プロパティ
実装する側からはフィールドのようなもので、使用する側からはメソッドのようなもの。
非公開フィールドのバッキングストアに保持する値へのアクセッサのようなもの。
インライン展開される(場合があり)のでメソッドよりオーバーヘッドがない。
    class Person
    {
        private string firstName;
        private string familyName;
        private string delimiter;
        private int age;

        public string FirstName
        {
            get { return firstName; }
            set { firstName = value; }
        }
        public string FamilyName
        {
            get { return familyName; }
            set { familyName = value; }
        }

        // getterのみでもよい
        public string FullName
        {
            get { return string.Concat(familyName, delimiter, firstName); }
        }

        // setterとgetterとで異なるアクセスレベル
        public string Delimiter
        {
            get { return delimiter; }
            private set { delimiter = value; }
        }

        public int Age
        {
            get { return age; }
            set 
            {
                // 入力値のチェック
                if (value < 0)
                {
                    throw new ArgumentOutOfRangeException();
                }
                age = value; 
            }
        }
    }
自動プロパティ
バッキングストアを明示的に記載しなくてもよい。
    class Persons
    {
        // バッキングストアやそれへの代入参照するコードがコンパイラが自動生成
        public string Name { get; set; }
    }
自動プロパティの初期化子
    class Persons
    {
        // C#6.0から導入
        public string Name { get; set; } = "Sato";
    }
Getterのみの自動プロパティ
    class Persons
    {
        // C#6.0から導入 初期化子またはコンストラクタでのみ値の設定が可能
        public string Name { get;} = "Sato";
    }
Getterによる状態変更
   class Person
    {
        // getterによる状態変更は副作用があるので要注意
        public int NextAge
        {
            get { return Age++; } 
        }
    }
posted by RR at 00:51 | 基本文法 | このブログの読者になる | 更新情報をチェックする

列挙体

基本
列挙子リストという定数の集合からなり、プログラマが定義できる値型
System.Enumを継承(※Enumに定義されるメソッドを使える。定義した列挙体は継承できない)
// 明示的な指定がない場合はint型であり、列挙子は0から順に1,2と値が付与される
enum TrafficSignal 
{
    Blue,
    Yellow,
    Red
}
// byte,sbyte,short,ushort,int,uint,long,ulongを指定可能
// 列挙子に指定無い場合は前の値に1加算された値となる(C=21)
enum SampleEnum : long 
{ 
    A=5, 
    B=20, 
    C 
}
フラグ
FlagsAttributeを付与することによりビットフィールドとして扱える
    [Flags]
    enum Device
    {
        None = 0,
        Monitor = 1,
        Keyborad = 1<<1,
        Mouse =1<<2,
        All = Monitor | Monitor | Keyborad | Mouse
    }
// ビット演算可能
Device mine = Device.Monitor | Device.Keyborad;
// System.Enumに定義されたメソッドが使える
bool test1 = mine.HasFlag(Device.Monitor); // 戻値:true
bool test2 = mine.HasFlag(Device.Mouse);   // 戻値:false
string test = mine.ToString();             // 戻値:Monitor,Keyboard
数値との変換
// 列挙体定義
enum TrafficSignal {Blue,Yellow,Red}
// 列挙値を数値に変換 キャストする
int value = (int)TrafficSignal.Red;  // 2
// 数値を列挙値に変換 キャストする
TrafficSignal ts = (TrafficSignal)2; // Red
// 数値を列挙値に変換 Enumのメソッドを使う
TrafficSignal ts = (TrafficSignal)Enum.ToObject(typeof(TrafficSignal), 2);
// なお、未定義の値が変換されないようチェックが必要
int value = 引数等;
TrafficSignal ts;
if(Enum.IsDefined(typeof(TrafficSignal),value ))
{
    ts = (TrafficSignal)Enum.ToObject(typeof(TrafficSignal), value );
}
文字列との変換
// 列挙体定義
enum TrafficSignal {Blue,Yellow,Red}
// 列挙値を文字列に変換
string test = TrafficSignal.Red.ToString();
// 文字列を列挙値に変換
TrafficSignal ts = (TrafficSignal)Enum.Parse(typeof(TrafficSignal), "Red");
posted by RR at 19:50 | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年06月21日

構造体

構造体
C#(.NET)の構造体は以下の点に特徴があります。他の言語の構造体とは異なっている可能性があります。
参照型ではなくて値型。よって引数に使うと値渡となる。
・ヒープではなくてスタックが使われる。よって、長い配列とかボックス化とかしないならパフォーマンスが良い。
継承できない。System.ValueType型を(暗黙的に)直接継承する。
// 構造体
struct StructSample
{
    public int Value { get; set; }
}
// クラス
class ClassSample
{
    public int Value { get; set; }
}

class Sample
{
    static void Main(string[] args)
    {
        StructSample ss = new StructSample() { Value = 3 };
        ClassSample cs = new ClassSample() { Value = 3 };

        SetValue(ss);
        SetValue(cs);

        int value1 = ss.Value; //   3
        int value2 = cs.Value; // 100
    }

    static void SetValue(StructSample structSample)
    {
        structSample.Value = 100;
    }

    static void SetValue(ClassSample classSample)
    {
        classSample.Value = 100;
    }
}

C#の構造体は制約が多いのですが、使いどころがハマるとパフォーマンスが向上します。
ネイティブコードをコールするときの引数などでは構造体定義が必須だったりします。
posted by RR at 05:59 | Comment(0) | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年06月22日

定数 const と readonly と getter

const
C#で定数を表すにはconstキーワードを指定する。
値を設定するのは宣言時のみ。
コンパイル時定数。

readonly
読み取り専用を意味するが、定数としても使える。
値を設定するのは宣言時とコンストラクタで可能。
ローカル変数でも使える。
実行時定数。
インスタンスフィールドの場合(静的フィールドではない場合)はインスタンス毎に値が違うこともある。

getter property
Setterを持たないプロパティは定数として使用できる。

    class ConstantValues
    {
        // const
        public const string JAPANESE_CAPITAL = "東京都";
        // readonly
        public static readonly string CURRENT_CAPITAL;
        // getter property
        public static string JAPANESE_CAPTIAL_EN { get { return "TOKYO"; } }

        static ConstantValues()
        {
            CURRENT_CAPITAL = "とうきょうと";
        }
    }

    class Sample
    {
        static void Main(string[] args)
        {
            string capital1 = ConstantValues.JAPANESE_CAPITAL;

            string capital2 = ConstantValues.CURRENT_CAPITAL;

            string capital3 = ConstantValues.JAPANESE_CAPTIAL_EN;
        }
    }

posted by RR at 05:12 | Comment(0) | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年06月23日

反復処理 くりかえし

集合に対する繰り返し処理の記述方法は多々ある

Do-While,While,For
基本的な制御構文
int[] list = { 1,2,3,4,5,6,7,8,9,10};
int pos = 0;

// do-while構文
do
{
    Console.WriteLine(list[pos++]);
}
while (pos < list.Length);

// while構文
int pos = 0;
while(pos < list.Length)
{
    Console.WriteLine(list[pos++]);
}

// for構文
for(int pos = 0 ; pos < list.Length ; ++pos)
{
    Console.WriteLine(list[pos]);
}
Foreach(制御構文)
IEnumerableインタフェースを継承(実現)してる集合にはfoeach文が使えます。
配列も暗黙でSytem.Arrayを介して継承(実現)しているので使えます。
for文とforeach文とでのパフォーマンスの差異は今はあまり無いようです。
int[] list = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
foreach(int item in list)
{
    Console.WriteLine(item);
}
Foreach(配列用)
配列用の処理を提供しているSystem.Arrayクラスにもメソッドがあります。
int[] list = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Array.ForEach(list,item=>Console.WriteLine(item));
Foreach(LINQ)
LINQにもあります。
using System.Linq;
int[] list = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
list.ToList().ForEach(item => Console.WriteLine(item));
Yield(列挙子)
IEnumerableを継承(実現)した処理を簡単に書けるようyieldがあります。
副作用や制限事項が多いので実装時は要注意です。(別途取り上げる予定)
using System.Collections;

public IEnumerable GetEnumeratorSample()
{
    int[] list = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

    for (int pos = 0; pos < list.Length; ++pos)
    {
        yield return list[pos];
    }
}

foreach (int item in GetEnumeratorSample())
{
    Console.WriteLine(item);
}
posted by RR at 21:34 | Comment(0) | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年07月03日

Null許容型

Null許容型とは System.Nullable<T>構造体のインスタンスのこと。Tには値型(構造体)が指定可能。
構造体型に?を付与する略記も可能

メンバーとして主に以下の3つを使用する。
HasValue 値が設定されている場合はTrue、NullならFalse
Value 値が設定されている場合はその値
GetValueOrDefault()メソッド。値が設定されている場合はそれ、Nullならデフォルト値
            // 宣言は2通り可能
            System.Nullable<int> number1 = null;
            int? number2 = null;
            // 値が設定されているかの確認
            if(number1.HasValue)
            {
                // 設定されている場合はその値を参照可能
                int temp1 = number1.Value;
            }
            // 値の設定有無はnullとの比較でも可能
            if(number2 != null)
            {
                var temp2 = number2.Value;
            }
            // 以下の場合number1がnullなのでintのデフォルト値0が戻値
            int temp3 = number1.GetValueOrDefault();

Null合体演算子
左辺値がNullではないならその値、Nullなら右辺値を返す。
            int? number4 = null;
            // number4がnullなので右辺値の-1となる
            int temp4 = number4 ?? -1;

変換
Null許容型へ値を代入する場合は暗黙の変換が行われるが、逆は明示的変換(キャスト)が必要。
            int? number5 = 5;         // int -> int? 不要
            int temp5 = (int)number5; // int? -> int 必要

is演算子の実行結果
宣言ではなく実際の値に依拠する。
            int? number6;

            number6 = null;
            bool ret1 = number6 is int;   // False
            bool ret2 = number6 is int?;  // False

            number6 = 0;
            bool ret3 = number6 is int;   // True
            bool ret4 = number6 is int?;  // True

            bool ret5 = 0 is int;         // True
            bool ret6 = 0 is int?;        // True

その他
パフォーマンスが良くないです。安易に多用するのも問題かも。
posted by RR at 00:00 | Comment(0) | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年07月09日

ジェネリック

ジェネリックとは

型パラメータを指定することにより、その型に応じたクラスやメソッドなどを実現する機能のこと。
<T>のように<>で指定

ジェネリッククラスとジェネリックメソッド

    class GenericClassSample<T>
    {
        public void GenericMethodSample<T>(T t)
        {
        }
    }

制約

指定する型に制約を付与する場合はwhereを指定する。
    // 下記の場合は参照型のみ指定可能
    class GenericSample<T> where T : class
    {
    }

既定値

指定されたタイプにより、値型なら0を、参照型ならNullを返す。
    class GenericSample
    {
        static T GetDefaultValue<T>()
        {
            // default(type)の戻値が既定値
            return default(T);
        }

        static void Main(string[] args)
        {
            var ret1 = GetDefaultValue<int>();            // 0
            var ret3 = GetDefaultValue<GenericSample>();  // Null
        }
    }

共変性と反変性

暗黙の変換に関する規則。ソース内コメント参照のこと。
        static void MainMM(string[] args)
        {
            // string型はobject型を継承しているので代入可能
            string str = "test";
            object obj = str;

            // ジェネリックではこの代入ができない。下記コードはコンパイルエラーとなる。
            // ここで、IList型は IList<T> と定義されている。
            
            //IList<string> strList = new List<string>();
            //IList<object> objList = strList;

            // Covariance 共変性(代入互換性)
            // <out T>のように定義されている場合、この代入が可能となる。
            // IEnumerable<out T> : IEnumerable
            IEnumerable<string> strEnumerable = new List<string>();
            IEnumerable<object> objEnumerable = strEnumerable;

            // 反変性
            // 共変性の逆。<in T>と定義があれば可能
            //  Action<in T>
            Action<object> objAction = _obj => Console.WriteLine(_obj);
            Action<string> strAction = objAction;
            
        }
posted by RR at 20:35 | Comment(0) | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年07月11日

数値型と数値に関する作法等

数値型

C#(.NET)の数値型は構造体として定義されている。(なのでフィールドやメソッドもある)
例えばint型はSystem.Int32構造体の別名。

Mathクラス

算術演算用にSystem.Mathクラスが便利メソッドを提供している。以下はその一部。
            var ret1 = Math.Abs(-10);       // 10 絶対値 10
            var ret2 = Math.Ceiling(12.34); // 13 指定値以上の最少整数値
            var ret3 = Math.Floor(12.34);   // 12 指定値以下の最大整数値
            var ret4 = Math.Max(10, 15);    // 15 指定値で大きい方
            var ret5 = Math.Min(10, 15);    // 10 指定値で小さい方
            var ret6 = Math.Round(12.34);   // 12 五捨六入

数値と文字列型との変換

int型を例にしているが他の数値型も基本的には同じ。
            // 文字列を数値に変換する例(数値に変換不能な文字列が指定されたら例外)
            int number1 = int.Parse("100");
            int number2 = Convert.ToInt32("100");

            // 文字列が数値に変換できるかを確認し可能な場合はその数値、不可の場合はNull
            Func<string, int?> func = (str) =>
            {
                int value;
                return int.TryParse(str, out value) ? (int?)value : null;
            };
            int? number3 = func("100"); // 100
            int? number4 = func("");     // null;

            // 数値を文字列に変換
            string str = 100.ToString();

除算は最後に

2 / 3 * 3 ≠ 2 * 3 / 3
演算は左から順に行われる。2 / 3 は 0.5 だが剰余は切り捨てられて0となりこれに3を掛けても0。
          int ret1 = 2 / 3 * 3;  // 0
          int ret2 = 2 * 3 / 3;  // 2

型変換も要注意

       // 桁数の大きい型へは暗黙の変換が可能  
           short s = 10;
            int i = s;
            // 桁数の小さい型へはキャストすれば変換可能
            long l1 = int.MaxValue;              // 2147483647
            int i1 = (int)val20;                 // 2147483647
            // 桁あふれは想定外の値となるから要注意
            long l2 = (long)int.MaxValue + 1;    // 2147483648
            int i2 = (int)val21;                 // -2147483648

浮動小数点型の問題

0.1など2進数で表せないことから誤差の集積が問題となりえる。
消費税の計算など要注意。
            float f1 = 0;       
            decimal d1 = 0;

            for(int cnt = 0 ; cnt < 100 ; ++cnt)
            {
                f1 += 0.1f;     // 10.0000019
                d1 += 0.1m;     // 10
             }

オーバーフローのチェック

checkedキーワード指定すると桁あふれ時は例外がスローされる。
            // 定数値のみの場合はコンパイル不可
            //int value1 = 2147483647 + 1;
         
            // 桁溢れで不正値となる
            int one = 1;
            int value2 = 2147483647 + one; // -2147483648

            checked
            {
                // checked内では桁あふれ時にOverflowException がスローされる
                int value3 = 2147483647 + one;
            }
 
posted by RR at 01:22 | Comment(0) | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年07月18日

デリゲートとイベント 出番減少したけれども重要


デリゲート

メソッドを参照するための型。
C/C++の関数ポインタに近い機能を持つ(同じではないよと言う必要はある)。
変数と同じように処理を扱えるようになる。
匿名メソッドやラムダ式の導入で使いどころのほとんどを失ったけれど未だに重要。
(以下のサンプルコードでは静的メソッドを使っているがインスタンスメソッドでも全く同じ)
        // デリゲートの定義 シグネチャ(戻値と引数リスト)が指定できればOK
        delegate void SampleDelegate(int id, string name);

        static void Main(string[] args)
        {
            // 大昔はnew演算子を使っていた
            SampleDelegate sd1 = new SampleDelegate(WriteToConsole);
            sd1(1, "taro");
            // 直接メソッドを指定できる
            SampleDelegate sd2 = WriteToConsole;
            sd2(2, "jiro");

            // マルチキャストデリゲート
            // 複数指定できる。追加した順に逐次実行(並列実行ではない)
            SampleDelegate sd3 = WriteToConsole;
            sd3 += WriteToConsole_A;
            sd3 += WriteToConsole_B;

            sd3(3, "saburo");

            // 非同期呼び出し
            SampleDelegate sd4 = SomeHeavyProcedure;
            // デリゲートを定義すると非同期メソッドも使えるようになる。
            IAsyncResult asyncRet = sd4.BeginInvoke(4, "shiro", null, null);
            // メインスレッドでの重たい処理の代替
            System.Threading.Thread.Sleep(1000);
            // 非同期側のスレッドの終了待ち
            sd4.EndInvoke(asyncRet);
        }

        static void WriteToConsole(int id,string name)
        {
            Console.WriteLine("ID:{0} , NAME:{1}",id,name);
        }

        static void WriteToConsole_A(int id, string name)
        {
            Console.WriteLine("Method_A # ID:{0} , NAME:{1}", id, name);
        }

        static void WriteToConsole_B(int id, string name)
        {
            Console.WriteLine("Method_B # ID:{0} , NAME:{1}", id, name);
        }

        static void SomeHeavyProcedure(int id, string name)
        {
            System.Threading.Thread.Sleep(500);
            Console.WriteLine("SomeHeavyProcedure # ID:{0} , NAME:{1}", id, name);
        }

汎用デリゲート

都度デリゲートを定義するのも煩雑なので標準ライブラリに汎用デリゲート(定義済デリゲート)として提供されています。
    using System;
        // 戻値がない場合は Action を使う。引数のリストを指定する
        // 下記は delegate void HogeHoge(int x);と同等
        Action<int> action = (i) => Console.WriteLine("{0} times", i);
        // 戻値がある場合は Func を使う。引数リストの後に戻型を指定する
        // 下記は delegate bool HogeHoge(int x , int y);の定義と同等
        Func<int, int, bool> func = (x, y) => { return (x > y); };

イベント

デリゲートは自クラス内で使うものなので、他クラスからアクセスするためにイベントを使用する。
フィールドに対するプロパティの関係と同じようなもの。
    class Tester
    {
        static void Main(string[] args)
        {
            Shop shop = new Shop() { Id = 1, Name = "Tokyo Shiten" };

            shop.Name = "Kanagawa Shiten";

            shop.OnNameChanged += name => Console.WriteLine("Name changed to [{0}]", name);

            shop.Name = "Shizuoka Shiten";

            shop.Id = 2;
            
            shop.OnIdChanged += id => Console.WriteLine("Id changed to [{0}]", id);

            shop.Id = 3;

        }
    }

    class Shop
    {
        private string _name;
        public string Name 
        { 
            get {return _name;}
            set 
            { 
                _name = value;
                // 値変更時にデリゲートを起動
                if (OnNameChanged != null) OnNameChanged(_name);
            }
        }
        // デリゲートの定義
        public delegate void NameChangedHandler(string name);
        // イベントの定義
        public event NameChangedHandler OnNameChanged;

        private int _id;
        public int Id
        {
            get { return _id; }
            set 
            { 
                _id = value;
                // 値変更時にデリゲートを起動
                if (OnIdChanged != null) OnIdChanged(_id);
            }
        }
        // イベント定義 汎用デリゲートを使った例
        public event Action<int> OnIdChanged;
    }
}

posted by RR at 03:22 | Comment(0) | 基本文法 | このブログの読者になる | 更新情報をチェックする

2014年07月20日

匿名メソッドとラムダ式 超重要

匿名メソッド

処理をインラインで記述できる、が、ラムダ式の登場で使用機会消滅。

   class Sammple
    {
        static void SampleMethod(string val)
        {
            Console.WriteLine(val);
        }

        static void Main(string[] args)
        {
            // メソッド名を指定する
            Action<string> action1 = SampleMethod;

            // インラインで処理を定義するので名前が不要だから匿名メソッド
            Action<string> action2 = delegate(string val) { Console.WriteLine(val); };

            // 引数リストは省略可能
            Action<int> action3 = delegate { Console.WriteLine("hoge"); };
         }


ラムダ式

デリゲート型または式ツリーを作成するために使用。
「引数 => 式」の形式で表す  (input parameters) => expression
            // 引数 => 式(処理) 
            Func<int, int, bool> func1 = (int x, int y) =>
            {
                return (x == y);
            };

            // 引数の型は省略可能(コンパイラが型を推論可能の場合)
            Func<int, int, bool> func2 = (x, y) =>
            {
                return (x == y);
            };

            // 処理が短文の場合は中カッコとreturnも省略可能
            Func<int, int, bool> func3 = (x, y) => x == y;

            // 入力パラメタが1つの場合はカッコも省略可能
            Func<int, bool> func4 = x => x > 0;

            // 入力パラメタがない場合は空のカッコが必要
            Action action = () => Console.WriteLine("Test");
C#6.0からはメソッド本体もラムダ式で書ける。
    class Calc
    {
        public static int Add(int x , int y) => x+y;
    }

posted by RR at 19:28 | Comment(0) | 基本文法 | このブログの読者になる | 更新情報をチェックする

2016年11月25日

Using 使い方は4種類

Usingディレクティブ

使用する側のコードと同じ名前空間に定義されたクラスなどはそのまま使うことができます。
異なる名前空間に定義されている場合は、「名前空間 + クラス名」といった完全修飾名を必要としますが、逐一記述するのも大変なのでファイル冒頭に使用可能な名前空間を列挙するために使います。

using System.IO;

namespace Sample
{
    class Program
    {
        static void Main(string[] args)
        {
            // System.IO名前空間にあるDirectoryクラスを完全修飾名なしで使える
            string[] directories = Directory.GetDirectories(@"c:\test");

            // using ディレクティブの記述がない場合は以下のように完全修飾名が必要
            //string[] directories = System.IO.Directory.GetDirectories(@"c:\test");
        }
    }
}

Usingエイリアスディレクティブ

名前空間が異なるがクラス名が同じ場合にエイリアス(別名)を付与するために使う。

namespace Sample
{
    using AAA = NS_AAA; 
    using BBB = NS_BBB;

    class Program
    {
        static void Main(string[] args)
        {
            // NS_AAA名前空間にあるSameクラスが使われる
            int val = AAA.Same.Value;
        }
    }
}

namespace NS_AAA
{
    public static class Same
    {
        public static int Value => 10;
    }
}

namespace NS_BBB
{
    public static class Same
    {
        public static int Value => 20;
    }
}

Using静的ディレクティブ 

C#6.0から追加された文法。
クラス名を指定することなく静的メンバのようにアクセスできるようになる。

namespace Sample
{
    using static NS_CCC.Calc;

    class Program
    {
        static void Main(string[] args)
        {
            // クラス名の指定をせずに他クラスの静的メンバを使える
            int val = Add(3, 4);
        }
    }
}

namespace NS_CCC
{
    public static class Calc
    {
        public static int Add(int x, int y) => x + y;
    }
}

※拡張メソッドにも似ていますが、使われる側にどのクラスを拡張するかの指定はありません。C#をオブジェクト指向言語と呼ぶのはますます難しくなってきました。

Usingステートメント

インスタンス化されたオブジェクトは、使い終わるとガベージコレクションが自動で破棄してくれます。しかし時にはファイルアクセスやデータベースアクセスのように、使い終わったら明示的に終了処理(クローズ)をしてほしい場合だってあります。
使う側に明示的な終了処理をお願いするためには、IDisposableインタフェースを実現(継承)するというオヤクソクがあります。IDisposableインタフェースにはDisposeというメソッドが一つだけ定義されており、このメソッドに終了処理を定義することになっています。
逆に言うと、IDisposableインタフェースを実現(継承)しているクラスを使う場合は、使い終わったらDisposeメソッドを呼ぶ必要がある。処理中に例外発生することもあるのでTry-Catch-FinallyのFinally句内で呼ぶ必要がるが逐一記述するのはメンドイ。同じことをUsingステートメントが解釈してくれる。

    class Program
    {
        static void Main(string[] args)
        {
            // 普通に書くとこうなる
            var writer =new StreamWriter(@"C:\test.txt");

            try
            {
                // 通常処理
            }
            catch (Exception)
            {
                // 例外処理
            }
            finally
            {
                if (writer != null)
                    writer.Dispose();
            }

            // 上と全く同じことをやってくれる。
            using (var writer = new StreamWriter(@"C:\test.txt"))
            {

            }
        }
    }
posted by RR at 03:21 | Comment(0) | 基本文法 | このブログの読者になる | 更新情報をチェックする