C#を勉強したので、そのまとめ
更新日:2019年6月15日
C#を一通り勉強したので、それをまとめてみました。基本的にCS11を対象にしています。
コメントの書き方
コメントは「//」と「/*~*/」ですが、JavaDocのように、文頭を「///」にして、タグを記載する方法もあります。
必要に応じて記載すればよいかと思います。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
* コメントの記載方法について
*/
/*
コードへのコメントについては、以下のドキュメントを参照し、必要そうなものをピックアップした
https://docs.microsoft.com/ja-jp/dotnet/csharp/codedoc
*/
// javaのように、namespaceと実際のファイルパスを一致させる必要が無い。
namespace study1.case1
{
/*
* Kiso1用の例外クラス
*/
/// <summary>
/// Kiso1で使用する例外クラス
/// </summary>
class MyException : Exception
{
// コンストラクタ
public MyException(string msg) : base(msg)
{
}
}
/*
* Kiso1
* ここにはクラス名など、何か書きたかったら書く。
* クラスの説明は以下のsummaryの中に記載する。
*/
/// <summary>
/// 説明を記載する。
/// ここに記載した内容がVisualStudioでクラスにカーソルを当てると表示される。
/// </summary>
/// <remarks>
/// なにか追加情報的なものがある場合はsummaryとは別にremarksとして記載できる。
/// </remarks>
class Kiso1
{
// コンストラクタ
public Kiso1()
{
Console.WriteLine("study1.case1.Kiso1コンストラクタが呼び出されました。");
}
// ディストラクタ
~Kiso1()
{
Console.WriteLine("study1.case1.Kiso1ディストラクタが呼び出されました。");
}
// 引数で指定した値の和を返す
/// <summary>
/// メソッドに対するコメントはparamを使って記載する
/// VisualStudioではメソッドに対し「///」と入力すると、このコメントを補完してくれる。
/// </summary>
/// <param name="a">足し合わせる値その1</param>
/// <param name="b">足し合わせる値その2</param>
/// <returns>aとbを足し合わせた値を返します</returns>
public int calcAdd(int a, int b)
{
return a + b;
}
// 問答無用で例外を投げます
/// <summary>
/// メソッドを呼ぶと無条件で例外をthrowします
/// </summary>
/// <exception>
/// 例外に関するコメントはexceptionに記載します
/// </exception>
public void nyannyan()
{
// 例外を投げる
throw new MyException("フヒヒ");
}
// この形でコメントを記載すると、VisualStudioでカーソルを当てた時にコメントは表示されない
public void showMessage()
{
Console.WriteLine("なんだあのでっかいモノ♂・・・");
Console.WriteLine("はえーすっごい大きい・・・");
}
}
}
組み込みのデータ型と文字列と列挙型について
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
* 組み込みのデータ型と文字列と列挙型について
*/
namespace study1.case1
{
// 列挙型(列挙型のアクセス修飾子を指定しないとpublic扱いになる)
enum NUM_TYPE
{
AAA, BBB, CCC
}
class Kiso2
{
// 組み込みデータ型は以下のものがある(サンプルは代表例のみ)
// ※フィールドとして定義しているので、なんとなくprivateをつけています
// 整数型
private sbyte a1 = 0; // 1byte(C言語で言うchar (signed charの略だと思う)
private short a2 = 0; // 2byte(C言語で言うsigned short int)
private int a3 = 0; // 4byte(C言語で言うint)
private long a4 = 0L; // 8byte(C言語で言うlong longとか(Cではlongの定義が微妙だったが、C#では言語仕様で定まっている))
private byte b1 = 0; // 1byte(C言語で言うunsinged char)
private ushort b2 = 0; // 2byte(C言語で言うunsinged short int)
private uint b3 = 0U; // 4byte(C言語で言うunsinged int)
private ulong b4 = 0UL; // 8byte(C言語でいうunsinged long longとか)
// 実数型
private float f = 0.0f; // 4byte(C言語でいうfloat)
private double d = 0.0D; // 8byte(C言語でいうdouble)
// 10進データ型
private decimal dec = 0.0M; // 16byte(Javaのようにメモリの許す限りではない。decimal型は末尾にMをつける)
// 文字型
private char c = '\0'; // 2byte(C#では1文字2byteとしてUTF-16で取り扱う)
// 文字列型
private string msg = null; // 文字列を扱う場合の型(Javaで言うString型)
// 論理型
private bool torf = false; // Javaで言うboolean型
// 定数について(constをつけると値の変更ができなくなる)
private const int NUM = 10;
// 文字列の取り扱いについて
public void sample()
{
// 文字列をセットできる
string a = "やっぱ好きなんすねぇ";
// \などの文字はエスケープしてあげる必要がある
string b = "c:\\windows\\";
// 逐次的文字列リテラルを使うとエスケープ不要(@をつける)。perlやシェルにあったヒアドキュメントみたいな感じ
string c = @"c\windows\逐次的文字列リテラル中のダブルコーテーションは""のように2個連ねて記載する
さらには改行まで含められる。";
// cの値をコンソールに出力してみる
Console.WriteLine(c);
// 短い文字列を連結するときは文字列補完を使う(C#6以降)
string name = "醤油瓶";
string sampleMessage1 = $"このやろう{name}"; // "このやろう醤油瓶"がsampleMessage1にセットされる
// sampleMessage1の値をコンソールに出力してみる
Console.WriteLine(sampleMessage1);
// 文字列を何度も連結する場合はStringBuilderを使う
StringBuilder builder = new StringBuilder();
for (int i=0; i<10; i++)
{
builder.Append("うひw");
}
// builderの値をコンソールに出力してみる
Console.WriteLine(builder);
// 列挙型について
// 扱いはC++とほぼ同じです。
NUM_TYPE type = NUM_TYPE.AAA;
switch (type)
{
case NUM_TYPE.AAA:
Console.WriteLine("type:{0}", type);
break;
default:
break;
}
}
}
}
制御構文について(if, switch, for, while, do-while, foreach, goto)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
* 制御構文について(if, switch, for, while, do-while, foreach, goto)
*/
namespace study1.case1
{
class Kiso3
{
// if, switch, for, while, do-while, foreach, goto
public void sample(int val)
{
Console.WriteLine("■if文のサンプル");
if (val == 0)
{
Console.WriteLine("{0}はゼロです。", val);
}
else if (val % 2 == 0) // C言語とは違いif(val%2)ではコンパイルエラーになる。
{
Console.WriteLine("{0}は偶数です。", val);
}
else
{
Console.WriteLine("{0}は奇数です。", val);
}
Console.WriteLine("■switch文のサンプル");
// C#ではbreakを記載するのが必須です。fall throughは文法的に認めていないっぽい。
switch (val)
{
case 1:
Console.WriteLine("valの値は1です。");
break;
case 2:
Console.WriteLine("valの値は2です。");
break;
default:
Console.WriteLine("valの値は1,2以外の値です。");
break;
}
Console.WriteLine("■for文のサンプル");
for (int i=0; i<5; i++)
{
Console.WriteLine("forループ:{0}", i);
}
// C#ではforの中で定義した変数iは、forループのスコープ内のみで有効。以下はコンパイルエラーになる。C/C++とは違う。Javaと同じ。
//Console.WriteLine("i:{0}", i);
Console.WriteLine("■while文のサンプル");
int whileCounter = val;
while (whileCounter-- > 0)
{
Console.WriteLine("whileのループ:{0}", whileCounter);
}
Console.WriteLine("■do-while文のサンプル");
int doWhileCounter = val;
do
{
Console.WriteLine("do-whileのループ:{0}", doWhileCounter);
} while (doWhileCounter-- > 0);
Console.WriteLine("■foreach文のサンプル");
int[] numArray = new int[] { 0, 1, 2, 3, 4, 5 };
foreach(int num in numArray)
{
Console.WriteLine("foreachのループ:{0}", num);
}
Console.WriteLine("■goto文のサンプル");
for(int a=0; a<=10; a++)
{
Console.WriteLine("goto文のループ1個目:{0}", a);
for (int b=0; b<=10;b++)
{
Console.WriteLine("goto文のループ2個目:{0}", b);
goto aiueo;
}
}
aiueo: // gotoのとび先のラベルを定義する
Console.WriteLine("「aiueo」に移動しました");
}
}
}
配列について
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
* 配列について
*/
namespace study1.case2
{
class Data1
{
// 配列について
public void sample()
{
Console.WriteLine("■配列について");
// 配列にメモリを割り当てる(int[]と記載する必要がある。int numArray[]ではコンパイルエラー)
int[] numArray = new int[10];
// 添え字アクセスなど、ほかの言語と使い勝手は同じ
numArray[0] = 100;
numArray[1] = 200;
Console.WriteLine("numArray length : {0}", numArray.Length);
foreach (int val in numArray)
{
Console.WriteLine("val:{0}", val);
}
// 以下のように、配列の宣言と同時にメモリ確保と初期値設定を1行で済ますことができる。
// どちらも同じ動作をする。好きな方で書けばOK。
int[] numArray2 = new int[] { 1, 2, 3 };
int[] numArray3 = { 1, 2, 3 };
// 型推論(C# バージョン3以上)を使う場合は以下のように書くことができる
var numArray4 = new [] { 1, 2, 3 };
Console.WriteLine("■多次元配列について");
// 以下で5x5の2次元多配列を作成できる
int[,] numArray5 = new int[5, 5];
// 以下で5x5x10の3次元多配列を作成できる
int[,,] numArray6 = new int[5, 5, 10];
// 多次元配列に初期値セットは以下のように記載する
int[,,] numArray7 =
{
{
{ 1, 2, 3 },
{ 4, 5, 6 }
},
{
{ 2, 3, 4 },
{ 5, 6, 7 }
}
};
Console.WriteLine("■ジャグ配列について");
// 多次元配列の1行分のサイズは全部同じでしたが、1行のサイズが異なる多次元配列を「ジャグ配列」というらしい。
// ジャグ配列は最初の添え字のサイズだけ決めます。
int[][,] numArray8 = new int[3][,];
// 以下のように、要素の数がばらばらの配列が作成できます。
numArray8[0] = new int[,] { { 1, 2 }, { 10, 20 } };
numArray8[1] = new int[,] { { 1 }, { 10 } };
numArray8[2] = new int[,] { { 1, 2, 3 }, { 10, 20, 30 } };
// foreachのループで回してみる。{ { 1, 2 }, { 10, 20 } }と分けて書いても
// 2つの配列が連結された状態になっている。有益に使う方法は、よくわからない。
foreach (int[,] num1 in numArray8)
{
Console.WriteLine("len:{0}", num1.Length);
foreach(int num2 in num1)
{
Console.WriteLine(" num2:{0}", num2);
}
}
}
}
}
構造体について
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
* 構造体について
*/
namespace study1.case2
{
/*
* 構造体とクラスの違いは
* ・クラスは参照で扱われるのに対し、構造体は値渡しで扱われる。
* ・インターフェイスの実装はできるが、継承が行えない。
* ・フィールドの定義と初期値のセットを同時に行えない。
* ・構造体のコンストラクタには引数なしのコンストラクタは記載できない。
*
* 構造体は、クラスと同じように「フィールド」「メソッド」「プロパティ」「コンストラクタ」を定義できる。
*/
// 構造体の定義
struct DataStruct
{
public string name;
public int num;
// 以下のような、引数なしのコンストラクタはコンパイルエラーになる
//public DataStruct() { }
public DataStruct(string name, int num)
{
// コンストラクタではフィールドすべてに初期値をセットしないとコンパイルエラーになる
this.name = name;
this.num = num;
}
};
class Data2
{
public void sample()
{
// 構造体を使う場合は、インスタンスの生成を行う必要がある。
DataStruct data1 = new DataStruct(); // デフォルトコンストラクタで初期化
DataStruct data2 = new DataStruct("うひw", 10); // 自分で定義したコンストラクタで初期化
Console.WriteLine("data1.name : {0}", data1.name); // デフォルトコンストラクタでは既定値が初期値としてセットされる
Console.WriteLine("data1.num : {0}", data1.num); // デフォルトコンストラクタでは既定値が初期値としてセットされる
Console.WriteLine("data1.name : {0}", data2.name);
Console.WriteLine("data1.num : {0}", data2.num);
}
}
}
匿名型(C#バージョン3から使用可能)とタプル(C#バージョン7から使用可能)について
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
* 匿名型(C#バージョン3から使用可能)とタプル(C#バージョン7から使用可能)について
*/
namespace study1.case2
{
class Data3
{
public void sample()
{
// var型は、C#における型推論用のキーワードです。
// var型で定義すると型推論になり、匿名型やタプルでよく使います
Console.WriteLine("■匿名型について(C#バージョン3から使用可能)");
// 匿名型(値の変更ができない連想配列みたいなもの)。主にLINQで使用するのが目的らしい。
var a = new { age = 24, job = "学生" };
Console.WriteLine("{0}歳、{1}です。", a.age, a.job);
Console.WriteLine("■タプルについて(C#バージョン7から可能)");
// いろんな型を1つにまとめる入れ物みたいなもの。
// 構造体やクラスをわざわざ定義するまでもないようなケース(メソッドの戻り値に複数の値を返したいなど)で使用する。
// タプルは以下のように記載する(2通り記載の方法がある)
// 値だけを記載するパターン
var taple1 = (143000, "年齢", 24.0f);
// 値と名前をセットで記載するパターン
var taple2 = (rent:143000, ageLabel:"年齢", ageFloat:24.0f);
Console.WriteLine("値だけをセットしたタプルへのアクセス:");
Console.WriteLine("家賃:{0}, {1}:{2}", taple1.Item1, taple1.Item2, taple1.Item3); // itemXが自動で追加されて、アクセスできるようになる
Console.WriteLine("ToStringで文字列にすることもできる(debugの時に使う系):{0}", taple1.ToString());
Console.WriteLine("値と名前をセットしたタプルへのアクセス:");
Console.WriteLine("家賃:{0}, {1}:{2}", taple2.rent, taple2.ageLabel, taple2.ageFloat);
Console.WriteLine("ToStringで文字列にすることもできる(debugの時に使う系):{0}", taple2.ToString());
// タプルを使うことで、メソッドから複数の値を返す処理を簡単に記載できる
// (タプルを使わない場合、クラスか構造体を定義し、それを戻り値としないといけない)
var res = getDrinkType("遠野");
Console.WriteLine("飲み物の種類:{0} / サーッ!(迫真):{1}", res.drinkName, res.traditionalCulture);
// 戻り値の受け方として、以下のパターンも可能
var (drinkName, traditionalCalture) = getDrinkType("軍畑先輩");
// 戻り値のうち、不要な値がある場合は「_」を記載する
var (_, aa) = getDrinkType("軍畑先輩");
}
// タプルを使い、複数の値を返すメソッド
private (string drinkName, bool traditionalCulture) getDrinkType(string targetName)
{
if ("遠野".Equals(targetName))
{
return ("アイスティー", true);
} else
{
return ("アク〇リアス", false);
}
}
}
}
nullについて
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
* nullについて
*/
namespace study1.case2
{
class Data4
{
public void sample()
{
Console.WriteLine("■nullについて");
// nullはオブジェクトにインスタンスを振られていない状態などを表す
string msg = null;
if (msg == null)
{
Console.WriteLine("msgはnullです");
}
// null許容型(C# 2.0で追加)
// 以下の書き方をすれば、値が降られていないことを表現するため、int型にもnullがセットできる
int? num = null; // 「?」を付けるとnullをセットすることを許容する。そのため、コンパイルエラーとならない
Console.WriteLine("num : {0}", num);
// ※上記に説明は間違いがある。
// このコードはint型にnullをセットしているわけではない。
// 「?」をつけてnull許容型にすると、System.Nullable構造体となる。なので正確には純粋なint型ではなくなる。
// 結果、以下のようにプロパティにアクセスできるようになる
if (num.HasValue)
{
Console.WriteLine("numの値はセットされています : {0}", num.Value);
}
else
{
Console.WriteLine("numの値はセットされていません(nullです)");
}
// 値へのアクセスはnumとnum.Value、どちらでも同じ
// しかし、nullの状態でnum.Valueをアクセスすると、例外(System.InvalidOperationException)が飛ぶ
Console.WriteLine("num.Value : {0}", num); // こちらは例外は飛ばない
try
{
Console.WriteLine("num.Value : {0}", num.Value); // こちらは例外が飛ぶ
}
catch (System.InvalidOperationException e)
{
Console.WriteLine("nullの状態のnum.Valueにアクセスしたので、例外が飛びました。");
}
Console.WriteLine("■null条件演算子について(C#6で追加された)");
string msgA = null;
// 以下のコードはmsgAがnullなのでNullReferenceExceptionがthrowされる
//int msgALen = msgA.Length;
int? msgALen = msgA?.Length; // msgAがnullならば、nullとなり、msgAがnull以外ならばmsgA.Lengthの値となる
Console.WriteLine("msgALen : {0}", msgALen);
// C#には「??」という演算子があり、「null合体演算子」というらしい
string msgB = msgA ?? "msgAはnull"; // msgAがnullならば"msgAはnull"となり、msgがnullではないならmsgA自身がmsgBにセットされる
Console.WriteLine("msgBの値 : {0}", msgB);
}
}
}
例外について
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
* 例外について
*/
namespace study1.case3
{
// 自作の例外クラス その1
class MyException1 : Exception
{
// 継承するクラスはExceptionでOK
// 昔はApplicationExceptionを継承することが推奨されていたらしいが、今では、Exception推奨になっているらしい
// コンストラクタの例として、以下のパターンを示す(使うものだけ定義すればOK)
// コンストラクタ(デフォルトコンストラクタ)
public MyException1() : base()
{
}
// コンストラクタ(メッセージ有り)
public MyException1(string msg) : base(msg)
{
}
// コンストラクタ(メッセージと例外有り)
public MyException1(string msg, Exception e) : base(msg, e)
{
}
}
// 自作の例外クラス その2
class MyException2 : MyException1
{
private int num;
public MyException2(string msg ,int num) : base(msg)
{
this.num = num;
}
public int getNum() { return num; }
}
class Exception1
{
public void sample1()
{
Console.WriteLine("■例外処理について");
// 例外をthrowするとcatch文の書いた順序どおり、上から合致する例外クラスを探し、そこの処理が実行される
// finallyは例外が発生してもしなくても、try句から処理を抜けるときに実行される。
// 例外の基本的な動作は、Javaとほぼ一緒。
try
{
throw new MyException1("これもうわかんねぇな");
}
catch (MyException2 e)
{
Console.WriteLine("MyException2がcatchしました");
//throw; // 例外を再スローする場合の記載方法。throw e;と同じ
}
catch (MyException1 e)
{
Console.WriteLine("MyException1がcatchしました");
//throw; // 例外を再スローする場合の記載方法。throw e;と同じ
}
catch (Exception e)
{
Console.WriteLine("Exceptionがcatchしました");
//throw; // 例外を再スローする場合の記載方法。throw e;と同じ
}
finally
{
Console.WriteLine("finally句を実行します");
}
// C#6から例外フィルターが追加された。以下のようにcatch句で条件判定ができる
try
{
throw new MyException2("お湯ください", 10);
}
catch (MyException2 e) when (e.getNum() > 10)
{
Console.WriteLine("catch (MyException2 e) when (e.getNum() > 10)がcatchしました");
}
catch (MyException2 e)
{
Console.WriteLine("catch (MyException2 e)がcatchしました");
}
}
public void sample2()
{
Console.WriteLine("■chekcedとcheckについて");
try
{
// chekcedとcheckについて
// 整数の演算で、オーバーフローが発生したことを検知することができる。
// checkedで囲むとオーバーフローが発生すると、例外が発生する
// uncheckedで囲むと例外が発生しない
// この機能は、整数型のみ。floatやdoubleでのオーバーフロー、アンダーフローの検知用の機能は無い。
// 通常、常にunckeckedの状態(オーバーフローが発生しても例外は飛ばない)
// なぜcheckedとcheckがあるかというと、以下のようなことをやるため
short num1 = short.MaxValue;
short num2 = short.MaxValue;
checked {
unchecked
{
num1++;
}
num2++;
}
// 上記のようにchekcedの範囲内で、unckeckedにしたいものがある場合・・・というこの考慮のためらしい。
}
catch (System.OverflowException e)
{
Console.WriteLine("例外が発生しました:{0}", e.ToString());
}
}
}
}
クラスについて(名前付きパラメータ、省略可能なパラメータ、可変パラメータ、ref, out)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
* クラスについて(名前付きパラメータ、省略可能なパラメータ、可変パラメータ、ref, out)
*/
namespace study1.case4
{
class Sample1
{
// staticフィールド
public static int AA = 10;
// フィールド
private int aa = 0;
// コンストラクタ
public Sample1()
{
Console.WriteLine("Sample1コンストラクタが呼ばれました");
}
// ディストラクタ
~Sample1()
{
// ディストラクタはインスタンスのメモリを破棄するときに自動で呼ばれる。
// 呼ばれるタイミングはプログラムで制御できない。
Console.WriteLine("Sample1のディストラクタが呼ばれました");
}
// staticメソッド
public static string getMessage()
{
return "仕方ないね♂";
}
// メソッド
public string getGreetingMessage()
{
return "至急、メールくれや。";
}
// メソッドの名前付きパラメータ(C#4で追加。メソッドを定義する側ではなく、使う側に違いがある)
public string method1(int a, int b)
{
return string.Format("a+bの値:{0}", a + b);
}
// メソッドの省略可能なパラメータ(C++の省略可能パラメータと同じ)
public string method2(int a, int b = 100)
{
return string.Format("a+bの値:{0}", a + b);
}
// メソッドの可変引数
public string method3(params int[] numArray)
{
int result = 0;
foreach(int num in numArray)
{
result += num;
}
return string.Format("numArrayの合計値:{0}", result);
}
// 引数の参照渡し
public void method5(ref int num)
{
num++;
}
// outキーワードについて
public void method6(int num1, int num2, out int a, out int b)
{
a = num1 + num2;
b = num1 * num2;
}
}
class Object1
{
public void sample1()
{
Console.WriteLine("■メソッドの呼び出し方について(名前付きパラメータ、省略可能なパラメータ、可変パラメータ)");
Sample1 sample = new Sample1();
// メソッドを名前付きパラメーターを使って呼び出す(引数の名前を使って、呼び出せる。パラメータは順不同でOK)
Console.WriteLine("method1 : {0}", sample.method1(b: 1000, a: 10));
// メソッドを、省略可能なパラメータを使って呼び出す(引数bにはデフォルトの値である100がセットされ呼び出される。)
Console.WriteLine("method2 : {0}", sample.method2(20));
// 可変パラメータ(例ではパラメータを3つ)のメソッドを呼び出す
Console.WriteLine("method3 : {0}", sample.method3(1, 2, 3));
}
public void sample2()
{
Console.WriteLine("■参照渡しについて");
Sample1 sample = new Sample1();
// メソッドの引数は、プリミティブ型は値渡し、オブジェクト系は参照渡しとなる(Javaと同じ)
// C#7よりrefキーワードが追加され、値渡しだったものを、参照渡しに変更できるようになった。
// 引数の参照渡し
int num = 5;
sample.method5(ref num); // 参照渡しのメソッドの場合、呼び出す側もrefキーワードの記載が必要
Console.WriteLine("numの参照渡しの結果:{0}", num);
}
public void sample3()
{
Console.WriteLine("■outキーワードについて");
Sample1 sample = new Sample1();
// メソッドの引数を、戻り値専用の項目にしてしまおうというのがout
// 以下のようにoutの部分はメソッド側で設定した値が入っている。
// 書き方として、コメントアウトしたものと、その下にあるものは意味が同じ(この記載方法はC#7で導入された)
//int result1, result2;
//sample.method6(10, 20, out result1, out result2);
sample.method6(10, 20, out int result1, out int result2);
Console.WriteLine("result1:{0} / result2:{1}", result1, result2);
}
}
}
クラスについて(ローカル関数、静的コンストラクタ、オブジェクト初期化子、インデクサー、プロパティ)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
* クラスについて(ローカル関数、静的コンストラクタ、オブジェクト初期化子、インデクサー、プロパティ)
*/
namespace study1.case4
{
class Sample2
{
// 静的コンストラクタについて
// 最初にインスタンスを生成する際に、1度だけ呼ばれます。
// Javaでいうクラスイニシャライザです。
// 静的コンストラクタ
static Sample2()
{
Console.WriteLine("Object2の静的コンストラクタが呼び出されました");
}
// ローカル関数について
public void method1(int num)
{
// ローカル関数を定義する(method1の中からしか使えない)
int add(int val)
{
if (val <= 0) return val;
return val + add(--val);
}
Console.WriteLine("method1の結果:{0}", add(num));
}
// オブジェクト初期化子について
// オブジェクト初期化子のサンプル用に外部から見えるフィールドを定義
public int pNum1;
public int pNum2;
private int pNum3;
public void method2()
{
Console.WriteLine("pNum1 : {0}", pNum1);
Console.WriteLine("pNum2 : {0}", pNum2);
Console.WriteLine("pNum3 : {0}", pNum3);
}
// インデクサーについて
// numArrayは自分で用意し、それ以外は、この書き方と同じ風にします(このケースにおいて、valueは特殊な意味を持ちます)。
int[] numArray = new int[5];
public int this[int index]
{
set
{
numArray[index] = value;
}
get
{
return numArray[index];
}
}
// プロパティについて その1
// フィールドと似ていますが、プロパティは以下のようにフィールドに対するエイリアス(ここではPropValueが別名になっている)
// のような動きをします。
int propRealValue = 0;
public int PropValue
{
set { this.propRealValue = value; }
get { return this.propRealValue; }
// 補足:
// PropValueのアクセス修飾子はpublicなので、set/get両方publicとなります。
// 別々のアクセス修飾子を指定することも可能で、たとえば以下のように設定も可能です。
// protected set { this.propRealValue = value; }
}
// プロパティについて その2
// その1で記載した方法でプロパティを使うことができるのですが、冗長なので、以下のように簡単に記載も可能です
// また、初期値をセットすることも可能です(123を初期値としてセットしている)。
public int PropValue2 { get; set; } = 123;
}
class Object2
{
// ローカル関数について
public void sample1()
{
Console.WriteLine("■ローカル関数について");
Sample2 sample2 = new Sample2();
sample2.method1(10);
}
// オブジェクト初期化子について
public void sample2()
{
Console.WriteLine("■オブジェクト初期化子について");
// newをする際、後ろにフィールドの初期値をセットできる。ただし、newする側から見えるスコープの変数のみが可能。
// なので「pNum3」は初期値をオブジェクト初期化子を使って値をセットすることができない。
Sample2 sample2 = new Sample2() { pNum1 = 10, pNum2 = 20 };
sample2.method2();
}
// インデクサーについて
public void sample3()
{
// インデクサーとは、インスタンスが保持している配列に対し、添え字アクセスで値にアクセスできる機能です
// 特定用途でのみ、使えそうな機能ですね。
Console.WriteLine("■インデクサーについて");
Sample2 sample3 = new Sample2();
sample3[0] = 10;
sample3[1] = 11;
sample3[2] = 12;
sample3[3] = 13;
sample3[4] = 100;
Console.WriteLine("sample3[0] : {0}", sample3[0]);
Console.WriteLine("sample3[1] : {0}", sample3[1]);
Console.WriteLine("sample3[2] : {0}", sample3[2]);
Console.WriteLine("sample3[3] : {0}", sample3[3]);
Console.WriteLine("sample3[4] : {0}", sample3[4]);
// また、上記のデータをセットする処理は、インスタンス生成とセットで、以下のように書くこともできます。
Sample2 sample4 = new Sample2()
{
[0] = 10,
[1] = 11,
[2] = 12,
[3] = 13,
[4] = 100
};
Console.WriteLine("sample4[0] : {0}", sample4[0]);
Console.WriteLine("sample4[1] : {0}", sample4[1]);
Console.WriteLine("sample4[2] : {0}", sample4[2]);
Console.WriteLine("sample4[3] : {0}", sample4[3]);
Console.WriteLine("sample4[4] : {0}", sample4[4]);
}
// プロパティについて その1
public void sample4()
{
// プロパティは、クラスのフィールドに対するエイリアスみたいな動きをします
Console.WriteLine("■プロパティについて その1");
Sample2 sample4 = new Sample2();
// 以下のように、プロパティとして定義した名前(PropValue)で、クラス内の値にset/getができます。
// PropValueのアクセス修飾子がpublicなので、どこからでも、以下の処理が行えます。
sample4.PropValue = 100;
int val = sample4.PropValue;
Console.WriteLine("sample4.PropValue : {0}", val);
}
public void sample5()
{
// プロパティの記載を簡略化した例です(クラスのソース参照)
// コンパイル時にプロパティ名の先頭に「__」がついたフィールドが自動作成され、機能を実現している様子です。
Console.WriteLine("■プロパティについて その2");
Sample2 sample5 = new Sample2();
Console.WriteLine("sample4.PropValue2 : {0}", sample5.PropValue2);
sample5.PropValue2 = 1000;
int val = sample5.PropValue2;
Console.WriteLine("sample4.PropValue2 : {0}", val);
}
}
}
クラスについて(コンストラクタから別のコンストラクタの呼び出し、継承、baseキーワード、隠蔽、多態性、アップキャスト・ダウンキャスト(is演算子、as演算子、ボックス化・ボックス化解除)、抽象メソッド、sealedキーワード)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
クラスについて
コンストラクタから別のコンストラクタの呼び出し
継承
baseキーワード
隠蔽
多態性
アップキャスト・ダウンキャスト(is演算子、as演算子、ボックス化・ボックス化解除)
抽象メソッド
sealedキーワード
*/
namespace study1.case4
{
// sealedキーワードについて
// クラスを継承させたくない場合に、以下のようにsealedをつけます。
// SampleXを継承したクラスを作ると、コンパイルエラーになります。
sealed class SampleX
{
// etc..
}
//class SampleXChild : SampleX
//{
//
//}
// メモ:C#では多重継承ができません(インターフェイスは多重継承可能)。Javaと同じですね。
// 親クラス(抽象メソッドを定義する場合はabstractを記載する)
abstract class SampleObjectBase
{
protected string name;
// デフォルトコンストラクタ
public SampleObjectBase()
{
Console.WriteLine("SampleObjectBaseのデフォルトコンストラクタが呼び出されました");
}
// コンストラクタ
public SampleObjectBase(string name)
{
Console.WriteLine("SampleObjectBaseのコンストラクタ(string)が呼び出されました");
this.name = name;
}
// ディストラクタ
~SampleObjectBase()
{
Console.WriteLine("SampleObjectBaseのディストラクタが呼び出されました");
}
public string getName() { return name; }
// オーバーライドされるメソッド(virtualをつける)
public virtual string getGreetingMessage()
{
return "SampleObjectBase(挨拶)";
}
// 抽象メソッド(abstractをつける)
public abstract string getJobName();
}
// 子クラス(継承)
class SampleObjectChildA : SampleObjectBase
{
// フィールド(アクセス修飾子を記載しない場合はprivateとして扱われる)
string msg;
int num;
// デフォルトコンストラクタ
public SampleObjectChildA() : base() // 実行したい親クラスのコンストラクタを指定する(baseキーワード)
{
Console.WriteLine("SampleObjectChildAのデフォルトコンストラクタが呼び出されました");
}
// コンストラクタ
public SampleObjectChildA(string msg) : base(msg) // 親のコンストラクタを指定して実行する
{
Console.WriteLine("SampleObjectChildAのコンストラクタ(string)が呼び出されました");
this.msg = msg;
}
// コンストラクタ(コンストラクタから別のコンストラクタの呼び出し)
public SampleObjectChildA(string msg, int num) : this(msg) // コンストラクタから別のコンストラクタを呼ぶ場合はthisを使う
{
Console.WriteLine("SampleObjectChildAのコンストラクタ(string, int)が呼び出されました");
this.num = num;
}
// ディストラクタ
~SampleObjectChildA()
{
Console.WriteLine("SampleObjectChildAのディストラクタが呼び出されました");
}
// 親クラスのメソッドをオーバーライドする(overrideをつける)
public override string getGreetingMessage()
{
return "SampleObjectChildA(挨拶)";
}
// 親クラスの抽象メソッドを実装する(抽象メソッドの実装も、overrideをつける)
public override string getJobName()
{
return "アイスティーしかなかったけどいいかな?";
}
}
// 子クラス(継承)
class SampleObjectChildB : SampleObjectBase
{
// 隠蔽について
// 親クラスにあって、このクラスから見えるフィールドと同じ名前のフィールドを定義すると、親クラスのフィールドが隠蔽されます
// 隠蔽が発生すると、コンパイラからワーニングが出力されます。
// 隠蔽を意志をもってやっている場合、newをつけます(ワーニングも消えます)
new string name;
// コンストラクタ
public SampleObjectChildB(string name) : base(name)
{
Console.WriteLine("SampleObjectChildBのコンストラクタ(string)が呼び出されました");
this.name = name + "aaaaa";
}
// SampleObjectChildBが持っているnameにアクセスする
public void show1()
{
// 親クラスが持っている「name」が見えなくなり、自分が持っているnameにアクセスする。
Console.WriteLine("SampleObjectChildB name : {0}", name);
}
// 親クラスが持っているnameを使いたい場合はbaseを指定する
public void show2()
{
// 親クラスが持っている「name」にアクセスしたい場合は以下のように記載する。
Console.WriteLine("SampleObjectBase name : {0}", base.name);
}
// 親クラスのメソッドをオーバーライドする(overrideをつける)
public override string getGreetingMessage()
{
return "SampleObjectChildB(挨拶)";
}
// 親クラスの抽象メソッドを実装する(抽象メソッドの実装も、overrideをつける)
public override string getJobName()
{
return "これもうわかんねぇな";
}
}
// 子クラス(継承)
class SampleObjectChildC : SampleObjectBase
{
// メソッドの隠蔽(親クラスのvirtualメソッドに対し、それを隠蔽(newをつける)し、メソッドを定義)
new public string getGreetingMessage()
{
return "SampleObjectChildC(挨拶)";
}
// 親クラスの抽象メソッドを実装する(抽象メソッドの実装も、overrideをつける)
public override string getJobName()
{
return "あら、いらっしゃい。ご無沙汰じゃないですか。";
}
}
class Object3
{
public void sample1()
{
SampleObjectBase sampleObject1 = new SampleObjectChildA("やりますねぇ!", 24);
SampleObjectBase sampleObject2 = new SampleObjectChildB("やりますねぇ!");
// 隠蔽のサンプル
Console.WriteLine("■隠蔽について");
((SampleObjectChildB)sampleObject2).show1();
((SampleObjectChildB)sampleObject2).show2();
// 多態性のサンプル
Console.WriteLine("■多態性について");
Console.WriteLine("sampleObject1.getGreetingMessage() : {0}", sampleObject1.getGreetingMessage());
Console.WriteLine("sampleObject2.getGreetingMessage() : {0}", sampleObject2.getGreetingMessage());
// virtualメソッドを定義したものの、それを隠蔽する実装とした場合の挙動について
// 多態性による実際の型のクラスが呼ばれる・・・という機能が動作しなくなり
// 今の型で、どのメソッドを呼ぶべきかが判断されます。
SampleObjectBase sampleObject3 = new SampleObjectChildC();
Console.WriteLine("sampleObject3.getGreetingMessage() : {0}", sampleObject3.getGreetingMessage());
Console.WriteLine("(SampleObjectChildC)sampleObject3.getGreetingMessage() : {0}", ((SampleObjectChildC)sampleObject3).getGreetingMessage());
// アップキャスト・ダウンキャスト(is演算子、as演算子、ボックス化・ボックス化解除)のサンプル
// is演算子(指定したクラスへダウンキャストできるかをチェックする
Console.WriteLine("■is演算子");
Console.WriteLine("sampleObject1をSampleObjectChildAにダウンキャスト可能か?:{0}", sampleObject1 is SampleObjectChildA);
Console.WriteLine("sampleObject1をSampleObjectChildBにダウンキャスト可能か?:{0}", sampleObject1 is SampleObjectChildB);
Console.WriteLine("sampleObject2をSampleObjectChildAにダウンキャスト可能か?:{0}", sampleObject2 is SampleObjectChildA);
Console.WriteLine("sampleObject2をSampleObjectChildBにダウンキャスト可能か?:{0}", sampleObject2 is SampleObjectChildB);
// as演算子(ダウンキャストできる場合はキャストし、できない場合はnullを返す)
Console.WriteLine("■as演算子");
SampleObjectChildA objA1 = sampleObject1 as SampleObjectChildA; // objA1にはインスタンスがセットされる
SampleObjectChildB objA2 = sampleObject1 as SampleObjectChildB; // objA2にはnullがセットされる
SampleObjectChildA objB1 = sampleObject2 as SampleObjectChildA; // objB1にはnullががセットされる
SampleObjectChildB objB2 = sampleObject2 as SampleObjectChildB; // objB2にはインスタンスがセットされる
// コンソールにメッセージを出力するのはobjA1だけにする(4つ全部やると長くなるので)
if (objA1 != null) Console.WriteLine("objA1にインスタンスがセットされました(ダウンキャスト成功)");
else Console.WriteLine("objA1はnullでした(ダウンキャスト失敗)");
// ボックス化・ボックス化解除
// ボックス化/ボックス化解除は、本サンプルの流れとまったく関係ないのですが、ついでに記載します。
// C#では、何も継承していないクラスを定義しても、自動でSystem.Objectを継承したことになります(Javaと同じ)
// さらには、int型などのプリミティブ型も、object型に変換が可能です(ボックス化)。
// そして、その逆も可能です(ボックス化解除)。
// すべての値は、object型として扱えるはずが、プリミティブ型はダメ。それでは都合が悪いので
// プリミティブ型もobject型として扱えるように、自動変換できる仕組みを欲しいね・・・ということで作られたのだと思います(予測)。
Console.WriteLine("■ボックス化・ボックス化解除");
int numValue = 100;
object obj = numValue; // ボックス化
numValue = 200; // 実行結果を見ると、ここで値を変更しても、objが持っている値に影響が無いことが分かります。
// なので、ボックス化した時点で、object型内部に値をコピーして保持しているっぽいですね。
Console.WriteLine("numValue:{0}", numValue);
Console.WriteLine("obj:{0}", obj);
Console.WriteLine("obj:{0}", obj);
int numValue2 = (int)obj; // ボックス化解除
Console.WriteLine("numValue2:{0}", numValue2);
// 抽象メソッドサンプル
// 抽象メソッドがあるクラスはインスタンス化できません(なので、子クラスには全部getJobNameメソッドを実装しています)
Console.WriteLine("■抽象メソッドについて");
Console.WriteLine("jobName : {0}", sampleObject1.getJobName());
}
}
}
クラスについて(インターフェイス、型スイッチ)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
クラスについて
インターフェイス
型スイッチ
*/
namespace study1.case4
{
// インターフェイスの定義
interface Object4Interface1
{
string getMessage();
int getNum();
}
interface Object4Interface2
{
string getMessage();
}
// C#では、クラスやインターフェイスを継承する際は「:」の右に羅列する(クラス、インターフェイス・・・という順序は守る必要あり)
class Object4SampleA : Object4Interface1, Object4Interface2
{
// 名前が重複したメソッドについて、1つ記載すれば、両方のインターフェイスのメソッドを実装した扱いになる
public string getMessage()
{
return "お菓子だからね";
}
public int getNum()
{
return 114514;
}
}
class Object4SampleB : Object4Interface1, Object4Interface2
{
// Object4Interface1とObject4Interface2で名前が重複しているメソッドを別々に実装したい場合は
// 以下のようにする。publicは記載するとコンパイルエラーになるので不要(interfaceのメソッドなので強制でpublic)
string Object4Interface1.getMessage()
{
return "お湯ください";
}
string Object4Interface2.getMessage()
{
return "モロモロ";
}
public int getNum()
{
return 143000;
}
}
class Object4
{
public void sample1()
{
Console.WriteLine("■インターフェイスについて");
// インターフェイスについて
// インターフェイスにはメソッド、プロパティ、インデクサー、イベントを入れることができます。
// インターフェイスに実装はいれられないので、すべてpublic abstract扱いです。
Object4Interface1 object4SampleA1 = new Object4SampleA();
Object4Interface2 object4SampleA2 = new Object4SampleA();
// 多重継承し、名前が重複しているgetMessageメソッドは、Object4Sampleで1つしか実装していない
// しかし、これで両方のinterfaceのgetMessageが実装された扱いになる。
Console.WriteLine("object4SampleA1.getMessage : {0}", object4SampleA1.getMessage());
Console.WriteLine("object4SampleA1.getNum : {0}", object4SampleA1.getNum());
Console.WriteLine("object4SampleA2.getMessage : {0}", object4SampleA2.getMessage());
// 多重継承し、名前が重複したメソッドを別々に実装したケースのサンプル
Object4Interface1 object4SampleB1 = new Object4SampleB();
Object4Interface2 object4SampleB2 = new Object4SampleB();
Console.WriteLine("object4SampleB1.getMessage : {0}", object4SampleB1.getMessage());
Console.WriteLine("object4SampleB1.getNum : {0}", object4SampleB1.getNum());
Console.WriteLine("object4SampleB2.getMessage : {0}", object4SampleB2.getMessage());
}
public void sample2()
{
Console.WriteLine("■型スイッチについて(C#7にて追加)");
// C#7では、is演算子とswitch文に拡張が加えられている。
// is演算子にて、以下のような記載ができるようになった
Object4Interface1 object4SampleA = new Object4SampleA();
// 以下は、object4SampleAがObject4SampleAにダウンキャスト可能ならば、ダウンキャストしてobjに代入し、if文の評価としてtrueにする
// という動作をする。
if (object4SampleA is Object4SampleA obj)
{
Console.WriteLine("obj.getMessage() : {0}", obj.getMessage());
}
// switch文にて、以下のようにダウンキャスト可能か・・・という条件判定もできるようになった。
switch (object4SampleA)
{
// このcase文では、object4SampleAがObject4SampleAにダウンキャスト可能、かつwhenに続く部分で条件が指定できて
// aiueoのgetNumが1145141919以上だったら、aiueoにキャストし値をセットし、このcase文に入る
case Object4SampleA aiueo when aiueo.getNum() >= 1145141919:
Console.WriteLine("(Object4SampleA aiueo when aiueo.getNum())aiueo : {0}", aiueo.getMessage());
break;
// もちろん、このようにwhenの部分は記載しなくてもOK
case Object4SampleA aiueo:
Console.WriteLine("(case Object4SampleA aiueo)aiueo : {0}", aiueo.getMessage());
break;
}
}
}
}
クラスについて(パーシャル)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
クラスについて
パーシャル
*/
namespace study1.case4
{
// パーシャルについて
// partialを使うと、クラスの定義を複数に分割できたり、メソッドの宣言と実装を分離できたりします。
// コードの自動生成部分と、実装部分を分離するときの便利機能らしい。
// 以下の例では同一ファイル内部でpartialで作っていますが、ファイルが別々になっていてもOKです。
partial class Object5Sample
{
public Object5Sample(int a, int b)
{
num1 = a;
num2 = b;
}
private int num1;
}
partial class Object5Sample
{
private int num2;
}
partial class Object5Sample
{
// partialにできるメソッドは以下の制限がある
// ・partialクラス内にしか記載できない
// ・privateメソッドのみ可能
// ・戻り値はvoidのみ
// ・引数にoutキーワードが使えない
partial void showMessage1();
partial void showMessage2();
public void showMsg()
{
showMessage1();
showMessage2();
}
// 宣言と実装を同じpartialクラスに書くこともできるし、別のpartialクラスに記載することもできる
partial void showMessage1()
{
Console.WriteLine("お湯ください");
}
}
partial class Object5Sample
{
partial void showMessage2()
{
Console.WriteLine("今日もいい天気");
}
}
class Object5
{
public void sample1()
{
Console.WriteLine("■パーシャルについて");
Object5Sample object5Sample = new Object5Sample(10, 20);
object5Sample.showMsg();
}
}
}
デリゲートについて
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace study1.case5
{
// デリゲートについて
// デザインパターンにでてくる、あのデリゲートです。
// 言語仕様レベルで取り込んでるんですねぇ・・・。
// デリゲートの定義
delegate int DelegateMethod(int num);
class Delegate1Sample
{
// マルチキャストの場合の戻り値を識別するための、カウンタ。
private static int VAL = 0;
static int delegate1(int num)
{
Console.WriteLine("delegate1が呼び出されました:{0}", num);
return (++VAL) + num;
}
int delegate2(int num)
{
Console.WriteLine("delegate2が呼び出されました:{0}", num);
return (++VAL) + num;
}
int delegate3(int num)
{
Console.WriteLine("delegate3が呼び出されました:{0}", num);
return (++VAL) + num;
}
// デリゲートの基本
public void method1()
{
Console.WriteLine("■デリゲートについて");
// デリゲートに対し、実装をセットする。staticメソッドでも、メソッドでも両方いける。
// 「new DelegateMethod(delegate1)」というのが基本だが、これを省略して、以下のようにも記載できる(C#2から)。
//DelegateMethod delegateMethod1 = new DelegateMethod(delegate1);
//DelegateMethod delegateMethod2 = new DelegateMethod(delegate2);
DelegateMethod delegateMethod1 = delegate1;
DelegateMethod delegateMethod2 = delegate2;
// デリゲートに登録されているメソッドを呼び出す
int returnValue1 = delegateMethod1(100);
int returnValue2 = delegateMethod2(1000);
Console.WriteLine("returnValue1:{0}", returnValue1);
Console.WriteLine("returnValue2:{0}", returnValue2);
Console.WriteLine("■マルチキャスティングについて(追加)");
// マルチキャスティングについて
// デリゲートに複数のメソッドを追加できます
delegateMethod1 += delegate1; // 同じメソッドを2回呼ぶようにすることもできる
delegateMethod1 += delegate3; // 他のメソッドも追加できる
int returnValue3 = delegateMethod1(100); // 実行結果を見ると、マルチキャストの場合、最後に実行したメソッドの
// 戻り値が、返ってくる様子です。
Console.WriteLine("returnValue3:{0}", returnValue3);
Console.WriteLine("■マルチキャスティングについて(削除)");
// 登録したメソッドを消すこともできる
delegateMethod1 -= delegate1;
int returnValue4 = delegateMethod1(100);
Console.WriteLine("returnValue4:{0}", returnValue4);
}
// デリゲートに、匿名メソッドを設定する(こちらの例は、ラムダ式がある現在では、もう使われません。参考程度のレベルです。)
public void method2()
{
Console.WriteLine("■デリゲートへの匿名メソッドのセットについて");
// 匿名メソッドもセットすることができる
DelegateMethod delegateMethod = delegate (int num)
{
Console.WriteLine("DelegateMethodが呼ばれました:{0}", num);
return num;
};
// 呼び出してみる
delegateMethod(123);
}
// デリゲートにラムダ式を設定する
public void method3()
{
Console.WriteLine("■デリゲートに対し、ラムダ式をセットする");
// デリゲートに対し、ラムダ式をセットする
DelegateMethod delegateMethod =
num =>
{
Console.WriteLine("DelegateMethodが呼ばれました:{0}", num);
return num;
};
// 呼び出してみる
delegateMethod(456);
Console.WriteLine("■おまけ:ラムダ式から参照しているスコープ外の変数について(変数のキャプチャ)");
// おまけ:ラムダ式から参照した値はスコープ外になっても、それは生きているデータとみなされる
// デリゲートを定義する
DelegateMethod delegateMethod1 = (int a) => a + 1; // (int a) => { return a + 1; }と同じ意味
{
// delegateMethod1に別の実装をセットする
int a = 1;
delegateMethod1 = (int num) => num + (a++); // ローカル変数のaと引数を足し合わせている
}
// 上記のスコープをぬけたので、「a」という変数は消えてなくなる・・・はずが、ラムダ式
// が「a」を使っているので、ガベージコレクションの対処外になります。
// なので、「a」は存在し続けることになります(このラムダ式が生きている間は)。
Console.WriteLine("delegateMethod1(123456) : {0}", delegateMethod1(123456));
Console.WriteLine("delegateMethod1(123456) : {0}", delegateMethod1(123456));
Console.WriteLine("delegateMethod1(123456) : {0}", delegateMethod1(123456));
}
}
class Delegate1
{
public void sample1()
{
Delegate1Sample delegate1Sample = new Delegate1Sample();
delegate1Sample.method1();
delegate1Sample.method2();
delegate1Sample.method3();
}
}
}
イベントについて
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
イベントについて
*/
namespace study1.case5
{
// C#で言う「イベント」が何を想定しているのか・・・ですが、正直よくわかりませんでした。
// 少なくともWindows APIのイベントとは無関係です。
// 単なるデリゲートの亜種の様子です。
// 恐らく、GUIアプリを作るときのボタン押下時のハンドラなどを想定しているのではと思いました。
// デリゲートとイベントの違いについて
// デリゲートに対し、イベントはできることが少なくなっています。
// ・eventHandlerをメソッドとして呼び出せるのは、eventHandlerを持っているクラス内部からのみ。
// (Event1Object内部でeventHandler()を実行するのはOKだけど、たとえば、eventHandlerをpublicにして、
// sample1メソッドからevent1Object.eventHandler()と呼び出すのは、コンパイルエラーとなる)
// ・eventHandlerに対し、クラス外から行える操作はメソッドの登録・削除のみ
//
delegate void EventHandler();
class Event1Object
{
// イベントの定義と、何も実行しない処理をセットする
private event EventHandler eventHandler = () => { };
public void addEvent(EventHandler handler)
{
eventHandler += handler;
}
// 登録されているイベントを実行する
public void execute()
{
eventHandler();
}
}
class Event1
{
void f()
{
Console.WriteLine("Event1.fメソッドが呼ばれました");
}
public void sample1()
{
Event1Object event1Object = new Event1Object();
// イベントを追加する(EventHandlerと同じ戻り値・引数ならなんでもOK)
event1Object.addEvent(f);
// ラムダ式でもOK
event1Object.addEvent(
() =>
{
Console.WriteLine("やりますねぇ!");
}
);
// イベントが登録し終わったので、何かしらのイベントが発生したと仮定して、処理を実行してみる
event1Object.execute();
}
}
}
マルチスレッドについて1(Parallel)
マルチスレッド処理を簡単に実行することができるようにしたのが、Parallel・・・だと思います。
スレッド間の制御が不要な場合は、こちらを使うと簡単にマルチスレッド処理が実装できます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
/*
マルチスレッドについて1(Parallel)
*/
namespace study1.case6
{
class MultiThread1ObjectA
{
public static void doWork()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("MultiThread1ObjectA : {0}", i);
Thread.Sleep(500);
}
}
}
class MultiThread1ObjectB
{
public static void doWork()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("MultiThread1ObjectB : {0}", i);
Thread.Sleep(1000);
}
}
}
class MultiThread1ObjectC
{
public static void doWork()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("MultiThread1ObjectC : {0}", i);
Thread.Sleep(1500);
}
}
}
class MultiThread1
{
public void sample1()
{
// スレッドの細かい処理の前に、簡単なParallelクラスについて扱います。
// 以下のように簡単に複数スレッドを立ち上げられます。
Console.WriteLine("■マルチスレッド処理について1(Parallel)");
// 以下のように、実行するメソッドを指定します。
// 可変引数になっているので、実行したいだけ引数に指定します
Parallel.Invoke(new Action(MultiThread1ObjectA.doWork),
new Action(MultiThread1ObjectB.doWork),
new Action(MultiThread1ObjectC.doWork));
// 上記処理は、引数で指定した処理のそれぞれが、すべて終了するまで、処理をブロックします。
// すべての処理が終わらないと、下の「処理が終了しました。」が実行されない。
Console.WriteLine("処理が終了しました。");
}
}
}
マルチスレッドについて2(await/async)
こちらがC#での基本的なマルチスレッドの制御処理です。
マルチスレッド処理には、色々変遷があった様子で・・・。
→ C#4より前は直接Thread処理をいじくるようになっていたらしいですが、C#4からはTaskを使うのが基本になった様子です。
さらには、C#5でTaskの使い方に変更が入り、使い方が変わったらしい。
そして、C#5からは大きな変更はない様子。
そんなこんななので、C#4以下については考慮しないことにします。
サンプルコードはC#5以上を前提とします。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
/*
マルチスレッドについて2(await/async)
*/
namespace study1.case6
{
// C#における、マルチスレッド処理について
// C#4になるまでは、まではThread/ThreadPoolクラスを使っていましたが
// C#4では、Thread/ThreadPoolを使う分かりに、Taskを使うようになりました。
// そして、C#5でさらにTaskの使い方が変わりました。
// なので、C#5を前提とし、Taskの例を記載します。
// C#に用意されている、await/asyncというキーワードを使った実装例を以下に示します。
// スレッドの戻り値を入れるためのクラス
class ExecuteResult
{
string msg;
int num;
public ExecuteResult(string msg, int num)
{
this.msg = msg;
this.num = num;
}
public string getMsg() { return msg; }
public int getNum() { return num; }
}
class MultiThread2Object
{
// awaitがあるメソッドには「async」をつける必要がある
// 戻り値はTaskにする
// 独自の戻り値を返したい場合、Taskのジェネリックを使用する
public async Task<ExecuteResult> work()
{
Console.WriteLine("<>MultiThread2Object.workを実行します・・・");
// awaitは、タスクが終了するまで処理をブロックします(ここで言うとdoWorkが制御を返すまでブロックする)。
await Task.Run(new Action(doWork));
Console.WriteLine("<>MultiThread2Object.workが終了しました・・・");
// ラムダ式で記載も、もちろん可能。
//await Task.Run(() =>
//{
// for (int i = 0; i < 5; i++)
// {
// Console.WriteLine("ループしています。。。{0}", i);
// Thread.Sleep(1000);
// }
//});
ExecuteResult result = new ExecuteResult("うひw", 114514);
return result;
// asyncのケースの特殊なパターンとして、「public async Task work()」というTaskにジェネリック
// を指定していない場合、return文が不要です(C#が勝手に返してくれる)
}
void doWork()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine(" ループしています。。。{0}", i);
Thread.Sleep(1000);
}
}
}
class MultiThread2
{
public void sample1()
{
Console.WriteLine("■マルチスレッド処理について1");
Console.WriteLine("別スレッドを作成し、そのスレッド処理が終了を待つ例");
MultiThread2Object multiThread2Object = new MultiThread2Object();
Console.WriteLine("スレッドを起動します。");
Task task = multiThread2Object.work();
Console.WriteLine("スレッドの処理が終了するまで待ちます。");
task.Wait();
Console.WriteLine("処理が終了しました");
}
}
}
マルチスレッドについて3(現実的(?)なサンプル)
マルチスレッドについて、うだうだ書いてきましたが、実際なにかのプログラムに組み込むとした時を想定し、それっぽい処理を書いてみました。
恐らくあっているはずヽ(^o^)丿
ちなみに、予想だにしないデッドロックを招くとして、Wait処理は使ってはいけない説があります(ググるといっぱい出てくる)。
自分で終わったかなと、チェックするのが基本ですね。
そもそも、プログラムの実装として、永久に待つという実装はしないので、話題にならない話かもしれませんが。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
/*
マルチスレッドについて3(現実的(?)なサンプル)
それっぽい処理を書いてみました。
*/
namespace study1.case6
{
// マルチスレッドでの、排他処理のサンプル
class MultiThread3CommonData
{
// みんながデータを追加するList
List<String> messageList = new List<String>();
// 排他処理に使用するオブジェクト
object lockObject = new object();
public void addMessage(string msg)
{
// lockキーワードを使えばこの{}の中は誰か1人しか実行できない
lock(lockObject)
{
messageList.Add(msg);
}
}
public void showMessage()
{
lock (lockObject)
{
foreach(var msg in messageList)
{
Console.WriteLine("msg : {0}", msg);
}
}
}
}
class MultiThread3WorkObjectStatus
{
// 処理の進捗率(0~100)
public int Progress { get; set; }
// 処理結果
public bool ResultStatus { get; set; }
}
class MultiThread3WorkObject
{
MultiThread3WorkObjectStatus status = new MultiThread3WorkObjectStatus();
int num;
MultiThread3CommonData multiThread3CommonData;
public MultiThread3WorkObject(int num)
{
this.num = num;
}
~MultiThread3WorkObject()
{
Console.WriteLine("MultiThread3WorkObject ディストラクタ{0}", num);
}
public void doWork(object obj)
{
multiThread3CommonData = (MultiThread3CommonData)obj; // 引数で渡ってきた参照を、フィールドに保存してく
Console.WriteLine("MultiThread3WorkObject.doWork {0} を実行します。", num);
status.ResultStatus = false;
for (int i=0; i<10; i++)
{
Console.WriteLine("doWork{0} : {1}", num ,i);
multiThread3CommonData.addMessage($"doWork{num} : {i}"); // 共通のデータの入れ物にデータを足してみる
status.Progress += 10; // ループが10回固定なので毎回10づつ加算して最後は100になるようにする
Thread.Sleep(100*num); // なにかやってるっぽく見せるために適当に寝る
}
Console.WriteLine("MultiThread3WorkObject.doWork {0} を終了します。", num);
status.ResultStatus = true;
}
public MultiThread3WorkObjectStatus getStatus() { return status; }
public int getNum() { return num; }
}
class MultiThread3Object
{
List<MultiThread3WorkObject> threadWorkObjectList = new List<MultiThread3WorkObject>();
public Task method1(MultiThread3CommonData commonData)
{
var taskList = new List<Task>();
for (int i=0; i<5; i++)
{
MultiThread3WorkObject workObject = new MultiThread3WorkObject(i);
threadWorkObjectList.Add(workObject);
Task task = Task.Run(() => workObject.doWork(commonData));
taskList.Add(task);
}
Task resultTask = Task.WhenAll(taskList);
return resultTask;
}
public List<MultiThread3WorkObject> getThreadWorkObjectList() { return threadWorkObjectList; }
}
class MultiThread3
{
//
public void sample1()
{
Console.WriteLine("■マルチスレッドについて3(現実的(?)なサンプル)");
MultiThread3CommonData commonData = new MultiThread3CommonData(); // 各スレッドがデータを書き込む処理を持っているクラス
MultiThread3Object multiThread3Object = new MultiThread3Object();
Task resultTask = multiThread3Object.method1(commonData);
// メインの処理から各タスクの状況を監視する処理
bool loopFlag = true;
while (loopFlag)
{
loopFlag = false;
foreach (var workObject in multiThread3Object.getThreadWorkObjectList())
{
MultiThread3WorkObjectStatus status = workObject.getStatus();
if (status.ResultStatus)
{
Console.WriteLine("{0} : 処理完了={1}%", workObject.getNum(), status.Progress);
} else
{
Console.WriteLine("{0} : 処理中={1}%", workObject.getNum(), status.Progress);
loopFlag = true;
}
}
if (!loopFlag && !resultTask.IsCompleted)
{
// 必要な処理は完了しているがTask側が終了として判定していない状態
// 恐らく、ディストラクタ実行中とか裏方で何かやってるか、もしくは、謎の問題が発生しているケース
// とりあえずループを続けさせているが、しばらく待っても状況が変わらないなら
// リカバリの処理(プログラムを致命的エラーで終了させるなど)が必要。
loopFlag = true;
}
Thread.Sleep(1000);
}
if (resultTask.IsCompleted)
{
Console.WriteLine("すべてのタスクが完了しました!");
} else
{
Console.WriteLine("処理が完了しているけど、Taskオブジェクトとしては完了を認識していない、謎の状況です!");
}
// 共通のデータの入れ物に入っているものを出力する
Console.WriteLine("--------------------------\n共通のデータの入れ物に入っているデータを出力します.....");
commonData.showMessage();
// 上記処理で、自力でタスクの終了をチェックしているので「resultTask.Wait();」は不要。
// ちなみに、Waitメソッドは、不用意に使うとデットロックを引き起こすケースがある(ググると出てくる)
//resultTask.Wait();
Console.WriteLine("処理を終了します");
}
}
}
動的型付け変数について(IronPythonを実行してみる)
外部プロセスとデータのやりとりをするために、dynamic型というものがC#には存在する。
たぶん、COMを主眼に置いているんだと思いますが・・・。
サンプルでは簡単に、.NETFrameworkで動くpython(IronPython)を動かして、pythonのスクリプトを実行して、その実行結果からデータを取得しています。
・・・pythonのソースの例が悪いですが、Pythonのソースコードとしては、単にクラスを定義しているだけです。
pythonスクリプト上、「Alctail」というクラスを定義しており、このクラスは「msg」「num」というフィールドを持っており、コンストラクタの第一引数、第二引数から初期値をもらいます。
それ以外の処理は書いていません。
C#から、「Alctail」クラスのインスタンスを作成し(コンストラクタに値を渡している)、「Alctail」インスタンスが持っている「msg」「num」を取得しているだけの処理です。
「py.Alctail("あいうえお", 123);」と書いていますが、C#のソースには、どこにも「Alctail」なんて存在しません。
だけど、コンパイルエラーになりません。
dynamic型とは、そういう特別なものの様子です。
サンプルを動かすためには、IronPythonの設定追加が必要です(ソースコード内部に手順を記載しました)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// IronPythonを呼び出すためのusing
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
/*
動的型付け変数について
C#4から追加された。
C#の世界の外(ほかのプロセスが持っているデータ構造)と、データのやりとりをするために作られたらしい。
今回の例はPythonとデータ連携します。
VisualStudioのメニューから「プロジェクト」→「NuGetパッケージ管理」を開き
"IronPython"で検索し、インストールしてください。
これだけで下準備完了です。
※「IronPython」は.NETFramework上で動くpythonです。
*/
namespace study1.case7
{
class DynamicObject
{
public void method1()
{
var scriptRuntime = Python.CreateRuntime();
dynamic py = scriptRuntime.UseFile(@"D:\repository\vs\cs\Study1\Study1\pyScript.py");
// pythonで定義されているクラス「Alctail」のインスタンスを作成する
dynamic alctail = py.Alctail("あいうえお", 123);
// 「Alctail」クラスに定義されているメソッドgetMsgとgetNumを呼び出す
string msg = alctail.getMsg();
int num = alctail.getNum();
Console.WriteLine("alctail.msg : {0}", msg);
Console.WriteLine("alctail.num : {0}", num);
}
}
class Dynamic
{
public void sample1()
{
Console.WriteLine("■動的型付け変数について");
DynamicObject dynamicObject = new DynamicObject();
dynamicObject.method1();
}
}
}
※上記のC#のソースから呼び出している「D:\repository\vs\cs\Study1\Study1\pyScript.py」のソース。
# study1.case7.Dynamicのサンプル用のpythonコード
# Acltailクラスを定義
class Alctail:
_msg = ""
_num = 0
# コンストラクタ
def __init__(self, msg, num):
print("[pythonコード]PythonSampleのコンストラクタが呼び出されました")
self._msg = msg
self._num = num
def getMsg(self):
return self._msg
def getNum(self):
return self._num
拡張メソッドについて
既存クラス(自作クラスに対してはもちろん、.NETFrameworkのクラスに対しても)に対し、メソッドを追加することができる不思議な仕様です。
ただ、追加する実装を書いたとしても、メソッド名が重複している場合は、無視されます(エラーにはなりません!)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/*
拡張メソッドについて
C#3から、既存のクラスに対し、メソッドを追加できる仕組みです。
ライブラリのクラスや、自作のクラス、どちらにも追加できます。
拡張メソッドを使用する際の注意点としては、拡張メソッドが存在する名前空間と
それを使う処理の名前空間が別々の場合です(今回のサンプルは同じ名前空間)
その場合、使う側が、拡張メソッドが定義されている名前空間を「using (名前空間)」
してあげる必要があります。
*/
namespace study1.case7
{
// 既存のクラス
class ExpansionMethod1Sample
{
public void showMessage(string msg)
{
Console.WriteLine("msg : {0}", msg);
}
}
// 「ExpansionMethod1Sample」に対し拡張メソッドを追加するクラス(static classである必要がある)
static class ExpansionMethod1Object
{
// 追加するメソッド。public staticでかつ、第一引数に「this 拡張対象のクラス 仮引数」を追加する
public static int expMethod1(this ExpansionMethod1Sample obj, string msg)
{
// objに対するアクセスはpublicなものしか、アクセスできない様子。
// protectedやprivateにアクセス不可らしい。
// クラスのpublicメソッドにはアクセスできる
obj.showMessage(msg);
return msg.Length;
}
}
static class ExpansionMethodString
{
// すでに存在するメソッドに対し、拡張メソッドを追加した場合は、単に拡張メソッドが無視されるだけです。
// エラーにもなりません。
public static string ToLower(this string obj)
{
return "ふひひw";
}
// 拡張メソッドは、既存クラスに対する重複は無視されますが、重複していなければ自由に追加できます。
public static string getAdvertisingMessage(this string obj)
{
return "これもうわかんねぇな。";
}
}
class ExpansionMethod1
{
public void sample1()
{
Console.WriteLine("■拡張メソッドについて");
// 「ExpansionMethod1Object」にて、「ExpansionMethod1Sample」クラスにメソッド「expMethod1」が
// 追加されています。
// 使用する側は「ExpansionMethod1Object」が存在することを気にしなくてもOKです。
ExpansionMethod1Sample expansionMethod1Sample = new ExpansionMethod1Sample();
expansionMethod1Sample.expMethod1("やりますねぇ!");
Console.WriteLine("すでに存在するメソッドを、拡張メソッドとして追加しても、拡張メソッドが無視されるだけです。");
string str1 = "AAAAA";
string str2 = str1.ToLower();
Console.WriteLine("str2 : {0}", str2);
Console.WriteLine("重複していなければ、自由に追加できます。");
Console.WriteLine("string.getAdvertisingMessage : {0}",str1.getAdvertisingMessage());
}
}
}
アノテーションについて
基本的な例を示します。
色々と網羅できておりませんが、雰囲気はわかるかと。
メソッドの一覧を取得し、1個づつ出力していますが、C#が裏で自動で追加しているメソッドも出てきます。
// Conditionalのサンプル用に定義
// ※C言語でよくつかうプリプロセッサはC#でも使えます。
#define DEBUG_MSG_SHOW
#define DEBUG_MSG_SHOW_ALL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reflection;
namespace study1.case7
{
// [Obsolete]を付けると、対象のクラスを非推奨扱いにできます
[Obsolete]
class Sample
{ }
// 自作のアノテーション(C#では属性と呼ぶらしいが、フィールド(属性)と混乱するのでアノテーションと呼ぶことにする。)
// アノテーションの名前の末尾は~Attributeにする。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] // クラスもしくは構造体に対するアノテーションであることを示す
class SampleAttribute : Attribute
{
// アノテーションに、値をセットすることもできる)
private string msg;
private int num;
public string job { get; set; } = "";
public SampleAttribute(string msg, int num, int num2)
{
this.msg = msg;
this.num = num+num2;
}
public string getMsg() { return msg; }
public int getNum() { return num; }
}
// メソッドに対するアノテーション
[AttributeUsage(AttributeTargets.Method)]
class SampleMethodAttribute : Attribute
{
// 例として、プロパティを1個だけ持っているものを作ってみた。
public int val { get; set; }
}
// フィールドに対するアノテーション
[AttributeUsage(AttributeTargets.Field)]
class SampleFieldAttribute : Attribute
{
// 例として、プロパティを1個だけ持っているものを作ってみた。
public int num { get; set; }
}
// アノテーションを使う際、末尾のAttributeの記載は不要。SampleAttributeはSampleになる。
[Sample("あいうえお", 100, 200, job = "学生です。")]
class Sample2
{
// フィールドにアノテーションを付けてみる
[SampleField(num =1000)]
private string message1; // アノテーションあり
private string message2; // アノテーションなし
// メソッドにアノテーションをつけてみる
[SampleMethod(val = 100)]
public void sampleMethod1()
{
}
// アノテーションなしのメソッド
public void sampleMethod2()
{
}
}
class AnnotationObject
{
public void method1()
{
Console.WriteLine("■アノテーションについて");
// もともと用意されている「Conditional」アノテーションの例
showMsg();
// 自作アノテーションの例
// Type type = typeof(Sample2);でインスタンスが無い状態からでもOK
Sample2 sample2 = new Sample2();
Type type = sample2.GetType();
Console.WriteLine("className : {0}", type.Name);
// クラスに付与しているアノテーションを取得する
StringBuilder builder = new StringBuilder();
builder.Append("クラスに付与しているアノテーション情報:\n");
IList<CustomAttributeData> cusutomAttributeDataList = CustomAttributeData.GetCustomAttributes(type);
showPropAndField(cusutomAttributeDataList, builder);
// クラスのフィールドの一覧を表示する(staticフィールドを除くフィールドを取得)
MemberInfo[] memberInfoArray = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach(MemberInfo memberInfo in memberInfoArray)
{
// 付与されているアノテーションを取得する
IList<CustomAttributeData> attrList = memberInfo.GetCustomAttributesData();
// memberInfoArrayには、メソッドなども含めて取得されるので、必要なものを判定する必要がある
if (memberInfo.MemberType == MemberTypes.Field) // フィールドだけを対象にする
{
builder.Append("フィールド情報:\n");
builder.Append(string.Format(" field : {0}", memberInfo.Name)).Append("\n");
showPropAndField(attrList, builder);
}
else if (memberInfo.MemberType == MemberTypes.Method) // メソッドだけを対象にする
{
builder.Append("メソッド情報:\n");
builder.Append(string.Format(" method : {0}", memberInfo.Name)).Append("\n");
showPropAndField(attrList, builder);
}
}
Console.WriteLine(builder.ToString());
}
void showPropAndField(IList<CustomAttributeData> cusutomAttributeDataList, StringBuilder builder)
{
foreach (CustomAttributeData customAttributeData in cusutomAttributeDataList)
{
builder.Append(" annotation Class Name :").Append(customAttributeData.AttributeType.Name).Append("\n");
builder.Append(" annotation Namespace :").Append(customAttributeData.AttributeType.Namespace).Append("\n");
// アノテーションが持っているプロパティを取得(publicフィールドもとれるかも?)
builder.Append(" Properties:\n");
IList<CustomAttributeNamedArgument> fieldList = customAttributeData.NamedArguments;
foreach (CustomAttributeNamedArgument namedArgument in fieldList)
{
string name = namedArgument.MemberName;
object value = namedArgument.TypedValue;
builder.Append(" [").Append(name).Append("] : ").Append(value).Append("\n");
}
// アノテーションのコンストラクタで指定している値を取得
builder.Append(" Constractor argument:\n");
IList<CustomAttributeTypedArgument> consractorFieldList = customAttributeData.ConstructorArguments;
foreach (CustomAttributeTypedArgument constractorAttribute in consractorFieldList)
{
// コンストラクターの引数名と値を紐づける方法は、よくわからなかった。
// 一応、引数の左からの順番通りに、リストに入っているっぽい。
showCustomAttributeTypedArgument(constractorAttribute, builder);
}
}
}
void showCustomAttributeTypedArgument(CustomAttributeTypedArgument argument, StringBuilder builder)
{
// 配列かどうかを判定する
if (argument.Value.GetType() == typeof(ReadOnlyCollection<CustomAttributeTypedArgument>))
{
// このケースは、今回のサンプルでは実行されないので、試していないが、以下の感じで値がとれるはず
builder.Append(" array:");
// 配列の場合
foreach (CustomAttributeTypedArgument arg in (ReadOnlyCollection<CustomAttributeTypedArgument>)argument.Value)
{
builder.Append(string.Format(" {0}:{1}", arg.ArgumentType, arg.Value)).Append("\n");
}
}
else
{
builder.Append(" ").Append(string.Format("{0}:{1}", argument.ArgumentType, argument.Value)).Append("\n");
}
}
// Conditionalを使うと、#defineで定義されていれば実行するし、定義されていない場合は実行しません。
// また、アノテーションを複数指定する場合は「,」で区切るでOKです
[Conditional("DEBUG_MSG_SHOW"), Conditional("DEBUG_MSG_SHOW_ALL")]
void showMsg()
{
Console.WriteLine("これもうわかんねぇな。");
}
}
class Annotation
{
public void sample1()
{
AnnotationObject annotationObject = new AnnotationObject();
annotationObject.method1();
}
}
}
リフレクションについて
インスタンスを作成して、メソッドを実行するサンプルです。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace study1.case7
{
class Sample7
{
public Sample7()
{
Console.WriteLine("コンストラクタが呼び出されました。");
}
public int showMessage(string msg)
{
Console.WriteLine(msg);
return msg.Length;
}
}
class RefrectionObject
{
public void method1()
{
Console.WriteLine("■リフレクションについて");
// Sample7のshowMessageをリフレクションを使って実行します
// Sample7のインスタンスを作成する
Type type = Type.GetType("study1.case7.Sample7");
object instance = System.Activator.CreateInstance(type);
// メソッドを取得する
MethodInfo methodInfo = type.GetMethod("showMessage");
// メソッドを実行する
object[] args = {
"これもうわかんねぇな。"
};
methodInfo.Invoke(instance, args);
}
}
class Refrection
{
public void sample1()
{
RefrectionObject refrectionObject = new RefrectionObject();
refrectionObject.method1();
}
}
}
ライブラリ関数(List, Dictionary, SortedList, SortedDictionary, yield, 文字列操作、正規表現)
基本的なライブラリが提供しているクラスの使用例です。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
/*
* ライブラリ関数(List, Dictionary, SortedList, SortedDictionary, yield, 文字列操作、正規表現)
*/
namespace study1.case8
{
// SortedListとSortedDictionaryのサンプルで
// データの比較に使用するクラス(IComparerの<>の中でリストの中のkey値のデータ型を指定しています)
class SotredCompair : IComparer<string>
{
// val1とval2を比較して
// val1が大きい場合は1以上の値を返す
// val1とval2が同じ場合は0を返す
// val1が小さい場合は-1以下の値を返す
// ・・・という仕様で実装します
public int Compare(string val1, string val2)
{
// 独自の比較実装を行う。今回の例では文字列の長さで上・下を決める
int len1 = val1?.Length ?? 0; // val1がnullではないならばval1.Length。nullならば0
int len2 = val2?.Length ?? 0; // val2がnullではないならばval2.Length。nullならば0
Console.WriteLine("☆SotredDictionaryCompair val1:{0}({1}) / val2:{2}({3})", val1, len1, val2, len2);
// 最初に一致した場合のケースを記載します(そうしないとうまく動かないケースがあります)
if (val1 == val2)
{
return 0;
}
else if (len1 > len2)
{
return 1;
}
else
{
return -1;
}
}
}
class Lib1Object
{
// 基本的なコレクション
public void method1()
{
Console.WriteLine("■基本的なコレクション");
Console.WriteLine("・リストについて");
// List(内部はArrayListの実装となっている)
var list = new List<string>();
list.Add("うひw1");
list.Add("うひw1");
list.Add("うひw1");
foreach(string msg in list) {
Console.WriteLine("list:{0}", msg);
}
// 事前にセットする値が決まっているなら、以下のような書き方もできる(C#3から)
var list2 = new List<string>()
{
"うひw1",
"うひw2",
"うひw3",
};
foreach (string msg in list2)
{
Console.WriteLine("list2:{0}", msg);
}
// これ以外に「LinkedList」「SortedList」「Stack」「Queue」がある。考え方は上記のListと同じなのでサンプルコードは省略
Console.WriteLine("・マップについて");
// マップのサンプル(別途Hashtableというクラスがありますが、こちらは古い(ジェネリックが導入される前からあったもの)
// ので、基本的にDictionaryだけ使うことになると思います。
var map = new Dictionary<string, string>();
// 要素の追加は[]演算子を使う。AddメソッドでもOKですが、キー値がすでに存在している場合は例外を投げます。
map["その1"] = "これもうわかんねぇな。";
map["その2"] = "やりますねぇ!";
// キーの一覧をforeachで回すのは、以下のようにもできますが、C#では「KeyBaluePair」というKeyとValueを受けるクラスがあります
//foreach (string key in map.Keys)
//{
// // 値を取得するのも、[]演算子を使う
// Console.WriteLine("{0} : {1}", key, map[key]);
//}
foreach(KeyValuePair<string, string> keyValue in map)
{
Console.WriteLine("{0} : {1}", keyValue.Key, keyValue.Value);
}
}
// SortedList(二分探索)について
// SotedListとSotedDictionaryがありますが、基本的にSotedDictionaryを使うケースが多いはずです。
// SotedListをあえて使う場合は
// ・メモリ使用量を少しでも切り詰めたい(気持ちレベル)
// ・すべての要素を取得する処理を頻繁に行う(SortedDictionaryは探索木をたどる分、処理が重い)
// ぐらい。特定用途でしか使用しない感じかと。
public void method2()
{
Console.WriteLine("■SortedList(二分探索)について");
// ソート済みリスト(ソート条件を判定するクラスを引数に渡している)
var sortedList = new SortedList<string, string>(new SotredCompair()) {
{ "AKYS", "エンジン全開" },
{ "YJ", "じゃけん夜行きましょうね" },
{ "MUR", "あっそうだ" },
{ "KMR", "なんで見る必要なんかあるんですか" },
{ "TOHNO", "あー、いいっすねー" },
};
Console.WriteLine("要素を追加してみる・・・");
sortedList["AAA"] = "ヽ(^o^)丿 追加した行 -------------------";
foreach (KeyValuePair<string, string> keyValue in sortedList)
{
Console.WriteLine("{0} : {1}", keyValue.Key, keyValue.Value);
}
// キー値を指定して、値を取得する(SotredListCompairがWriteLineで出力しているメッセージを見ると、二分探索しているのが分かります)
Console.WriteLine("要素を取得してみる・・・");
Console.WriteLine("sortedList[\"AAA\"] : {0}", sortedList["AAA"]);
}
// SortedDictionaryについて
public void method3()
{
Console.WriteLine("■SortedDictionaryについて");
// ソート済みDictionary(ソート条件を判定するクラスを引数に渡している)
var sortedDictionary = new SortedList<string, string>(new SotredCompair()) {
{ "AKYS", "エンジン全開" },
{ "YJ", "じゃけん夜行きましょうね" },
{ "MUR", "あっそうだ" },
{ "KMR", "なんで見る必要なんかあるんですか" },
{ "TOHNO", "あー、いいっすねー" },
};
Console.WriteLine("要素を追加取得してみる・・・");
sortedDictionary["AAA"] = " ^^) _旦~~ 追加した行 -------------------";
foreach (KeyValuePair<string, string> keyValue in sortedDictionary)
{
Console.WriteLine("{0} : {1}", keyValue.Key, keyValue.Value);
}
Console.WriteLine("要素を取得してみる・・・");
Console.WriteLine("sortedDictionary[\"AAA\"] : {0}", sortedDictionary["AAA"]);
}
// yield returnとyield breakについて
public void method4()
{
Console.WriteLine("■yieldについて");
// 関数内関数として記載していますが、どこに書いてもOKです。
// 戻り値の型が「IEnumerable<int>」であればyield returnとyield breakが使えます
// yield return iの部分が、すぐさま呼び出し元に制御を返すのではなく、return iしたものを貯めておいて
// 処理が終わったら、その貯めておいたもの(iの値のIEnumerable)を返すように、制御できます。
// また、yield returnがあるとき、forループの処理がすべて処理が終わらないと、returnできません。
// 途中でやめたい場合(もしくは以下の例のように処理できない場合)、yield breakを実行することで
// 呼び出し元に制御を返すことができます。
IEnumerable<int> createList(int count)
{
if (count <= 0)
{
yield break;
}
for (int i=0; i<count; i++)
{
yield return i;
}
}
IEnumerable<int> intEnumerable1 = createList(100);
int[] intList1 = intEnumerable1.ToArray();
Console.WriteLine("intList1のサイズ : {0}", intList1.Length);
// yield breakした場合でも、戻り値がnullではなく、要素ゼロ個のオブジェクトが返ってきます
IEnumerable<int> intEnumerable2 = createList(-100);
int[] intList2 = intEnumerable2.ToArray();
Console.WriteLine("intList2のサイズ : {0}", intList2.Length);
}
// 文字列操作について
public void method5()
{
// 文字列操作でよく使うものをピックアップして例を示す
Console.WriteLine("■文字列操作の代表例");
// 文字列を作成する(簡単な例)
string msg1 = string.Format("{0} : {1}", "foo↑", "ビール!ビール!");
Console.WriteLine("string.Formatの例その1 : {0}", msg1);
// 文字列を作成する(書式指定も可能。色々なことができるので、詳しくはググる感じで。)
string msg2 = string.Format("{0:D4}年 {1:D2}月 {2:D2}日 十六進もこんな感じで出力できる:{3:X8} 3桁区切りも!:{4:N0}",
2019, 6, 15, 123456, 1145141919);
Console.WriteLine("string.Formatの例その2 : {0}", msg2);
// nameof演算子を使うと、変数の名前を取得できる(デバッグメッセージなどで、極めて有用。)
Console.WriteLine("nameof(msg1) : {0}", nameof(msg1));
// 文字列をsplitする
string msg3 = "10, 20, 30, 40, 50";
string[] splitResult = msg3.Split(',');
foreach (string result in splitResult)
{
Console.WriteLine("Splitした結果(trimして出力):[{0}]", result.Trim());
}
}
// 正規表現について
public void method6()
{
// ソースにusing System.Text.RegularExpressions;を追記する必要あり
Console.WriteLine("■正規表現の使い方");
// 指定した正規表現に一致するかのチェックの例
var reg = new Regex("^[0-9a-cA-C]{3}$");
Match matcher = reg.Match("ab1");
if (matcher.Success)
{
Console.WriteLine("正規表現に一致しました!");
}
else
{
Console.WriteLine("正規表現に一致しませんでした!");
}
}
}
class Lib1
{
public void sample1()
{
Lib1Object lib1Object = new Lib1Object();
lib1Object.method1();
lib1Object.method2();
lib1Object.method3();
lib1Object.method4();
lib1Object.method5();
lib1Object.method6();
}
}
}
ライブラリ関数(ファイル操作とIDisposable、LINQ)
IDisposableの話は、話をだいぶ省略していますが、Pythonとかに出てくるやつと同じですね。
自作クラスにも実装して、リソースの自動開放を自動化させることもできます。
LINQ(「リンク」と呼ぶらしい)について、軽く触れる程度にしています。
使用する予定がないので。。。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace study1.case8
{
class Lib2Object
{
// 適当なファイルがなかったので、たまたまこれにしています。今回の例はpythonとは関係ありません。テキストファイルなら何でもOK。
private static string FILE_PATH = @"D:\repository\vs\cs\Study1\Study1\pyScript.py";
// ファイル操作(Disposable)について
public void method1()
{
Console.WriteLine("■テキストファイルの読み込みとIDisposableについて");
// 過去のサンプルで作った「pyScrpit.py」を読み込ませてみる
StringBuilder builder = new StringBuilder();
// ここでのusingは、IDisposableを実装しているクラスは、自動でリソース開放をするように処理してくれます(StreamReaderはIDisposableを継承している)
using (StreamReader streamReader = new StreamReader(FILE_PATH, System.Text.Encoding.UTF8))
{
bool eofFlag = false;
do
{
// 1行読み込む
string line = streamReader.ReadLine();
if (line != null)
{
builder.Append(line).Append("\n");
}
else
{
eofFlag = true;
}
} while (!eofFlag);
}
// 読み込んだテキストを出力
Console.WriteLine("読み込み結果:\n{0}", builder.ToString());
}
// LINQについて
public void method2()
{
Console.WriteLine("■LINQについて");
// LINQは、DBやXML、コレクションのデータなどの情報源の形に依存せず、共通の手順で、データを取得したい・・・
// というものの仕様らしい。
// いろんな機能があるらしいが、よくわからないので、簡単な例のみを記載します。
// SQLライクな命令文(共通の手順)で、いろんなデータをとってくる処理です。
// 実現には、LINQプロバイダーという、DBドライバーみたいなのが必要です。
// これをせこせこ作ることで、DBやXMLなどからも、共通の手順でデータ取得が可能です。
// 以下は、配列の情報から、50以上の値を、降順にソートして取得しています
int[] scoreArray =
{
10, 20, 30, 40, 50, 60, 70, 80, 90, 100
};
int limitScore = 50;
var resultArray = from val in scoreArray
where val >= limitScore
orderby val descending
select val;
// LINQの特徴として、実際にデータアクセスしようとしたときに、内部でデータ取得処理が実行されます。
// なので、resultArrayは、foreachの処理に入ったときに、データ取得処理を実行します。
// (恐らく、resultArrayに関数ポインタをセットしているイメージ。実行はそれをcallしたときに行われる・・・感じ?)
foreach (var val in resultArray)
{
Console.WriteLine("val : {0}", val);
}
}
}
class Lib2
{
public void sample1()
{
Lib2Object lib2Object = new Lib2Object();
//lib2Object.method1();
lib2Object.method2();
}
}
}
参考資料:サンプルの実行結果
各サンプルソースの実行結果を、一気に実行した際の、出力結果を置いておきます。
雑すぎて、申し訳ございません。
実行コード:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using study1.case1;
using study1.case2;
using study1.case3;
using study1.case4;
using study1.case5;
using study1.case6;
using study1.case7;
using study1.case8;
namespace study1
{
class CSMain
{
// アプリのエントリーポイント
static void Main(string[] args)
{
kiso1();
kiso2();
kiso3();
data1();
data2();
data3();
data4();
exception1();
object1();
object2();
object3();
object4();
object5();
delegate1();
event1();
multiThread1();
multiThread2();
multiThread3();
expansionMethod1();
dynamic1();
annotation1();
refrection1();
lib1();
lib2();
}
// コメントの書き方
static void kiso1()
{
Kiso1 kiso1 = new Kiso1();
kiso1.showMessage();
try
{
kiso1.nyannyan();
}
catch (MyException e)
{
Console.WriteLine("nyannyanからの例外をcatchしました");
}
Console.WriteLine("サンプルです!");
}
// 組み込みのデータ型と文字列と列挙型について
static void kiso2()
{
Kiso2 kiso2 = new Kiso2();
kiso2.sample();
}
// 制御構文について(if, switch, for, while, do-while, foreach, goto)
static void kiso3()
{
Kiso3 kiso3 = new Kiso3();
kiso3.sample(5);
}
// 配列、構造体、タプル、nullについて
// 配列について
static void data1()
{
Data1 data1 = new Data1();
data1.sample();
}
// 構造体について
static void data2()
{
Data2 data2 = new Data2();
data2.sample();
}
// 匿名型(C#バージョン3から使用可能)とタプル(C#バージョン7から使用可能)について
static void data3()
{
Data3 data3 = new Data3();
data3.sample();
}
// nullについて
static void data4()
{
Data4 data4 = new Data4();
data4.sample();
}
// 例外について
static void exception1()
{
Exception1 exception1 = new Exception1();
exception1.sample1();
exception1.sample2();
}
// クラスについて(名前付きパラメータ、省略可能なパラメータ、可変パラメータ、ref, out)
static void object1()
{
Object1 object1 = new Object1();
object1.sample1();
object1.sample2();
object1.sample3();
}
// クラスについて(ローカル関数、静的コンストラクタ、オブジェクト初期化子、インデクサー、プロパティ)
static void object2()
{
Object2 object2 = new Object2();
object2.sample1();
object2.sample2();
object2.sample3();
object2.sample4();
object2.sample5();
}
// クラスについて1
static void object3()
{
/*
以下についてのサンプルコード
コンストラクタから別のコンストラクタの呼び出し
継承
baseキーワード
隠蔽
多態性
アップキャスト・ダウンキャスト(is演算子、as演算子、ボックス化・ボックス化解除)
抽象メソッド
sealedキーワード
*/
Object3 object3 = new Object3();
object3.sample1();
}
// クラスについて2
static void object4()
{
Object4 object4 = new Object4();
object4.sample1();
object4.sample2();
}
// クラスについて3
static void object5()
{
Object5 object5 = new Object5();
object5.sample1();
}
// デリゲートについて
static void delegate1()
{
Delegate1 delegate1 = new Delegate1();
delegate1.sample1();
}
// イベントについて
static void event1()
{
Event1 event1 = new Event1();
event1.sample1();
}
// マルチスレッドについて1(Parallel)
static void multiThread1()
{
MultiThread1 multiTherad1 = new MultiThread1();
multiTherad1.sample1();
}
// マルチスレッドについて2(await/async)
static void multiThread2()
{
MultiThread2 multiTherad2 = new MultiThread2();
multiTherad2.sample1();
}
// マルチスレッドについて3(現実的(?)なサンプル)
static void multiThread3()
{
MultiThread3 multiThread3 = new MultiThread3();
multiThread3.sample1();
}
// 拡張メソッドについて
static void expansionMethod1()
{
ExpansionMethod1 expansionMethod1 = new ExpansionMethod1();
expansionMethod1.sample1();
}
// 動的型付け変数
static void dynamic1()
{
Dynamic dynamic = new Dynamic();
dynamic.sample1();
}
// アノテーションについて
static void annotation1()
{
Annotation annotation = new Annotation();
annotation.sample1();
}
// リフレクションについて
static void refrection1()
{
Refrection refrection = new Refrection();
refrection.sample1();
}
// ライブラリの代表例サンプル1
static void lib1()
{
Lib1 lib1 = new Lib1();
lib1.sample1();
}
// ライブラリの代表例サンプル2
static void lib2()
{
Lib2 lib2 = new Lib2();
lib2.sample1();
}
}
}
実行結果: study1.case1.Kiso1コンストラクタが呼び出されました。 なんだあのでっかいモノ♂・・・ はえーすっごい大きい・・・ nyannyanからの例外をcatchしました サンプルです! c\windows\逐次的文字列リテラル中のダブルコーテーションは"のように2個連ねて記載する さらには改行まで含められる。 このやろう醤油瓶 うひwうひwうひwうひwうひwうひwうひwうひwうひwうひw type:AAA ■if文のサンプル 5は奇数です。 ■switch文のサンプル valの値は1,2以外の値です。 ■for文のサンプル forループ:0 forループ:1 forループ:2 forループ:3 forループ:4 ■while文のサンプル whileのループ:4 whileのループ:3 whileのループ:2 whileのループ:1 whileのループ:0 ■do-while文のサンプル do-whileのループ:5 do-whileのループ:4 do-whileのループ:3 do-whileのループ:2 do-whileのループ:1 do-whileのループ:0 ■foreach文のサンプル foreachのループ:0 foreachのループ:1 foreachのループ:2 foreachのループ:3 foreachのループ:4 foreachのループ:5 ■goto文のサンプル goto文のループ1個目:0 goto文のループ2個目:0 「aiueo」に移動しました ■配列について numArray length : 10 val:100 val:200 val:0 val:0 val:0 val:0 val:0 val:0 val:0 val:0 ■多次元配列について ■ジャグ配列について len:4 num2:1 num2:2 num2:10 num2:20 len:2 num2:1 num2:10 len:6 num2:1 num2:2 num2:3 num2:10 num2:20 num2:30 data1.name : data1.num : 0 data1.name : うひw data1.num : 10 ■匿名型について(C#バージョン3から使用可能) 24歳、学生です。 ■タプルについて(C#バージョン7から可能) 値だけをセットしたタプルへのアクセス: 家賃:143000, 年齢:24 ToStringで文字列にすることもできる(debugの時に使う系):(143000, 年齢, 24) 値と名前をセットしたタプルへのアクセス: 家賃:143000, 年齢:24 ToStringで文字列にすることもできる(debugの時に使う系):(143000, 年齢, 24) 飲み物の種類:アイスティー / サーッ!(迫真):True ■nullについて msgはnullです num : numの値はセットされていません(nullです) num.Value : nullの状態のnum.Valueにアクセスしたので、例外が飛びました。 ■null条件演算子について(C#6で追加された) msgALen : msgBの値 : msgAはnull ■例外処理について MyException1がcatchしました finally句を実行します catch (MyException2 e)がcatchしました ■chekcedとcheckについて 例外が発生しました:System.OverflowException: 算術演算の結果オーバーフローが発生しました。 場所 study1.case3.Exception1.sample2() 場所 D:\repository\vs\cs\Study1\Study1\case3\Exception1.cs:行 113 ■メソッドの呼び出し方について(名前付きパラメータ、省略可能なパラメータ、可変パラメータ) Sample1コンストラクタが呼ばれました method1 : a+bの値:1010 method2 : a+bの値:120 method3 : numArrayの合計値:6 ■参照渡しについて Sample1コンストラクタが呼ばれました numの参照渡しの結果:6 ■outキーワードについて Sample1コンストラクタが呼ばれました result1:30 / result2:200 ■ローカル関数について Object2の静的コンストラクタが呼び出されました method1の結果:55 ■オブジェクト初期化子について pNum1 : 10 pNum2 : 20 pNum3 : 0 ■インデクサーについて sample3[0] : 10 sample3[1] : 11 sample3[2] : 12 sample3[3] : 13 sample3[4] : 100 sample4[0] : 10 sample4[1] : 11 sample4[2] : 12 sample4[3] : 13 sample4[4] : 100 ■プロパティについて その1 sample4.PropValue : 100 ■プロパティについて その2 sample4.PropValue2 : 123 sample4.PropValue2 : 1000 SampleObjectBaseのコンストラクタ(string)が呼び出されました SampleObjectChildAのコンストラクタ(string)が呼び出されました SampleObjectChildAのコンストラクタ(string, int)が呼び出されました SampleObjectBaseのコンストラクタ(string)が呼び出されました SampleObjectChildBのコンストラクタ(string)が呼び出されました ■隠蔽について SampleObjectChildB name : やりますねぇ!aaaaa SampleObjectBase name : やりますねぇ! ■多態性について sampleObject1.getGreetingMessage() : SampleObjectChildA(挨拶) sampleObject2.getGreetingMessage() : SampleObjectChildB(挨拶) SampleObjectBaseのデフォルトコンストラクタが呼び出されました sampleObject3.getGreetingMessage() : SampleObjectBase(挨拶) (SampleObjectChildC)sampleObject3.getGreetingMessage() : SampleObjectChildC(挨拶) ■is演算子 sampleObject1をSampleObjectChildAにダウンキャスト可能か?:True sampleObject1をSampleObjectChildBにダウンキャスト可能か?:False sampleObject2をSampleObjectChildAにダウンキャスト可能か?:False sampleObject2をSampleObjectChildBにダウンキャスト可能か?:True ■as演算子 objA1にインスタンスがセットされました(ダウンキャスト成功) ■ボックス化・ボックス化解除 numValue:200 obj:100 obj:100 numValue2:100 ■抽象メソッドについて jobName : アイスティーしかなかったけどいいかな? ■インターフェイスについて object4SampleA1.getMessage : お菓子だからね object4SampleA1.getNum : 114514 object4SampleA2.getMessage : お菓子だからね object4SampleB1.getMessage : お湯ください object4SampleB1.getNum : 143000 object4SampleB2.getMessage : モロモロ ■型スイッチについて(C#7にて追加) obj.getMessage() : お菓子だからね (case Object4SampleA aiueo)aiueo : お菓子だからね ■パーシャルについて お湯ください 今日もいい天気 ■デリゲートについて delegate1が呼び出されました:100 delegate2が呼び出されました:1000 returnValue1:101 returnValue2:1002 ■マルチキャスティングについて(追加) delegate1が呼び出されました:100 delegate1が呼び出されました:100 delegate3が呼び出されました:100 returnValue3:105 ■マルチキャスティングについて(削除) delegate1が呼び出されました:100 delegate3が呼び出されました:100 returnValue4:107 ■デリゲートへの匿名メソッドのセットについて DelegateMethodが呼ばれました:123 ■デリゲートに対し、ラムダ式をセットする DelegateMethodが呼ばれました:456 ■おまけ:ラムダ式から参照しているスコープ外の変数について(変数のキャプチャ) delegateMethod1(123456) : 123457 delegateMethod1(123456) : 123458 delegateMethod1(123456) : 123459 Event1.fメソッドが呼ばれました やりますねぇ! ■マルチスレッド処理について1(Parallel) MultiThread1ObjectA : 0 MultiThread1ObjectB : 0 MultiThread1ObjectC : 0 MultiThread1ObjectA : 1 MultiThread1ObjectB : 1 MultiThread1ObjectA : 2 MultiThread1ObjectC : 1 MultiThread1ObjectA : 3 MultiThread1ObjectB : 2 MultiThread1ObjectA : 4 MultiThread1ObjectC : 2 MultiThread1ObjectB : 3 MultiThread1ObjectB : 4 MultiThread1ObjectC : 3 MultiThread1ObjectC : 4 処理が終了しました。 ■マルチスレッド処理について1 別スレッドを作成し、そのスレッド処理が終了を待つ例 スレッドを起動します。 <>MultiThread2Object.workを実行します・・・ ループしています。。。0 スレッドの処理が終了するまで待ちます。 ループしています。。。1 ループしています。。。2 ループしています。。。3 ループしています。。。4 <>MultiThread2Object.workが終了しました・・・ 処理が終了しました ■マルチスレッドについて3(現実的(?)なサンプル) MultiThread3WorkObject.doWork 3 を実行します。 MultiThread3WorkObject.doWork 1 を実行します。 doWork3 : 0 MultiThread3WorkObject.doWork 0 を実行します。 MultiThread3WorkObject.doWork 2 を実行します。 0 : 処理中=0% doWork1 : 0 MultiThread3WorkObject.doWork 4 を実行します。 doWork4 : 0 doWork0 : 0 doWork0 : 1 doWork0 : 2 1 : 処理中=0% 2 : 処理中=0% 3 : 処理中=10% 4 : 処理中=10% doWork0 : 3 doWork0 : 4 doWork2 : 0 doWork0 : 5 doWork0 : 6 doWork0 : 7 doWork0 : 8 doWork0 : 9 MultiThread3WorkObject.doWork 0 を終了します。 doWork1 : 1 doWork1 : 2 doWork2 : 1 doWork3 : 1 doWork1 : 3 doWork4 : 1 doWork1 : 4 doWork2 : 2 doWork1 : 5 doWork3 : 2 doWork1 : 6 doWork2 : 3 doWork1 : 7 doWork4 : 2 doWork1 : 8 doWork2 : 4 doWork3 : 3 doWork1 : 9 0 : 処理完了=100% 1 : 処理中=100% 2 : 処理中=50% 3 : 処理中=40% 4 : 処理中=30% MultiThread3WorkObject.doWork 1 を終了します。 doWork2 : 5 doWork3 : 4 doWork4 : 3 doWork2 : 6 doWork2 : 7 doWork3 : 5 doWork4 : 4 doWork2 : 8 doWork3 : 6 doWork2 : 9 doWork4 : 5 0 : 処理完了=100% 1 : 処理完了=100% 2 : 処理中=100% 3 : 処理中=70% 4 : 処理中=60% MultiThread3WorkObject.doWork 2 を終了します。 doWork3 : 7 doWork3 : 8 doWork4 : 6 doWork3 : 9 doWork4 : 7 MultiThread3WorkObject.doWork 3 を終了します。 0 : 処理完了=100% 1 : 処理完了=100% 2 : 処理完了=100% 3 : 処理完了=100% 4 : 処理中=80% doWork4 : 8 doWork4 : 9 MultiThread3WorkObject.doWork 4 を終了します。 0 : 処理完了=100% 1 : 処理完了=100% 2 : 処理完了=100% 3 : 処理完了=100% 4 : 処理完了=100% すべてのタスクが完了しました! -------------------------- 共通のデータの入れ物に入っているデータを出力します..... msg : doWork3 : 0 msg : doWork1 : 0 msg : doWork4 : 0 msg : doWork0 : 0 msg : doWork0 : 1 msg : doWork0 : 2 msg : doWork0 : 3 msg : doWork0 : 4 msg : doWork2 : 0 msg : doWork0 : 5 msg : doWork0 : 6 msg : doWork0 : 7 msg : doWork0 : 8 msg : doWork0 : 9 msg : doWork1 : 1 msg : doWork1 : 2 msg : doWork2 : 1 msg : doWork3 : 1 msg : doWork1 : 3 msg : doWork4 : 1 msg : doWork1 : 4 msg : doWork2 : 2 msg : doWork1 : 5 msg : doWork3 : 2 msg : doWork1 : 6 msg : doWork2 : 3 msg : doWork1 : 7 msg : doWork4 : 2 msg : doWork1 : 8 msg : doWork2 : 4 msg : doWork3 : 3 msg : doWork1 : 9 msg : doWork2 : 5 msg : doWork3 : 4 msg : doWork4 : 3 msg : doWork2 : 6 msg : doWork2 : 7 msg : doWork3 : 5 msg : doWork4 : 4 msg : doWork2 : 8 msg : doWork3 : 6 msg : doWork2 : 9 msg : doWork4 : 5 msg : doWork3 : 7 msg : doWork3 : 8 msg : doWork4 : 6 msg : doWork3 : 9 msg : doWork4 : 7 msg : doWork4 : 8 msg : doWork4 : 9 処理を終了します ■拡張メソッドについて msg : やりますねぇ! すでに存在するメソッドを、拡張メソッドとして追加しても、拡張メソッドが無視されるだけです。 str2 : aaaaa 重複していなければ、自由に追加できます。 string.getAdvertisingMessage : これもうわかんねぇな。 ■動的型付け変数について [pythonコード]PythonSampleのコンストラクタが呼び出されました alctail.msg : あいうえお alctail.num : 123 ■アノテーションについて これもうわかんねぇな。 className : Sample2 クラスに付与しているアノテーション情報: annotation Class Name :SampleAttribute annotation Namespace :study1.case7 Properties: [job] : "学生です。" Constractor argument: System.String:あいうえお System.Int32:100 System.Int32:200 メソッド情報: method : sampleMethod1 annotation Class Name :SampleMethodAttribute annotation Namespace :study1.case7 Properties: [val] : (Int32)100 Constractor argument: メソッド情報: method : sampleMethod2 メソッド情報: method : Equals annotation Class Name :__DynamicallyInvokableAttribute annotation Namespace : Properties: Constractor argument: メソッド情報: method : GetHashCode annotation Class Name :__DynamicallyInvokableAttribute annotation Namespace : Properties: Constractor argument: メソッド情報: method : Finalize annotation Class Name :ReliabilityContractAttribute annotation Namespace :System.Runtime.ConstrainedExecution Properties: Constractor argument: System.Runtime.ConstrainedExecution.Consistency:3 System.Runtime.ConstrainedExecution.Cer:2 annotation Class Name :NonVersionableAttribute annotation Namespace :System.Runtime.Versioning Properties: Constractor argument: annotation Class Name :__DynamicallyInvokableAttribute annotation Namespace : Properties: Constractor argument: メソッド情報: method : GetType annotation Class Name :SecuritySafeCriticalAttribute annotation Namespace :System.Security Properties: Constractor argument: annotation Class Name :__DynamicallyInvokableAttribute annotation Namespace : Properties: Constractor argument: メソッド情報: method : MemberwiseClone annotation Class Name :__DynamicallyInvokableAttribute annotation Namespace : Properties: Constractor argument: annotation Class Name :SecuritySafeCriticalAttribute annotation Namespace :System.Security Properties: Constractor argument: メソッド情報: method : ToString annotation Class Name :__DynamicallyInvokableAttribute annotation Namespace : Properties: Constractor argument: フィールド情報: field : message1 annotation Class Name :SampleFieldAttribute annotation Namespace :study1.case7 Properties: [num] : (Int32)1000 Constractor argument: フィールド情報: field : message2 ■リフレクションについて コンストラクタが呼び出されました。 これもうわかんねぇな。 ■基本的なコレクション ・リストについて list:うひw1 list:うひw1 list:うひw1 list2:うひw1 list2:うひw2 list2:うひw3 ・マップについて その1 : これもうわかんねぇな。 その2 : やりますねぇ! ■SortedList(二分探索)について ☆SotredDictionaryCompair val1:AKYS(4) / val2:YJ(2) ☆SotredDictionaryCompair val1:YJ(2) / val2:MUR(3) ☆SotredDictionaryCompair val1:AKYS(4) / val2:MUR(3) ☆SotredDictionaryCompair val1:MUR(3) / val2:KMR(3) ☆SotredDictionaryCompair val1:AKYS(4) / val2:KMR(3) ☆SotredDictionaryCompair val1:MUR(3) / val2:TOHNO(5) ☆SotredDictionaryCompair val1:KMR(3) / val2:TOHNO(5) ☆SotredDictionaryCompair val1:AKYS(4) / val2:TOHNO(5) 要素を追加してみる・・・ ☆SotredDictionaryCompair val1:KMR(3) / val2:AAA(3) ☆SotredDictionaryCompair val1:AKYS(4) / val2:AAA(3) YJ : じゃけん夜行きましょうね MUR : あっそうだ KMR : なんで見る必要なんかあるんですか AAA : ヽ(^o^)丿 追加した行 ------------------- AKYS : エンジン全開 TOHNO : あー、いいっすねー 要素を取得してみる・・・ ☆SotredDictionaryCompair val1:KMR(3) / val2:AAA(3) ☆SotredDictionaryCompair val1:AKYS(4) / val2:AAA(3) ☆SotredDictionaryCompair val1:AAA(3) / val2:AAA(3) sortedList["AAA"] : ヽ(^o^)丿 追加した行 ------------------- ■SortedDictionaryについて ☆SotredDictionaryCompair val1:AKYS(4) / val2:YJ(2) ☆SotredDictionaryCompair val1:YJ(2) / val2:MUR(3) ☆SotredDictionaryCompair val1:AKYS(4) / val2:MUR(3) ☆SotredDictionaryCompair val1:MUR(3) / val2:KMR(3) ☆SotredDictionaryCompair val1:AKYS(4) / val2:KMR(3) ☆SotredDictionaryCompair val1:MUR(3) / val2:TOHNO(5) ☆SotredDictionaryCompair val1:KMR(3) / val2:TOHNO(5) ☆SotredDictionaryCompair val1:AKYS(4) / val2:TOHNO(5) 要素を追加取得してみる・・・ ☆SotredDictionaryCompair val1:KMR(3) / val2:AAA(3) ☆SotredDictionaryCompair val1:AKYS(4) / val2:AAA(3) YJ : じゃけん夜行きましょうね MUR : あっそうだ KMR : なんで見る必要なんかあるんですか AAA : ^^) _旦~~ 追加した行 ------------------- AKYS : エンジン全開 TOHNO : あー、いいっすねー 要素を取得してみる・・・ ☆SotredDictionaryCompair val1:KMR(3) / val2:AAA(3) ☆SotredDictionaryCompair val1:AKYS(4) / val2:AAA(3) ☆SotredDictionaryCompair val1:AAA(3) / val2:AAA(3) sortedDictionary["AAA"] : ^^) _旦~~ 追加した行 ------------------- ■yieldについて intList1のサイズ : 100 intList2のサイズ : 0 ■文字列操作の代表例 string.Formatの例その1 : foo↑ : ビール!ビール! string.Formatの例その2 : 2019年 06月 15日 十六進もこんな感じで出力できる:0001E240 3桁区切りも!:1,145,141,919 nameof(msg1) : msg1 Splitした結果(trimして出力):[10] Splitした結果(trimして出力):[20] Splitした結果(trimして出力):[30] Splitした結果(trimして出力):[40] Splitした結果(trimして出力):[50] ■正規表現の使い方 正規表現に一致しました! ■LINQについて val : 100 val : 90 val : 80 val : 70 val : 60 val : 50 study1.case1.Kiso1ディストラクタが呼び出されました。 Sample1のディストラクタが呼ばれました Sample1のディストラクタが呼ばれました Sample1のディストラクタが呼ばれました SampleObjectChildAのディストラクタが呼び出されました SampleObjectBaseのディストラクタが呼び出されました SampleObjectBaseのディストラクタが呼び出されました SampleObjectBaseのディストラクタが呼び出されました MultiThread3WorkObject ディストラクタ4 MultiThread3WorkObject ディストラクタ3 MultiThread3WorkObject ディストラクタ2 MultiThread3WorkObject ディストラクタ1 MultiThread3WorkObject ディストラクタ0 続行するには何かキーを押してください . . .