!!!プログラミングC# 第7版(1) [言語 まとめ C#][C#][C# サンプルコード][Effective C# 4.0][Universal Windows Platform][Visual Studio] *プログラミングC# 第7版 *プログラミングC# 第7版(2) {{amazon 4873116503}} {{amazon 4798153826}} !!!C#言語の基礎 !.NET Frameworkクラスライブラリ ::ユーティリティ機能(.NET) ::Windowの機能をラップ ::フレームワーク !言語スタイル ::結合可能性 *小さく組み合わせやすく汎用の機能を重視 ::マネージドコード *CLRに完全に依存するコード *IL(Intermediate Language) 中間言語 **コンパイル済プログラム **--32bit、64bit問わない **--別アーキテクチャでも実行可能 ::連続性とWindows体制 *C#4.0 **OfficeやCOMオートメーションと簡単に連携 !!!基本的なプログラミングテクニック !!名前空間と型 *ライブラリは名前空間で分割 *名前空間には型や名前空間が含まれる *using ディレクティブで名前空間の入力を省略 *名前空間は複数のライブラリに分割して配置されることも多い *どうやって必要な機能を探すのか **ヘルプドキュメントに型が所属する名前空間とライブラリ名が記載されている *Visual Stuido **プロジェクトと同じ名前空間を生成 **自由に編集可能 **プロジェクトのプロパティで別の名前を指定可能 !推奨 ""{企業名}.{技術名}.{機能名}.{サブ名前空間} Microsoft.AspNet.Identity.OWIN !別名 ""using 別名 = 名前空間 using Hg = Foo.Bar.Hoge; var h = new Hg.Hoge(); *クラスについても別名を付与できる using MyHoge = Foo.Bar.Hoge.Hoge; var h = new MyHoge(); !エイリアス修飾子 *エイリアス修飾子(::)を使用し、別名::クラス名 とすることで、本来の名前と別名を区別できる using Hg = Foo.Bar.Hoge; var h = new Hg::Hoge(); !グローバル名前空間エイリアス *グローバル名前空間を指定 using H = Hoge; namespace Hoge { class Foo { } } class Program { } namespace DokushuCSharp { class Program { static void Main(string[] args) { // エイリアス var h = new H.Foo(); // エイリアス修飾子 var i = new H::Foo(); // グローバル名前空間エイリアス var p = new global::Program(); } } } !using staticによるインポート *クラス、構造体、列挙型などを略記できる **静的メンバー **入れ子になった型 using static System.Console; class Program { static void Main(string[] args) { WriteLine("Hoge"); } } !!プロジェクトとソリューション !プロジェクト *単一の出力が生成されるソースファイルの集合 *.csproj 拡張子 *通常XML形式 *msbuild ツールでコマンドラインからビルドできる *自動化テストを別プロジェクトへ分離 !ソリューション *関連する複数のプロジェクトの集合 *.sln 拡張子 **単なるテキストファイル *.suo **ユーザーごとの設定バイナリファイル *Visual Studioでは常に1つのソリューションが必要 *テストプロジェクト **ソリューションにテストプロジェクトを追加 **プロジェクトのコンテキストメニューから、参照の追加 **テスト対象のプロジェクトを設定 !!コメントとリージョンと可読性 ::XMLドキュメントコメント *コードを使用する際にツールチップ表示 *ドキュメントのみを公開することも可能 !!識別子 ::逐語的識別子(verbatim identifier) *@を頭につけることで、予約語を識別子として使用できる *@は識別子とみなされない *他言語との連携が主な用途(C#では、eventは予約語だが、@eventとすることで利用できる) !!変数 ::型 *静的な型付き **object型、dynamic 宣言 を利用すると動的な型付けとできる **var を利用するとコンパイラが初期化子を見て型を解決する !動的型付け(dynamic型) *var、objectとは根本的に異なる *.NET Framework 4以降、DLR(Dynamic Language Runtime)が追加され、これに呼応した拡張 !!定数 *const で宣言 *readonly で宣言 ,相違点,readonly,const ,ローカル変数に付与,不可,可 ,初期化,宣言時/コンストラクター,宣言時のみ ,クラスメンバー,static指定時のみ,無条件にstaticクラスメンバー ,値の決定タイミング,実行時,ビルド時(※1) ,代入できる型,フィールドに代入できるものすべて,制限あり ""※1 例えば、dllで宣言された const を exe で参照コンパイル後、dllの const値を変更、dllを差し替えてもexeの値は、元のまま。将来的にも不変な値だけをconstとすべき。読み取り専用の値はreadonly とまずはするとよい !!宣言空間 ::同じ名前が2つの異なる実体を参照してはいけない範囲 *ネストの宣言空間では、親の宣言空間の中の変数と同じ名前を宣言できない *変数宣言をブロックの先頭に移動した場合意味が変わらない !!ステートメント ::選択 *if *switch **フォールスルーは許可しない **フォールスルーさせたい時には明示的にgotoを利用する switch (i) { case 1: Console.WriteLine("1"); goto case 2; case 2: Console.WriteLine("2"); break; } ::繰り返し *foreach,for,while !!プリプロセッサディレクティブ ::コンパイルシンボル *#if,#else,#endif #if DEBUG Console.WriteLine("Start Method."); #endif *#error,#warning #if SILVERLIGHT #error silverlightをサポートしていません #endif *条件付きメソッド **DEBUGディレクティブと同様な機能を提供 [System.Diagnostics.Conditional("DEBUG")] static void DebugLog(object o) { Console.WriteLine(o); } **--コンパイラはビルド時にコードを効率的に削除 **System.Diagnostics.Debug,および Trace はこの機能を利用している *#line **指定した行番号でエラーが発生したかのように振舞う *#pragma **コンパイラの警告無効化 *#region,#endregion **対応のテキストエディタでは折りたためる !!組み込みデータ型 ,分類,C#エイリアス型,.NET Framework型 ,論理,bool,Boolean ,文字,char,Char ,整数(符号あり),sbyte,System.SByte ,整数(符号あり),short,System.Int16 ,整数(符号あり),int,System.Int32 ,整数(符号あり),long,System.Int64 ,整数(符号なし),byte,System.Byte ,整数(符号なし),ushort,System.UInt16 ,整数(符号なし),uint,System.UInt32 ,整数(符号なし),ulong,System.UInt64 ,小数点数,float,System.Single ,小数点数,double,System.Double ,小数点数,decimal,System.Decimal ,文字列型,string,System.String ,オブジェクト型,object,System.Object ""どちらも利用できるが、一般的にはエイリアスを優先して使用する ::数値リテラル *サフィックスを追加して型を指定できる **123U(unit),123L(long),123UL(ulong),123D(double),123F(float),123M(decimal) **16進数値リテラルには、0x をプレフィックストする **10進浮動小数点数 *数値セパレーター *123456789 は、123_456_789 のように表記できる *ただし、Int32.Parse メソッドなどはこれを正しく認識できない ::decimal型 *内部表現が10進数 *小数部最大28桁 ::数値変換 *checked コンテキスト *キャスト時に桁あふれが発生すると、System.OverflowException が発生 int result(a + b) + c; checked { int r1 = a + b; int r2 = r1 - (int) c; } ::BigInteger *値に応じて大きくなる *理論的には限界はない *System.Numricsへの参照追加が必要 ::文字列と文字 *文字列は、変更不可 *UTF-16で保持 *StringBuilderは、修正可能な文字の並び !!演算子 ::null合体演算子 *string neverNull = s ?? ""; *左式がnullでなければその値、nullあら右式の値を返す ::sizeof *値型のサイズをバイト単位で取得 *sizeof(int) ::nameof *変数、クラス、メンバーなどの識別子を文字列リテラルとして取得 !!文字列リテラル ::逐語的文字列リテラル *「\xx」をエスケープシーケンスとみなさず、表記のままに解釈 * @"C:\work" ::文字列への変数展開 *$"..." で文字列リテラルを現した場合、{...} が式として解釈される * { を表す場合、{{ とする *書式を指定 Console.WriteLine($"fn({x:#0.00}) = {Horner(x,a,4):#0.00}"); ::組み合わせる *$@"..."で組み合わせることができる !!!型 !!クラス ::キーワードclassで定義された型はすべて参照型 ::Nullable *値型に対してもnullを許容するラッパー *Nullable **int 型だが null を持てる !!構造体 *カスタム型に対して組み込み値型と同様な挙動をさせたい **例:カスタムな数値型 *クラスとほとんど同じ特徴 *コンパイラは常に引数なしのコンストラクタを自動的に補充 *すべてのフィールドを0,false,nullで初期化する *効率化、単純化 !C# のデフォルトの == は、object.RefereceEquals と等価 *値型には意味がない public static bool operator ==(Hoge x, Hoge y) {...} public static bool operator !=(Hoge x, Hoge y) {...} public override bool Equals(object o) {...} public override int GetHashCode() {...} !値型を書くべき場合 *2つのみ **数値など値的なものを表現したい **パフォーマンスが得られる *値型は参照型に比べ常に効率的であるわけではない !!メンバ ::フィールド !!コンストラクタ ::コンストラクタからコンストラクタを呼び出す public class Hoge { public Hoge(int id) { } public Hoge(int id, string name):this(id) {} } !静的コンストラクタ *クラスを初期化する際に1度だけ呼び出される *static修飾子必須 *引数指定不可 *主にクラスフィールドの初期化に使用 class Program { public static readonly DateTime INIT_TIME; static Program() { INIT_TIME = DateTime.Now; } static void Main(string[] args) { Console.WriteLine($"{Program.INIT_TIME}"); } } !!メソッド !引数の参照渡し *refを付与すればよい *refの場合、事前の初期化が必要 *ref の情報の流れは双方向、メソッド中でrefに値が渡されなくても可 ::値を返すことを目的としている場合、outキーワードを指定する *出力パラメータであることを指定 public static int Divide(int x, int y, out int remainder) { remainder = x % y; return x / y; } *out キーワードを呼び出し側でも使用する int r; int q = Divide(10, 3, out r); *out はメソッドから呼び出し元へ情報が流れることを要請 *渡した値を読みだすとともに変更可能 *参照型オブジェクトを、参照で渡せば、オブジェクトそのものを置き換えることができる *コンストラクタでも ref、outを指定できる *メソッドの引数だけが参照を使用できる ::out呼び出し時にまとめて宣言可 int q = Divide(10, 3, out int r); !戻り値の参照渡し static ref int OperateAryFirst(int[] nums) { return ref nums[0]; } static void Main(string[] args) { int[] ary = { 0, 1, 2, 3 }; ref int first = ref OperateAryFirst(ary); Console.WriteLine($"{first}"); first = 99; Console.WriteLine($"{ary[0]}"); } ::結果 0 99 !省略可能な引数と名前付き引数 public void Hoge(string a="a", string b="b"){ ... } Hoge("A"); // 引数を順序で指定(bを省略) Hoge(b:"B"); // 引数を名前で指定(aを省略) *引数の名前を指定して呼び出すのは、どのような場合でも可能 *デフォルト引数はコンパイラがインラインに展開するので、デフォルト値を変更すると、再コンパイルしないと反映されない問題がある *代替案はメソッドのオーバーロード ::メリット *デフォルト値を実行時に決定可能 *ref、out 引数を追加で指定できる !可変長引数 *params キーワードを付与することで表現できる *引数リストの末尾にのみ指定可能 static int Sum(params int[] nums) { int result = 0; nums.ToList().ForEach(x => result += x); return result; } static void Main(string[] args) { Console.WriteLine($"{Sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)}"); Console.WriteLine($"{Sum(1, 2, 3, 4, 5, 6)}"); } !拡張メソッド *既存の型の新しいメンバとしてメソッドを書くことができる *第1パラメータに this キーワードを付与 *静的クラスにのみ定義可能 *以下のいずれかで有効 **拡張メソッドが定義されている名前空間をusing指定 **拡張メソッドが定義されている同じ名前空間 *string に追加 namespace MyApp { public static class StringExtensions { public static void Show(this string s) { Console.WriteLine(s); } } } !ローカル関数 *特定のメソッド配下でのみ利用できる static void Main(string[] args) { string Join(string[] strs) { var buf = new StringBuilder(); foreach(var s in strs) { buf.Append(s); } return buf.ToString(); } string[] alpha = { "a","b","c" }; Console.WriteLine($"{Join(alpha)}"); } !!プロパティ *クラスと構造体はプロパティを定義できる *インターフェースはプロパティを持つことはできるがフィールドを持つことはできない !プロパティの自動生成 public class Hoge { public int X { get; set; } = 1; // 初期値を与える } *コンパイラがフィールドを隠しアクセスできない ::読み取り専用の自動生成 public string Name { get; } = "Yagi"; !プロパティおよび変更可能な値型 using System.Windows; public class Hoge { public Point Location { get; set; } } vat hoge = new Hoge(); hoge.Location.X = 123; *エラーとなる *Pointは値型であるため、プロパティのgetで、コピーが返される !!インデクサ *1つ以上の引数をとるプロパティで配列で用いられるのと同じ構文を使ってアクセス可能 var nums = new List { 1,2,3 }; nums[2] += nums[1]; *インデクサのおかげで普通の配列のように利用できる !宣言 *プロパティが、this[...] のようになる以外はほぼプロパティ構文 public classs Hoge { public string this[int index] { get { return index < 5 ? "Foo":"Bar"; } } } *デフォルトのプロパティのみをインデクサにできる !!演算子 ::クラスおよび構造体ではほとんどの演算子の意味をカスタマイズできる ::&&,|| は意味を定義できない ::+演算子の実装 public static Counter operator +(Counter x, Counter y) { return new Counter(x.Count + y.Count); } ::他のオペランド型のサポート public static Counter operator +(Counter x ,int y) { return new Counter(x.Count + y); } public static Counter operator +(int x, Counter y) { return new Counter(x + y.Count); } **int と Counter の加算を可能とする *特定の演算子ではペアで定義することが必要 **==,!= **>,< ::カスタム型変換 public static explicit operator int(Counter value) { return value.Count(); } public static explicit operator Counter(int value) { return new Counter(value); } **explicit を使用しているのでキャスト構文が使用可能 var c = (Counter) 123; var v = (int) c; **implicit を使用すると、型変換はキャストなしで可能となる **--int を long に変換の場合 ::true,false *単項演算子のオーバーロードとして定義されている !!イベント !!ネスト型 ::アクセシビリティが増える *グローバルスコープ **public,internal のみが有効 *ネスト型 **privateが有効となる !!インターフェース !宣言 public interface IDoStuff { string this[int i] { get; set; } string Name { get; set; } int Id { get; } int SomeMethod(string arg); event EventHandler Click; } *個々のメンバーにアクセス修飾子は付与することはできない *.NET の大部分のインターフェースは名前は大文字のIで始まりパスカル記法 *実装する場合通常、インターフェースの各メソッドをpublicメンバーとする !明示的な実装 *同じシグネチャのメソッドを複数のインターフェースを実装し実装を分けたい場合 int IDoStuff.SomeMethod(string arg) { ... } *型自体の参照を通して使うことはできない *インターフェース型を通してのみ利用できる !!列挙(enum) *名前付きの値の集合を定義 *64 ビットの enum public enum Hoge : long { ... } *enum 型は数値 *組み込み型の数値や構造体と同じ *定数値以外のいかなるメンバも定義できない !フラグとして扱う [System.Flags] public Hoge { A = 1 B = 2 C = 4 } Hoge.A | Hoge.B !データ型の指定 enum Hoge : long { A, B, } !Enumに変換 enum Hoge { Foo, Bar } ::Parse var b = (Hoge)Enum.Parse(typeof(Hoge), "Bar"); Console.WriteLine(b.ToString()); ::TryParse if (Enum.TryParse("Foo",out Hoge f)) { Console.WriteLine(f.ToString()); } !foreachで回す ::値 foreach(HogeEnum tag in Enum.GetValues(typeof(HogeEnum))) { } ::名称 foreach(string tag in Enum.GetNames(typeof(HogeEnum))) { } !拡張メソッドで列挙にメソッドを実装 public enum HogeType { Sample, } public static class HogeTypeExt { public static string GetReportDefName(this HogeType hogeType) { string result = null; switch (hogeType) { case HogeType.Sample: result = @"HogeTypeSampleName!!"; break; } return result; } } !!匿名型 *プロパティにいくつか値を格納するだけの理由なので型が必要など *LINQ の便宜上追加された仕様 var x = new { Id="123",Name="abc" }; !匿名型のリスト var list = new List(); list.Add(new { Id="123",Name="abc" }); !!部分型とメソッド ::部分型宣言 *型宣言が複数ファイルにまたがっている可能性 *型宣言に partial *コード作成ツールを簡単に書くために用意 ::部分メソッド *生成されたファイルにてメソッドが宣言 *別のファイルでメソッドが実装 !!!ジェネリック型 !型パラメータ *コンパイル時に別の型に置き換えることを可能とする *プレースホルダー *List の T !ジェネリック型 *クラス、構造体、インターフェースはすべてジェネリックになることができる public class Hoge { public Hoge(T x, string name) { ... } } *クラスの本体の内側では、通常型名の用いることができる場所ならどこでも、T を用いることができる *完全な型ではない !非バインド *完全な型となるためには、型パラメータが指定されなければならない !制約 *:型引数が特定の要請を満たさなければならいことを明示できる public static class Deferred where T : new() { private static T _instance; public static T Instance { get { if (_instance == null) { _instance = new T(); } return _instance; } } } *遅延された構築を提供 *プロパティを最初に読みだすまでインスタンスは構築されない *T は、引数なしのコンストラクタを供給する必要がある !型制約 *特定の型と互換 public class Hoge : ICompara where T : IComparable { ... } !参照型制約 public class Hoge where T : class { ... } *型引数を参照型に限定 *可能となる **nullかテストできる **as 演算子を使用できる **他のジェネリック型が使用できる !値型制約 public class Hoge where T : struct { ... } *型引数を値型に制約 *int など組み込み算術型に対しても有効 *Nullable はこの制約を使用している **通常、int? のように記述する !複数の制約 *1つの型引数に対して複数の制約を課したい場合、リストとして並べる public class Hoge where T : IEnumerable, IDisposable, new() { ... } !ゼロ系の値 *default(型) *その型のデフォルトの初期値 *ジェネリックな型パラメータに対しても使用できる static void PrintDefault() { Console.WirteLine(default(T)); } !ジェネリックメソッド public static T GetLast(T[] items) { return item[item.Length -1 ]; } int[] values = {1,2,3}; int last = GetLast(values); *ジェネリック型の中でも、以外でも定義できる !型推論 *多くの場合、ジェネリックメソッドの型引数を推論することができる int[] values = {1,2,3}; int last = GetLast(values); !ジェネリックの内側 *C++のテンプレートとは全く異なる !!!コレクション ""C#1.0から提供されているSystem.Collections と、System.Collections.Generic に分類できる。前者は古いライブラリで、ジェネリックに対応しておらず、現在ではほぼ利用しない。 !!分類 ,分類,クラス ,リスト,List ,リスト,LinkedList ,リスト,Stack ,リスト,Queue ,セット,HashSet ,セット,SortedSet ,ディクショナリ,Dictionary ,ディクショナリ,SortedDictionary ,ディクショナリ,SortedList !!配列 ::System.Array の派生 ::初期化 *var hoge = new string[]{ "a","b","c" }; *string[] hoge = {"a","b","c"}; *var hoge = new[] {"a","b","c"}; **コンパイラによる型推論 **引数として利用できる **Hoge(new[] {"a","b","c"}); ::キーワード params による可変個引数カウント *public static void WriteLine(string format, params object[] arg) *Console.WriteLine("{0},{1},{2}",1,2,3); *最後の引数で、配列型に対してしか書けない ::比較 *Enumerable.SequenceEqualメソッドを利用(System.Linq名前空間) ::検索とソート *Array.IndexOf **要素探索を行う最も素直な方法 *Array.FindIndex int x = Array.FindIndex(nums, x=> x>0); *Array.FidAll **該当する複数要素 *Array.Sort **ソート *Array.BinarySearch **バイナリサーチ ::多次元配列 *ジャグ配列 **配列の配列 int[][] nums = new inte[3][]{ new [] {1,2} new [] {3,4,5} new [] {6} }; *四角形配列 **多次元のインデックス処理をサポートする単一の配列オブジェクト int[,] grid = new int[3,3]; var grid2 = new int[,] { {1,2}, {3,4}, {5,6} }; var cube = new int[,,] { { {1,2}, {3,4} }, { {5,6}, {7,8} } }; ::コピーとサイズ変更 *Array.Copy *Array.Resize *Array.Reverse *Array.Clear !!List ::インデクサを備える *サイズ可変の配列のようにふるまう **配列 T[] を期待しているパラメータに Listは渡せない **IList を期待しているパラメータには、配列もListも渡すことができる ::初期化子 *var nums = new List { 1,2,3 }; !!リストインターフェースとシーケンスインターフェース ::.NET Frameworkのコレクションインターフェース *IList **ICollection,IEnumrable も実装する必要がある **List,配列 は IList を実装している *ICollection **変更可能なコレクションに対する汎用インターフェース **実装時には、IEnumerable も提供する必要がある *IEnumerable **実装者に対する要求が最も少ない **列挙子を返す **LINQ to Objects の核心を占める ""IEnumerableを実装し、GetEnumeratorメソッドを定義するとクラスそのものを列挙可能にできる !リストとシーケンスを実装する *IEnumerable または、IList のどちらかの形式で情報を提供すると便利なことが多い *LINQ to Objects が提供する演算はすべて IEnumerableで動く !!イテレータ *yield を使って列挙可能なシーケンスを生成 *IEnumerable を実装する言語サポート public static IEnumerable Numbers(int start, int count) { for (int i=0; i *List にと似ているがAPIがずっと小さい *リストが分かる方法を提供 *IList,IListを実装 !!ReadOnlyCollection *変更不要なコレクションを提供 *リストのラッパーとして利用できる !!ディクショナリ ::Dictionay *IDictionay インターフェースを実装 ::TryGetValue *データをディクショナリから探す ::インデクサを利用して取得も可能 *対応キーがない場合、KeyNotFoundException *ContainsKey とあわせて利用 !値の設定 ::インデクサ利用の場合 *既存のエントリを上書き ::Add利用の場合 *既存エントリの場合例外 !初期化 *IEnumerable> ::コレクション初期化構文を利用できる var dic = new Dictionary { {"One",1}, {"Two",2} }; ::インデックス初期化子 *C#6から下記の初期化記法が追加されているこちらを推奨 var dic = new Dictionary { ["One"] = 1, ["Two"] = 2 }; *大文字小文字を区別しないディクショナリ var dic = new Dictionary(StringComparer.InvariantCultureIgnoreCase); ::ソート済みディクショナリ *SortedDictionary *SortedList !!集合 ::ISet *実装 **HashSet **SortedSet !!キューとスタック ::Queue ::Stack !!リンクリスト ::LinkedList ::利点 *要素の挿入削除が安価 ::欠点 *メモリオーバーヘッドがかなり高い *余分なオブジェクトがヒープに必要 *n番目の要素を得るのにn個のノードを走査する必要 !!並行コレクション ::並行プログラミングの課題を解くよう設計 *ConcurrentQueue *ConcurrentStack *ConcurrentDictionary *ConcurrentBag *BlockingCollection !!タプル *固定個数の要素を含むデータ構造 *各要素は異なる型でもよい *8つの型引数を取る8タプルまで準備されている Tuple *Item1,Item2 など読み取り専用プロパティで内容にアクセス var four = Tuple.Create(1,"One",new[] {1,2}, 3.4); ::例 public static (int max, int min) MaxMin(int initial, params int[] nums) { int max = initial; int min = max; nums.ToList().ForEach(x => { max = (x > max) ? x : max; min = (x < min) ? x : min; }); return (max, min); } static void Main(string[] args) { var t = MaxMin(4, 14, 7, 2123, 6, 1, 23, -123, 1); Console.WriteLine($"Max={t.max},Min={t.min}"); } ::実行結果 Max=2123,Min=-123 !!!継承 !!値型 *継承に対応していない ::スライシング *派生型を基底型に代入すると拡張情報が失われる *参照型であれば発生しない !!構文 *基底型はコロンに続けて記入 *インターフェースを実装する場合もコロンに続ける public class DrvCls : BaseCls { } public class DrvCls : BaseCls, IDisposable { public void Dispose() {} } !!継承と型変換 !ダウンキャスト ::キャスト構文 *成功するとは限らない *InvalidCastException var d = (DerivedCls) baseCls; ::as 演算子 *例外発生しない *失敗したら null var d = baseCls as DerivedCls; ::is演算子 *参照が特定の型か調べる if (!(baseCls is DerivedCls)) {} !!インターフェース継承 ::多重継承に対応 *多重継承の多くの問題は純粋な抽象型では生じない ::interfase IBoth : IBase1, IBase2 {} !!ジェネリック ::ジェネリッククラスから派生させる場合、型引数を渡さなければならない *派生クラスがジェネリックではない **public class NonGenericDerived : GenericBase {} **具体的な型を渡す *派生クラスがジェネリック **public class GenericDerived : GenericBase {} **自身の型パラメータを渡す *組み合わせ **public MixedDerived : GenericBase2 {} **複数の型パラメータがある場合、2つの手法を組み合わせられる ::共変性と反変性 *共変性(covariance) **共変性型パラメータ **--public interface IEnumrable : IEnumrable **.NET Framework 4 以降には、共変の型パラメーターを持つジェネリック インターフェイス **--IEnumerable、IEnumerator、IQueryable、IGrouping **--すべての型パラメーターは共変のみであるため、型パラメーターはメンバーの戻り値の型だけに使用 *反変性(contravariance) **反変性型パラメータ **--public interface ICompara **.NET Framework 4 以降には、反変の型パラメーターを持つジェネリック インターフェイス **--IComparer、IComparable、IEqualityComparer など **--型パラメーターは反変のみであるため、これらの型パラメーターは、インターフェイスのメンバーのパラメーター型としてのみ使用 *数学の圏論(category theory) から来ている *一般に、共変の型パラメーターはデリゲートの戻り値の型として使用でき、反変の型パラメーターはパラメーター型として使用できます。 !!System.Object(object) ::汎用コンテナとして利用できる ::すべてのクラスで利用できる object クラスのメソッド *ToString *Equals **null の場合に利用できる、静的バージョンもある *GetHashCode *GetType *オブジェクト自身からのみ利用可能 **Finalize **MemberwiseClone **--オブジェクトと同じ型のインスタンスを作成 **--フィールドをすべてコピーしたもので初期化 !!アクセシビリティと継承 *https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/accessibility-levels ::public *アクセスは無制限 ::protected *定義された型および派生型 ::internal *同じassembly内のすべてのコード ::protected internal *すべての派生型およびassemblyを共有するコードから ::private *宣言された型内部からのみ ::private protected *(C# 7.2 以降)包含クラス、または包含クラスから派生した型 !省略された場合のデフォルト http://did2.blog64.fc2.com/blog-entry-208.html ,対象,アクセシビリティ ,名前空間,public ,型(入れ子にされていないクラス、構造体など),internal ,クラスの子メンバ(メソッド、入れ子にされた型など),private ,構造体の子メンバ,private ,インターフェイス,public ,列挙型,public !!Virtual メソッド とオーバーライド *派生型で置き換えできるメソッド *デフォルトでは virtual ではない *どのメソッドを呼び出すかは実行時に決定される *オーバーライドする場合、override キーワードで明示する *抽象メソッド(abstract method) *virtual メソッドはデフォルトの実装を提供せずに定義できる *クラスも抽象クラスとなる *一度公開されたインターフェースは変更してはいけない **すべてのコードを完全に管理できている場合許される *あらたな virtual メソッドはほとんど問題をおこさない !!メンバーの隠蔽 *public new void SomeMethod() {} *newキーワードで明示しコンパイラ警告を抑制 ""プロパティにも適用される ""オーバーライドは、virtual/override のセットが前提だが、隠蔽は、派生クラスでnewするだけで利用可能。ただし、隠蔽ではポリモーフィズムは動作しない。 !!sealedメソッドとクラス *sealed メソッドは virtual の逆でオーバーライドができないようにシールする *newで隠蔽することは可能 *メソッドはデフォルトでシールされている *オーバーライドしたのちさらなる変更をさせないために利用できる *クラス全体をシールし派生できないようにすることも可能 *構造体と列挙型はシールされている ::目的 *不変性を保証 *継承を用いた拡張が容易な型を設計するのが困難 *派生クラスをすべて管理下に置いていない場合、基底クラスを変更するのはほぼ不可能 !!基底メンバへのアクセス ::base キーワード *virtual メソッドのディスパッチメカニズムを無効化 *オーバーライドされる元のメソッドを呼び出す *base.OriginalMethod(); !!継承とインスタンスの構築 ::派生クラスは基底クラスのメンバを受け継ぐが、コンストラクタは異なる ::基底クラスのコンストラクタを使用して派生クラスを構築できない ::base による明示的なコンストラクタの呼び出し *public DerivConstructor() : base(123) {} *public DerivConstructor(int id) : base(id) {} ::インスタンスフィールド初期化子は基底クラスの構築が行われるよりも先に行われる !!特別な基底型 ::.NET Framework 特別な基底型をいくつか定義 *System.Object *System.ValueType **すべての値型の抽象基底クラス **System.Objectから派生 **明示的な派生は許可しない **struct キーワード *System.Enum **System.ValueTypeから派生 **明示的な派生は許可しない **enum キーワード *System.Array **すべての配列の基底クラス *System.Exception *System.MulticastDelegate **デリゲートの基底クラス *System.Attribute **属性 !!!オブジェクトの生存期間 !GC(ガベージコレクション) ::到達可能性の決定 ::GCの不慮の死 ::弱参照 *GCは弱参照を追跡しない *オブジェクトが弱参照からしか到達できないなら、GCはそれが到達不能なオブジェクトであるかのように扱う *WeakReference ::メモリの回収 *CLRはヒープを世代で管理 **世代 **--オブジェクトが何回GCを生き延びたか **世代2を生き延びたオブジェクトは長い間、到達可能なままになる可能性が高い **非常に短い生存期間のオブジェクトと非常に長い生存期間のオブジェクトは効率的に扱える ::GCモード *カテゴリ **ワークステーション **--デフォルト **--モード **----並行モード **------複数の論理プロセッサ時デフォルト **------意図的に無視したい場合 **-------- **--論理プロセッサが複数ある場合にのみ利用可能 **--スループットはワークステーションより優れる **--GCの間はすべてのCPUコアを同時に使用する **----ワークステーションよりかなり多くのメモリを使用する **--.NET4.5 からは、バックグラウンドGCも利用可能 ::コンパクションの不慮の死 *CLRはヒープブロックの再配置を選択的に行わないようにする **GCはI/O操作の途中でも可能 **一部のヒープブロックは固定できる **固定ブロックは移動できないことをGCに伝えるフラグを設定 **固定する方法 **--fiexd キーワードを明示的に使用 **----フィールドや配列要素といった記憶域の位置への生ポインタを取得することができる **--相互運用性を介す **----COMコンポーネントやWin32 API のようなアンマネージドコード **----ポインタを必要とするAPIは自動的にブロックを固定 **--I/Oベースのストリーム実装 **----必要な場合、配列を含むヒープブロックを固定 ::強制GC *System.GC **GCを強制的に起こすことができるCollectメソッドを提供 **--世代番号を指定 **--引数を取らない場合フルGC !デストラクタとファイナライゼーション ::ファイラナライゼーション *CLRがもうすぐ削除されようとしていることをオブジェクトに告げる ::デストラクタ public class Hoge { ~Hoge() { } } *Finalize メソッドをオーバーライドしてコンパイルされる *デストラクタ内部から他のオブジェクトを利用することは保証されない *ハンドラによって表現されたエンティティが使われなくなったことをCLRに関係ないものに伝えるコードを置く場所 ::重大なファイナライザ *CriticalFinalizerObject から派生したクラスのファイナライザ **2つの保証 **--有効な時間制限を超えていてもファイナライザに実行機会を与える **--到達不能であることを同時に発見したオブジェクト群に対してCLRは重大なファイナライザを行う前に通常のファイナライザを実行 **----自分自身のファイナライズでそのオブジェクトを使用することは安全 **SafeHandle クラス **--.NET にハンドルをラップするのに好ましい **--CreticalFinalizeObject から派生している !IDisosable ::コードがこのインターフェースを実装しているオブジェクトを利用しているならそのオブジェクトを使い終わったらDisposeを呼び出すべき ::直接Disposeを呼び出すのは自由 ::2つの方法でサポート *foreach **列挙子でforeachループが実装されている場合、IDisposableを使ったコードを生成 *using **IDisposable を実装したオブジェクトを使い終わったらDisposeを呼び出す **複数のリソースを使用するにはusingを重ねる using(Stream src=File.OpenRead(@"C:\work\test.txt")) using(Stream dst=File.Create(@"C:\work\out.txt")) { ... } !ボックス化 ::型オブジェクトの変数に値型を参照できるようにする ::オブジェクト変数はヒープ上のものの参照を保持するだけ ::概念 public class Box where T : struct { public readonly T Value; public Box(T v) { Value = v; } public override string ToString(){ return Value.ToString(); } ... } ::object 型に box 化された int を取り出すのに、(int)o と書ける ::組み込みの値型だけではなく、すべての構造体に対して自動的に利用できる ::Nullable のボックス化 *Nullable の代わりに、int? と書ける *すべて成功 object boxed = 42; int? nv = boxed as int?; int? nv2 = (int?) boxed; int v = (int) boxed; !!!例外 !!例外オブジェクト *Exception基底クラスから派生 !別の例外が原因の場合 ::InnerException *例外が投げられた場所に関する情報を持っている ::StackTraceプロパティ *どのメソッドを実行していたか *例外クラス共通で利用できる ::TargetSite プロパティ !!例外フィルター *catchブロックに条件句を加えられる try{ } catch (FileNotFoundException ex) when (ex.Message.Contains(".dat")) { } !例外フィルターによるマルチキャッチ try{ } catch (Exception ex) when (ex is FileNotFoundException || ex is ArgumentException) { } !!チェック例外 *Javaでのチェック例外に相当するものはC#にはない *メソッドが投げる例外をメソッド宣言には書けない !!finallyブロック *関連するtryブロックが終了されると常に実行される **gotoステートメントでブロック外に出るときも実行 *IDisposableをサポートしないリソースをCloseするときなど有用 **サポートする場合はusingステートメントを利用できる !!例外をスローする *適切な型を構築して throw キーワードを使う *Exceptionの派生クラス !throw式 *C#7以降では、式としてthrowを利用できる ::条件演算子 *i < 0 なら例外 Console.WriteLine(i >= 0 ? i : throw new Exception("minus")); ::null合体演算子 *strがnulllなら例外 Console.WriteLine(str ?? throw new Exception("null")); ::式形式のラムダ式/メソッド *例外をスローするだけ void Hoge() => throw new NotSupportedException("not implemented"); !例外を再スロー *間違った例 try { DoSomething(); } catch(IOException x) { LogIOError(x); throw x; //NG } *エラーなくコンパイルできる ::深刻な問題 *例外がスローされたもともとのコンテキストを失ってしまう *新たな例外として扱い、位置情報を再設定してしまう *StackTrace *TargetSite *コンテキストを失わずに再スロー try { DoSomething(); } catch(IOException x) { LogIOError(x); throw; //OK } *catch ブロック内だけで許される *プロパティは依然としてもともとの場所を示す !素早く失敗させる *アプリケーションが絶望的に破壊された場合 *例外は処理されてしまう可能性がある *Environment.FailFast **CLRはメッセージをイベントログに書き込みWER(Windows Error Reporting)に詳細を提供しアプリケーションを終了 !!例外型 ::.NET Frameworkには非常に多くの例外型を定義 *独自の例外を定義できるが、多くの場合既存から選ぶ ::知っておくべき重要な例外型 *ArgumentException **不適切な引数の基底クラス **ArgumentNullException **ArtgumentOutOfRangeException **CultureNotFoundException *AggregateExcepition **例外のコレクションを返す **--InnerExceptions プロパティ *InvalidOperationException **現状でオブジェクトが処理できない何かを行う時にスロー **ObjectDisposedException *NotImplementedException **何かが足りないことを意味する **IDEがインターフェースの実装やイベントハンドラを生成する場合に作成されるスタブメソッドのデフォルト実装 **--開発者が実装を忘れないように *NotSupportedException **インターフェースが要求したときにスロー **例えばIList はコレクションを変更するメソッドを定義しているが、コレクションが変更可能である必要はない **読み取り専用の場合、変更するメンバからスローする !!カスタム例外 *最小限の要件はExceptionから派生すること *設計ガイドライン ::基底クラス *組込みの例外型の多くは以下のいずれかから派生 *ApplicationException **アプリケーションによって生成される例外 *SystemException **.NET Framework によって生成される例外 *これらのどちらからの派生も避けるべき *もともとの意図に反し効果的な区別ではない *シナリオによってはどちらの意図にもなる *Exception から直接派生させる *既存の例外を特殊化したものは除く *Exceptionはエラーのテキストで説明すべき *直接記述する代わりに Sytem.Resource 名前空間の機能でローカライズするようにもできる ::.NET Framework のクラスライブラリ設計ガイドライン *例外はシリアライズ可能にすべき *シリアライズ化は継承されない *クラス宣言の前に[Serializable] 属性を追加する *ISerializable のメンバーだけをオーバーライドする *HResultプロパティを設定するかどうか *相互運用性の境界に到達すると重要な意味を持つ *.NET の例外はアンマネージコードに伝搬できない *例外を表すエラーに最も相当するCOMエラーコードを返すべき *FileNotFoundException の HResult=0x80070002 *Win32 SDK ERROR_FILE_NOT_FOUND に相当 !!未処理例外 ::CLRは未処理例外がスタックの最上位に到達したことを発見する方法を提供する static void Main(string[] args) { AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; DoSomething(); } private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e) { Console.WriteLine($"例外は処理されません:{e.ExceptionObject}"); } *例外を停止するのは手遅れ *ログを置く場所の提供 ::フレームワークで未処理例外を処理 *ASP.NET **globa.aspx **--Application_Error メソッド *WPF **Application クラス **--DispatcherUnhandleException イベント *Windowsフォーム ** Application クラス **--ThreadException メンバ ::デバッグと例外 *デフォルトではVisual Studio のデバッガは未処理例外にステップイン *デバッガの例外設定ダイアログで動作を設定できる !!非同期例外 *非同期プログラミングとは関係ない *実行時のコードとは関係なく非同期で発生するという意味 *ThreadAbortException **他のスレッドがコードをアボート *OutOfMemoryException *StackOverflowException !!!デリゲート、ラムダ、イベント !!デリゲート型 !デリゲートパラメータを持つメソッド *public static int FindIndex (T[] arry, Predicate match) *predicate(述語、プレディケート) **あるものが真か偽を判断する機能 **Predicateデリゲート型 public delegate bool Predicate(T obj) !アクセシビリティ *他の型同様 **public,internal,private,protected **他の型にネストできる !メソッドを参照する *シグネチャが一致すればメソッドは特手のデリゲート型と互換性を持つ *一致は正確である必要はない **暗黙的な参照変換がパラメータに適用できる場合にはより一般的なメソッドを用いることができる **--bool Hoge(object o) **----いずれとも互換 **------Predicate **------Predicate !生成 ::new キーワード *通常コンストラクタへの引数を渡す個所に互換性を持つメソッドの名前を指定できる var p = new Predicate(IsGreaterThanZero); *実際には new をデリゲートに使用することはほとんどない *コンパイラが推論できない場合のみ ::暗黙的作成 Predicate p =IsGreaterThanZero; *他のクラスのメソッドへのデリゲート Predicate p = OtherClz.IsGreaterThanZero; *インスタンスメソッドも参照できる *スコープ内で名前によって参照 ::CreateDelegate *Delegateクラスの静的メソッド !マルチキャストデリゲート *2つ以上のメソッドを参照出来る *.NET Framework 以外で自分で定義したデリゲートの基底型 *マルチキャストの機能は、Combine 静的メソッドを通して利用出来る *2つのデリゲートをとり、1つのデリゲートを返す *得られたデリゲートを実行すると、元の2つを順に呼び出したような動作 *戻り値は最後に実行されたメソッドのものが返される *実際はCombineメソッドではなく、「+」「+=」 を利用 Predicate p = gereaterThanZero; p += greaterThanTen; p +=greaterThanOneHundred; *Remove ではなく、「-」「-=」 が利用出来る *GetInvocationList **メソッドを順に呼び出せる delegate string Greet(string name); class Program { static string Morning(string name) { Console.WriteLine($"Good morning {name}."); return nameof(Morning); } static string Night(string name) { Console.WriteLine($"Good night {name}."); return nameof(Night); } static void Main(string[] args) { // マルチキャストデリゲート Greet greet = new Greet(Morning); // new を使った生成 greet += Night; // 暗黙的生成 // 最後の呼び出しの結果が返る Console.WriteLine($"{greet("yagi")}"); } } ::結果 Good morning yagi. Good night yagi. Night ::DynamicInvoke *動的にあらゆる型のデリゲートを呼び出すことが出来る *object[] を引数にとる *複数のメソッドを参照する場合戻り値は最後のメソッドのものとなる !呼び出し *メソッドパラメータ、プロパティとして使うことも可能 public static int Hoge(Predicate userCallback) { return userCallback(42); } !共通デリゲート型 *.NET Framework **有用なデリゲート型を提供 **--汎用デリゲート型 **----Action **------戻り値 void public delegate void Action(); public delegate void Action(T1 arg1); public delegate void Action(T1 arg1, T2 arg2); : **----Func **------Actionとよく似るが、戻り値を返す public delegate TResult Func(); public delegate TResult Func(T1 arg1); public delegate TResult Func(T1 arg1, T2 arg2); : **--ほとんどは特殊化されている **----System.IO.SerialPinChangedEventHandler **------シリアルポートに対する処理を行うときにのみ用いられる !型互換性 *C#で定義するデリゲートはすべてMulticastDelegateから直接派生 *反変性 **反変 (contravariant) : 狭い型(例:float)から広い型(例:double)へ変換すること。 *共変性 **共変 (covariant): 広い型(例:double)から狭い型(例:float)へ変換 Predicate po =IsLongString; Predicate ps = po; *不変性 **不変 (invariant): 型を変換できないこと。 !構文 *呼び出し **スタイル **--Invoke で明示的に呼び出し **--関数的に呼び出し **非同期呼び出しを行うメソッド組 **--BeginInvoke **----終了を待たずに呼び出し側に戻る **----out パラメータを持つ場合削除される **----追加パラメータ **------AsyncCallback **--------非同期実行が完了した時点でコールバック **--------デリゲート型 **------object **--------処理が完了したときに返される **--------複数の似た処理が同時に進行している場合など、どの処理か知るために有用 **--EndInvoke **----BeginInvokeの処理結果を知るために使用 var res = delMethod.BeginInvoke(); delMethod.EndInvoke(res); // 実行が遅延される **--非同期デリゲート呼び出し **----.NETの初期は一般的だった **----現在ではあまり広く使われない **------.NET4.0 で導入 **--------タスク並列ライブラリ(Task Parallel Library :TPL)の導入 **--------これを用いれば、スレッドプールのサービスを柔軟かつ強力に抽象化できる **------非同期プログラミングモデル(Asynchronous Programming Model)という古い形式 **--------C#の新しい非同期言語機能にうまく適合していない **------C#2.0 でインラインメソッドが導入された **--------1スレッドから別スレッドへ値の集合を簡単に渡す方法の優れた手段 !インラインメソッド *独立したメソッドを明示的に記述する必要なくデリゲートを作成できる *他のメソッドの中で定義 ::匿名関数(naonymous function) *値を返す場合 ::有用 *デリゲートの機能が単なるメソッドへの参照だけではない *デリゲートはインスタンスメソッドのターゲットオブジェクトという形でコンテキストを含むことができる ::2つの定義方法 *古い方法 **delegate キーワード **--匿名メソッド int idx = Array.FindIndex(ary, delegate(int val) { return val == 5; }); **--引数リストを完全に省略できる利点 EventHandler clickHandler = delegate { Debug.Writeline("Clicked");}; *.NET 3.5 !標準ライブラリで用意されているデリゲート ,デリゲート型,概要 ,Action,値を返さないメソッド ,Func,TResult型の値を返すメソッド ,Comparision,型が同じ2つのオブジェクトを比較するメソッド ,Converter,オブジェクトをほかの型に変換するメソッド ,Predicate,オブジェクトが条件を満たしているか判断するメソッド !!ラムダ式 *delegateキーワードは書かずに、=> で引数とメソッド本体とをつなぐ int idx = Array.FindIndex(ary, x => x == 5); !バリエーション *本体が1文の場合、ブロック {} を省略可能 *文の戻り値がそのまま戻り値とみなされるため、return も省略可能 *引数の型は暗黙的に推論される *引数が1つの場合、引数の括弧も省略可能 *引数がない場合の括弧は省略できない Predicate p1 = value => value > 0; Predicate p2 = (value) => value > 0; Predicate p3 = (int value) => value > 0; Predicate p4 = value => { return value > 0; }; Predicate p5 = (value) => { return value > 0;}; Predicate p6 = (int value) => { return value > 0;}; *引数のないラムダ Func isAfternoon = () => DateTime.Now.Hour >= 12; !キャプチャされた変数 *インラインメソッドからその外側のメソッドの変数参照ができる public static Predicate IsGreaterThan(int threshold) { return value => value > threshold; } Predicate greaterThanTen = IsGreaterThan(10); bool result = greaterThanThen(200); !ラムダと式ツリー *ラムダはデリゲートを提供するだけではない *他にも隠れた機能 *データ構造を作成できる *Expression *Tはデリゲート型 *Expression 自体はデリゲートではない *Entity Framework var expensiveProducts = dbCOntext.Products.Where(p => p.ListPrice > 3000); *SQLクエリを生成 WHERE [Extent1].[ListPrice] > cast(3000 as decimal(18)) !ラムダ式を伴うListクラスのメソッド *Listクラスでは、引数に対してラムダ式を指定できるメソッドが多く用意されている。 ,メソッド,内容 ,ForEach,内容を順番に処理 ,ConvertAll,内容を変換 ,Find/FindAll,指定された条件で検索 ,FindIndex/FindLastIndex,条件に合致する要素の位置を検索 ,Exists,条件に合致した要素が存在するかを判定 ,TrueForAll,すべての要素が指定された条件に合致するか ,RemoveAll,指定された条件に合致する要素を削除 !!イベント ::基本 *アクセシビリティ **指定しない場合、privateがデフォルト *event キーワード **続くevent型には、どのようなデリゲート型でも指定可能 *クラスの外部からはイベントを発生させることはできない *外部からできるのは、+=、-= を用いてイベントの結びつけと削除のみ *宣言 public class Eventful { public event Action Announcement; public void Announce(string message) { if (Announcement != null) { Announcement(message); } } } *扱い var souce = new Eventful(); source.Annoucement += m => Console.WriteLine("msg:" + m); ::標準的なイベントデリゲートパターン *最もシンプルで広く利用 **public delegate void EvnetHandler(object sender, EventArgs e); **sender : イベントソース **e : イベント固有の情報を格納 **--特殊化されたものであっても、EventArgs の派生 ::イベントとガベージコレクタ *通常のオブジェクトと変わらない **イベントハンドラを付加したまま放置してしまうとGCに回収されない場合がある **イベントが問題を発生させるような使われ方をするのが問題 **--長い間生き続けるオブジェクトにUIレイヤーがハンドラを付加する場合 **----対応するUI要素が使われなくなったら削除する **----イベントソースがターゲットへの参照をもつ唯一のものであれば、弱参照を利用できる **----WPF では、WeakEventManager を提供 !!イベントvs.デリゲート *新しい非同期機能に対応させてたい **デリゲートを引数にとる *ユーザーインターフェース **たいていイベントを使用する *コールバックがどのように用いられるか **通知に対して複数の配送先 **--イベントは最良の選択肢 **ハンドラを削除する必要がある場合 **--イベントは最良の選択肢 **高度な機能を必要とする場合、IObsevable が最良の選択肢となる **ターゲットメソッドが1つだけであることが明白 **--メソッドまたはコンストラクタへの引数としてデリゲートを渡すのが一般的 !!デリゲート vs.インターフェース *デリゲートはより柔軟 **インターフェースに課されている制約にかかわらず、コードの構築の判断をAPI使用者にゆだねる *インターフェースがより望ましい **複数の関連するコールバックを提供する必要がある !!非同期機能 !async と await ::async *指定すると、このメソッド内で非同期機能を使用する予定であることをコンパイラに通知 *asyncキーワードはawaitキーワードを使用すると宣言するためだけ *メソッドのシグネチャは変わらない *コンパイラにより生成されるコードの挙動が変わる ::await *asyncが指定された場合のみ指定できる