お疲れ様です。たなしょです。
このような駄文を垂れ流している記事にコメントありがとうございます。励みになります。
今日はC#で第一級市民化やラムダ式について勉強しました。
この辺から難易度が上がってきました。
第一級市民化
ユーザー定義型を組み込み型と区別しないように扱うこと?と書いてありますがよく理解できませんでした。
ユーザーが定義した方にも+,-などが使えるということなんでしょうかね?
using System;
class Counter
{
public int Count { get; private set; }
public Counter() : this(0) { }
private Counter(int count) { Count = count; }
public static Counter operator++(Counter x)
{
return new Counter(x.Count + 1);
}
}
class Sample
{
static void Main()
{
var c = new Counter();
var c1 = ++c;
Console.WriteLine(c1.Count);
var c2 = c++;
Console.WriteLine(c2.Count);
Console.WriteLine(c.Count);
}
}
上のコードの場合、宣言したcounterクラスを2回インクリメントしてるから最終的に```Console.WriteLine(c.Count);
```c#
public static Counter operator++(Counter x)
{
return new Counter(x.Count + 1);
}
上記のCounterクラスのopratorを2回処理してるから値が2になるということですね。
いろいろな演算子で実行できる
&や|などの演算子でも判定するようになってるみたいですね。
宣言した型名 operator 演算子の様に宣言するそうです。
using System;
struct IntBool
{
private int i;
public IntBool(int i) { this.i = i == 0 ? 0 : 1; }
public static bool operator true(IntBool b)
{
Console.WriteLine("operator true");
return b.i != 0;
}
public static bool operator false(IntBool b)
{
Console.WriteLine("operator false");
return b.i == 0;
}
public static IntBool operator &(IntBool a, IntBool b)
{
Console.WriteLine("operator &");
return new IntBool(a.i & b.i);
}
public static IntBool operator | (IntBool a, IntBool b)
{
Console.WriteLine("operator |");
return new IntBool(a.i | b.i);
}
public override string ToString()
{
return i == 0 ? "false" : "true";
}
}
コレクション初期化子
JavaでいうところArrayListみたいなやつなんですかね?
IEnumerableインターフェイスを実装していることとAddメソッドを持っていることがコレクション初期化子になる条件だそうです。
よくわかりません。
class CollectionInitializable : IEnumerable
{
public void Add(string item)
{
Console.WriteLine($"{item} Added");
}
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
そもそもIEnumerableって何だろうと思って公式ドキュメントを読んでみたところ、反復処理をサポートしているそうです。
GetEnumerator
も反復処理返すIEnumerableのメソッドです。
インデクサー
配列みたくユーザー定義型に添え字アクセスできる文法という認識です。
using System;
class Program
{
static void Main()
{
var x = new RangedArray<int>(1, 3);
x[1] = 1;
x[2] = 2;
x[3] = 4;
Console.WriteLine(x[3]);
}
}
class RangedArray<T>
{
T[] array;
int lower;
public RangedArray(int lower, int length)
{
this.lower = lower;
array = new T[length];
}
public T this[int i]
{
set { this.array[i - lower] = value; }
get { return this.array[i - lower] ; }
}
}
正直、第一級市民化をすることで可読性が高くなるのかなと疑問に思っています。
C#の文法を理解している人にとっては使いやすいのかもしれないですが、そうでない人は初見でソースコードを理解することが難しいのかなと思いました。大規模な開発なってくればありがたみがわかるのでしょうか。
デリゲート
間接的に関数を呼び出してくれる函?(関数?)的なものだと理解しました。
class Program
{
static void Main()
{
Func<int, int, int> f = Add;
Console.WriteLine(f(1, 2));
}
static int Add(int x, int y)
{
return x + y;
}
}
匿名関数、ラムダ式
デリゲートへ渡すための名前のないメソッドのことだそうです。
ラムダ式で書かれるイメージがあります。
class Smaple
{
static void Filter(List<int> input, Predicate<int> pred)
{
foreach (var x in input)
{
if (pred(x))
Console.WriteLine(x);
}
}
static void Main()
{
var foo = new List<int>() { 1, 2, 3, 4, 5 };
Filter(foo, x => x > 3);
}
}
x => x > 3
と書かれている部分がラムダ式です。
(パッとみ関数型言語みたいですよね。いつかLispやHaskellを勉強してみたいです。)
ローカル関数
匿名関数と似ているようで少し違うみたいです。
関数メンバーの直下にしか書けないのがローカル関数でどこでも書けるのが匿名関数らしいです。
下の例だとローカル関数と判別されます。
int fn(int n)
で宣言されている関数の中でラムダ式を展開しています。
class Program
{
static void Main()
{
int fn(int n) => n >= 1 ? n * fn(n - 1) : 1;
Console.WriteLine(fn(10));
}
}
下の例だと匿名関数と判別されます。
一度関数fn
を宣言した後に、ラムダ式を展開しています。
class Program
{
static void Main()
{
Func<int, int> fn = null;
fn = n => n >= 1 ? n * fn(n - 1) : 1;
Console.WriteLine(fn(10));
}
}
拡張メソッド
静的メソッドに色々な機能を追加できるみたいなことですよね。
いまいち本の説明だと理解できませんでした...。
最後に
ラムダ式は便利そうなと文法が関数型言語みたいでかっこいいので使っていきたいです。
だんだんと一回通して読んだだけだと理解できない部分が増えてきたので、
マイクロソフトのドキュメントと並行しながら読む機会が多くなりそうです。