「C Sharpの基礎 - インターフェイス」の版間の差分

ナビゲーションに移動 検索に移動
(ページの作成:「== 概要 == インターフェースとは、クラスが実装すべき規約を定めるものである。<br> インターフェイスには、非抽象クラスま…」)
 
17行目: 17行目:
* C# 8.0以降では、メンバの既定の実装を定義できる。
* C# 8.0以降では、メンバの既定の実装を定義できる。
<br><br>
<br><br>
== インターフェース ==
インターフェースとは、規約のみを定めるものである。<br>
<br>
C#では、抽象メソッドを使用することでメソッドの規約のみを定めることができる。<br>
つまり、C#のインターフェースとは、抽象メソッドのみを持つ抽象クラスだと考えることができる。<br>
<br>
C#のインターフェースの定義は以下のように記述する。<br>
<syntaxhighlight lang="c#">
interface インターフェース名
{
    // メソッド・プロパティの宣言
}
</syntaxhighlight>
<br>
インターフェースの実装は、クラスの継承と同じ構文である。<br>
<syntaxhighlight lang="c#">
class クラス名 : インターフェース名
{
    // クラスの定義
}
</syntaxhighlight>
<br>
インターフェースには、以下のような特徴がある。
* メンバ変数(フィールド)を持つことができない。
* staticメソッドを持つことができない。
* 宣言したメソッドおよびプロパティは、全て<code>public abstract</code>になる。
* 1つのクラスが複数のインターフェースを実装(多重継承)できる。
<br>
C# 8.0では、制限がいくつか緩和されている。<br>
機能面でいうと、クラスと抽象クラスとの差は、<u>メンバ変数(フィールド)を持てない代わりに多重継承できる</u>程度である。<br>
<br><br>
== 標準クラスライブラリのインターフェース ==
.NET Frameworkの標準クラスライブラリでは、いくつかの汎用性の高いインターフェースが標準で存在する。<br>
以下では、そのうちのいくつかを記載する。<br>
<br>
==== IComparable ====
<code>IComparable<T></code>インターフェイス(<code>System</code>名前空間)は、順序比較ができるものを表す。<br>
配列の整列等に使用する。<br>
<syntaxhighlight lang="c#">
using System;
using System.Linq;
/// <summary>
/// 2次元上の点
/// <see cref="IComparable{T}"/> を実装している = 順序をつけられる。
/// </summary>
class Point2D : IComparable<Point2D>
{
    public double X { get; }
    public double Y { get; }
    public Point2D(double x, double y)
    {
      X = x;
      Y = y;
    }
    public double Radius => Math.Sqrt(X * X + Y * Y);
    public double Angle => Math.Atan2(Y, X);
    /// <summary>
    /// 距離で順序を決める。
    /// 距離が全く同じなら偏角で順序付け。
    /// </summary>
    /// <param name="other"></param>
    /// <returns></returns>
    public int CompareTo(Point2D other)
    {
      var r = Radius.CompareTo(other.Radius);
      if (r != 0)
      {
          return r;
      }
      return Angle.CompareTo(other.Angle);
    }
}
class IComparableSample
{
    public static void Main()
    {
      const int N = 5;
      var rand = new Random();
      var data = Enumerable.Range(0, N).Select(_ => new Point2D(rand.NextDouble(), rand.NextDouble())).ToArray();
      Console.WriteLine("元:");
      foreach (var p in data) WriteLine(p);
      // 並べ替えの順序に使える
      Console.WriteLine("整列済み:");
      foreach (var p in data.OrderBy(x => x)) WriteLine(p);
    }
    private static void WriteLine(Point2D p)
    {
      Console.WriteLine($"({p.X:N3}, {p.Y:N3}), radius = {p.Radius:N3}, angle = {p.Angle:N3}");
    }
}
</syntaxhighlight>
<br>
==== コレクション ====
コレクションには、同じ操作ができる様々な実装方法がある。(それぞれ、メリットおよびデメリットがある)<br>
C#では、操作の種類ごとにインターフェイスが標準で存在しており、コレクションはそれらのインターフェイスを実装する。<br>
<br>
下表に、その例を示す。(いずれも<code>System.Collections.Generic</code>名前空間)<br>
<center>
{| class="wikitable" style="background-color:#fefefe;"
|-
! style="background-color:#00ffff;" | インターフェイス
! style="background-color:#00ffff;" | 説明
|-
| IEnumerable<T> || 要素の列挙ができる。<br>foreach文やLINQ to Objectsで使用する。
|-
| ICollection<T> || IEnumerable<T>に加えて、要素の追加(Add)、削除(Remove)、要素の個数の取得ができる。
|-
| IList<T> || ICollection<T>に加えて、インデクサを使用した要素の読み書きができる。
|-
| IDictionary<TKey, TValue> || 辞書アクセス(キーを使った値の検索)しての値の読み書きができる。
|-
| IReadOnlyCollection<T><br><u>.NET Framework 4.5以降とC# 5.0以降</u> || IEnumerable<T>に加えて、要素の個数を取得できる。<br>読み取り専用なので共変。
|-
| IReadOnlyList<T><br><u>.NET Framework 4.5以降とC# 5.0以降</u> || IReadOnlyCollection<T>に加えて、インデクサを使用した要素の読み取りができる。<br>読み取り専用なので共変。
|-
| IReadOnlyDictionary<TKey, TValue><br><u>.NET Framework 4.5以降とC# 5.0以降</u> || 辞書アクセス(キーを使った値の検索)しての値の読み取りができる。
|}
</center>
<br>
上表のうち、<code>IEnumerable<T></code>と<code>IReadIReadOnlyList<T></code>の例を示す。<br>
<syntaxhighlight lang="c#">
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// 連結リスト
/// <see cref="IEnumerable{T}"/> を実装している = データの列挙ができる。複数のデータを束ねてる。
/// </summary>
/// <typeparam name="T"></typeparam>
class LinkedList<T> : IEnumerable<T>
{
    public T Value { get; }
    public LinkedList<T> Next { get; }
    public LinkedList(T value) : this(value, null) { }
    private LinkedList(T value, LinkedList<T> next) { Value = value; Next = next; }
    public LinkedList<T> Add(T value) => new LinkedList<T>(value, this);
    public IEnumerator<T> GetEnumerator()
    {
      if(Next != null)
      {
          foreach (var x in Next)
          {
            yield return x;
          }
          yield return Value;
      }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
class IEnumerableSample
{
    public static void Main()
    {
      var a = new LinkedList<int>(1);
      var b = a.Add(2).Add(3).Add(4);
      // foreach で使える(これは IEnumerable 必須ではない)
      foreach (var x in b)
          Console.WriteLine(x);
      // string.Join で使える
      Console.WriteLine(string.Join(", ", b));
      // LINQ で使える
      Console.WriteLine(b.Sum());
    }
}
</syntaxhighlight>
<br>
<syntaxhighlight lang="c#">
using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// 4次元上の点
/// <see cref="IReadOnlyList{T}"/> を実装している = <see cref="IEnumerable{T}"/>に加えて、インデックス指定で値を読める。
/// </summary>
class Point4D : IReadOnlyList<double>
{
    public double X { get; }
    public double Y { get; }
    public double Z { get; }
    public double W { get; }
    public Point4D(double x, double y, double z, double w)
    {
      X = x; Y = y; Z = z; W = w;
    }
    public double this[int index]
    {
      get
      {
          switch (index)
          {
            default:
            case 0: return X;
            case 1: return Y;
            case 2: return Z;
            case 3: return W;
          }
      }
    }
    public int Count => 4;
    public IEnumerator<double> GetEnumerator()
    {
      yield return X;
      yield return Y;
      yield return Z;
      yield return W;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
class IReadOnlyListSample
{
    public static void Main()
    {
      var p1 = new Point4D(1, 2, 3, 4);
      var p2 = new Point4D(3, 7, 5, 11);
      // X, Y, Z, W の代わりに 0, 1, 2, 3 のインデックスで値を読み出し
      var innerProduct = 0.0;
      for (int i = 0; i < 4; i++)
      {
          innerProduct += p1[i] * p2[i];
      }
      Console.WriteLine(innerProduct);
    }
}
</syntaxhighlight>
<br>
==== IDisposable ====
<code>IDisposable</code>インターフェイス(<code>System</code>名前空間)は、明示的なタイミングで破棄処理を行う場合に使用する。<br>
<syntaxhighlight lang="c#">
using System;
/// <summary>
/// <see cref="IDisposable"/> を実装している = 使い終わったら明示的に Dispose を呼ぶ必要がある。
/// </summary>
class Stopwatch : IDisposable
{
    System.Diagnostics.Stopwatch _s = new System.Diagnostics.Stopwatch();
    public Stopwatch() { _s.Start(); }
    public void Dispose()
    {
      _s.Stop();
      Console.WriteLine(_s.Elapsed);
    }
}
class IDisposableSample
{
    public static void Main()
    {
      // using ブロックを抜けたら自動的に Dispose が呼ばれる
      using (new Stopwatch())
      {
          var t = T(12, 6, 0);
      }
    }
    private static int T(int x, int y, int z) => x <= y ? y : T(T(x - 1, y, z), T(y - 1, z, x), T(z - 1, x, y));
}
</syntaxhighlight>
<br>


__FORCETOC__
__FORCETOC__
[[カテゴリ:C_Sharp]]
[[カテゴリ:C_Sharp]]

案内メニュー