C# におけるオブジェクト指向のまとめ
目次
- クラスの定義
- コンストラクタ、フィールド、メソッド
- プロパティ
- 自動実装プロパティ
- クラスメソッド、クラス変数
- アクセス権
- オーバーロード
- 演算子の定義
- 継承、オーバーライド
- 抽象クラス
- インターフェース
- ジェネリックス
- 拡張メソッド
- イベント
クラスの定義
-
class
というキーワードでクラスを宣言 -
new クラス名()
でインスタンスの生成
class Foo {}
class Program {
static void Main() {
Foo foo = new Foo();
}
}
コンストラクタ、フィールド、メソッド
- クラス名と同名の関数を宣言するとコンストラクタになります (コンストラクタの返り値は書かない)
- フィールドとメソッドのアクセス修飾子を省略した場合は、デフォルトで private になります
using System.IO;
using System;
class Point {
// フィールド
public int x, y;
// コンストラクタ
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// メソッド
public Point Add(Point other) {
return new Point(this.x + other.x, this.y + other.y);
}
public override string ToString() {
return "(" + this.x + ", " + this.y + ")";
}
}
class Program {
static void Main() {
Point p1 = new Point(1, 2);
Point p2 = new Point(3, 4);
Console.WriteLine(p1.Add(p2)); // => "(4, 6)"
}
}
メソッドはラムダ式を使って定義することもできます(C# 6.0)。
class Point {
// ...
// メソッド
public Point Add(Point other)
=> new Point(this.x + other.x, this.y + other.y);
public override string ToString()
=> "(" + this.x + ", " + this.y + ")";
}
プロパティ
- C# の プロパティ とは、フィールドとメソッドの両方の機能を持つものです
- 宣言の仕方は
型名 プロパティ名 { get { ... } set { ... } }
です -
get
やset
はデフォルトで public ですが、private
修飾子をつけることで private にすることができます
using System.IO;
using System;
class Point {
// フィールド
private int _x;
private int _y;
// プロパティ
public int x {
get { return this._x; }
private set { this._x = value; }
// value はプロパティの setter が受け取る引数が代入される、あらかじめ定義された変数
}
public int y {
get { return this._y; }
private set { this._y = value; }
}
// コンストラクタ
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Program {
static void Main() {
Point p = new Point(1, 2);
Console.WriteLine(p.x);
p.x = 0; // error
}
}
自動実装プロパティ
- プロパティの getter と setter を自動で実装するには、 自動実装プロパティ (auto-implemented properties)を利用します
- インスタンス変数の宣言の後にブロックを書き、
- その中で
get;
と書くとget { return this.インスタンス変数名 }
と展開されます。 - その中で
set;
と書くとset { this.インスタンス変数名 = value }
と展開されます。
- その中で
using System.IO;
using System;
class Point {
// フィールド
public int x { get; private set; } // getter は public、setter は private
public int y { get; private set; }
// コンストラクタ
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Program {
static void Main() {
Point p = new Point(1, 2);
Console.WriteLine(p.x);
//p.x = 0; // error
}
}
また、自動プロパティの初期化も行うことができます(C# 6.0)。
class Point {
public int x { get; private set; } = 0;
public int y { get; private set; } = 0;
}
クラスメソッド、クラス変数
- クラスメソッドは
static
修飾子を使います - クラス変数も
static
修飾子を使います
using System.IO;
using System;
class User {
// staticフィールド
private static int userCount = 0;
public string name { get; private set; }
private int age;
public User(string name, int age) {
IncrementUserCount();
this.name = name;
this.age = age;
}
// staticメソッド
public static int GetUserCount() {
return User.userCount;
}
private static void IncrementUserCount() {
User.userCount += 1;
}
}
class Program {
static void Main() {
Console.WriteLine(User.GetUserCount()); // => 0
User alice = new User("Alice", 20);
User bob = new User("Bob", 22);
Console.WriteLine(User.GetUserCount()); // => 2
}
}
アクセス権
アクセス修飾子は public と private と protected の3種類あります。 アクセス修飾子を省略した場合は、private になります。
オーバーロード
同じ名前のメソッドを定義することでオーバーロードすることができます。
using System.IO;
using System;
class Sample {
public static int foo(int x) {
return x + 1;
}
public static string foo(string x) {
return x + "1";
}
}
class Program {
static void Main() {
Console.WriteLine(Sample.foo(123)); // => 124
Console.WriteLine(Sample.foo("123")); // => "1231"
}
}
演算子の定義
- 演算子をオーバーロードするときは、
operator+
のように operator を演算子の前に付けてメソッド宣言を行います - 演算子のメソッドはクラスメソッドとして定義し、2つの引数(左オペランド, 右オペランド)をとるように宣言します
using System.IO;
using System;
class Point {
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// +演算子の再定義
public static Point operator+ (Point right, Point left) {
return new Point(right.x + left.x, right.y + left.y);
}
public override string ToString() {
return "(" + this.x + ", " + this.y + ")";
}
}
class Program {
static void Main() {
Point p1 = new Point(1, 2);
Point p2 = new Point(3, 4);
Console.WriteLine(p1 + p2); // => "(4, 6)"
}
}
継承、オーバーライド
- 継承をするには
class 子クラス : 親クラス
と書きます - 親クラスへの参照は
base
を使います - オーバーライドをするには、
override
修飾子を付けます - オーバーライドされるメソッドは、
すでに実装がある場合は
virtual
、実装がない場合はabstract
修飾子をメソッド定義に付けます
using System.IO;
using System;
class A {
public virtual void Foo() {
Console.WriteLine("A's method Foo()");
}
}
class B : A {
public override void Foo() {
Console.WriteLine("B's method Foo()");
base.Foo();
}
}
class Program {
static void Main() {
B b = new B();
b.Foo();
// => "B's method Foo()"
// => "A's method Foo()"
}
}
抽象クラス
抽象クラス・抽象メソッドを定義するには、abstract
キーワードを付けます。
using System.IO;
using System;
// 抽象クラスの定義
abstract class AbstractMusic {
public abstract void Play();
public abstract void Stop();
}
class Music : AbstractMusic {
public override void Play() {
Console.WriteLine("play!");
}
public override void Stop() {
Console.WriteLine("stop!");
}
}
class Program {
static void Main() {
Music music = new Music();
music.Play(); // => "play!"
}
}
インターフェース
- インターフェースを定義するには、
interface インターフェース名 { ... }
と書きます - C# のインターフェースは Java とは違い、インターフェースに定数などの変数を宣言することはできないです
- C# のインターフェースで宣言できる抽象メンバは、以下の通りです
- メソッド
- プロパティ
- インデクサ
- イベント
- interface で宣言するメンバは全て public abstract であると見なされるため、 これらの修飾子は不要です(明示的に書くとエラーで怒られる)。
// インターフェースの定義
interface USBInterface {
// インターフェースで宣言できるメソッドは、名前のみ
bool connectUSB();
bool disconnectUSB();
}
class Printer : USBInterface {
// インターフェースの実装
public bool connectUSB() {
// ...
return true;
}
public bool disconnectUSB() {
// ...
return true;
}
}
ジェネリックス
- ジェネリック とは、クラスやメソッドで型の種類を引数で取ることです
- ジェネリックは
class クラス名<仮の型名>
で型を取ることができます - ジェネリックの型に制約をつける場合は、ジェネリックの宣言をした後に、
where 仮の型名 : 制約
と書きます-
where 仮の型名 : クラス名
は、その型をそのクラスの派生クラスに制限します -
where 仮の型名 : インターフェース名
は、その型をそのインターフェースが実装してあるクラスに制限します
-
using System.IO;
using System;
// ジェネリックなRangeクラスの作成
// 型は、IComparableインターフェースを実装しているクラスに制限している
class Range<T> where T : IComparable {
T begin;
T end;
public Range(T begin, T end) {
if (begin.CompareTo(end) > 0) {
throw new System.ArgumentException("begin must be less than end");
}
this.begin = begin;
this.end = end;
}
public bool include(T item) {
return (item.CompareTo(begin) >= 0 && item.CompareTo(end) < 0);
}
}
class Program {
static void Main() {
Range<int> intRange = new Range<int>(1, 9);
Console.WriteLine(intRange.include(5)); // => True
Range<string> stringRange = new Range<string>("a", "e");
Console.WriteLine(stringRange.include("c")); // => True
Console.WriteLine(stringRange.include("z")); // => False
}
}
default
キーワードを用いると、ジェネリックの型でも初期値を設定することができます。
using System.IO;
using System;
class DefaultTest<T> {
public T GetDefault() {
return default(T);
}
}
class Program {
static void Main() {
Console.WriteLine( new DefaultTest<int>().GetDefault() ); // => 0
Console.WriteLine( new DefaultTest<string>().GetDefault() ); // => null
}
}
拡張メソッド
既存のクラスを継承せずにメソッドだけを拡張するには、
「static なクラス(名前はなんでも良い)に static なメソッドを定義し、その引数に拡張するクラスを取り、その引数の前に this
を付けます。」
using System.IO;
using System;
using System.Text.RegularExpressions;
// 拡張メソッドの定義
public static class StringExtender {
public static string ToAlphanumeric(this string str) {
return Regex.Replace(str, @"[^\w\s]+", "");
}
}
class Program {
static void Main() {
string str = "abc 123 </> edf";
Console.WriteLine(str.ToAlphanumeric()); // => "abc 123 edf"
}
}
イベント
- イベントの宣言は、
event デリゲート型名 イベント名
と書きます - C# は標準で
EventHandler(object, EventArgs)
というデリゲートを提供しています - イベントは、技術的にはデリゲート(delegate)と同じなので、イベントハンドラの登録をするときは
+=
で追加していきます
using System.IO;
using System;
class Mouse {
// イベント(メンバ変数と同じようにアクセスできる)
public event EventHandler onClick;
// Clickメソッドが呼び出されたら、イベントを発火させる
public void Click() {
if (onClick != null) {
onClick(this, EventArgs.Empty);
}
}
}
class Program {
static void Main() {
Mouse mouse = new Mouse();
// イベントハンドラの登録
mouse.onClick += (obj, e) => Console.WriteLine("event handler1: mouse was clicked!");
mouse.onClick += (obj, e) => Console.WriteLine("event handler2: mouse was clicked!");
mouse.Click();
// => "event handler1: mouse was clicked!"
// => "event handler2: mouse was clicked!"
}
}