メソッド -演算子のオーバーロード-

ホームC#プログラミング応用講義 > 演算子のオーバーロード

目次

演算子の使い道

演算子というと、特に学んだ覚えはありません。+ はプラス、- はマイナス、そのまま単純に使ってきました。
そして多くの場合、演算子は単純に演算を行うために利用します。

ところが、演算子は拡張することができます。単純な算術演算だけではなく、オブジェクト同士を加算したりすることも可能なのです。

その前に、演算子はメソッドと非常によく似た動きをすると思ったことはありますか? 演算子はメソッドと非常によく似ています。
例えば、+ 演算子は通常以下のように利用します。
int a = 1;
int b = 2;
int c = 0;

c = a + b;
しかし、Plus クラスのメンバ plus() メソッドで、その加算処理を定義しておけば、+ 演算子は以下のように代用できます。
c = Plus.plus(a,b);
+ 演算子の左側に第1引数、右側に第2引数が存在していると考えると、まさに単純なメソッドのようなものでしょう。

ただし、- 演算子のように右側にしか引数をとらないと考えられる演算子もあります。え? 両側にとるじゃないかって?
ここでの - 演算子は
c = -a;
というように、符号を変える働きをする演算子です。

演算子をメソッドと同じように考えるならば、当然それは拡張することができます。
+ 演算子はすでに int, double, string などの各種の型の加算演算に利用されていますが、さらに拡張を加えることができます。

その拡張機能が、演算子のオーバーロードです。
演算子をメソッドと考え、それを多重定義していくことによって拡張することができます。

例えば、x 軸と y 軸の座標情報を持つオブジェクトが2つあるとします。
その2つのオブジェクトの x 座標、y 座標を加算し、新たなオブジェクトを誕生させ、そしてその両座標の符号を変える実装するとすれば、以下のようになるでしょう。
using System;

class Plus
{
    int x;
    int y;
    
    public Plus (int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    
    public Plus plus (Plus p1, Plus p2)
    {
        return new Plus(p1.x + p2.x, p1.y + p2.y);
    }
    
    public Plus minus (Plus p)
    {
        return new Plus(-p.x,-p.y);
    }
    
    public void Show()
    {
        Console.WriteLine(x + ", " + y);
    }
}

class Demo
{
    public static void Main(string[] args)
    {
        Plus p1 = new Plus(1,2);
        Plus p2 = new Plus(2,4);
        Plus p = new Plus(0,0);
        
        p = p.plus(p1, p2);
        p.Show();
        
        p = p.minus(p);
        p.Show();
    }
}
加算するメソッドは plus で、符号を逆にするメソッドは minus です。

それらを使うときには、加算する2つのオブジェクトを引数に、符号を逆にするオブジェクトを引数に、いずれも引数に渡すことでその機能を実装しています。

しかし、この場合のように「加算」「符号の反転」といった「+」「-」演算子に相当する機能の場合、メソッドで処理するよりも演算子で処理するほうが直感的であるのは明らかです。
+ や - のように、整数型を演算する機能ではありませんが、目印としてこれらの演算子を利用するため、非常に好都合です。

実際の「演算子のオーバーロード」は、次の項目でやってみましょう。上のソースを大部分流用できます。

operator

演算子というのは英語で「operator」といいます。演算子をオーバーロードするにはこのキーワード「operator」を利用します。

ただし、注意が必要なのは演算子のオーバーロードは必ず「static」なメンバとして定義するということです。
static にすることで、オブジェクトを生成することなく目的の演算子を利用できるからです。いや、static にしないとそれ以前に利用できません。

また、引数の型に注意しなければなりません。
しかし、それは実際のソースを見てからのほうがわかりやすいでしょう。
using System;

class Plus
{
    int x;
    int y;
    
    public Plus(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    
    // + 演算子のオーバーロード
    public static Plus operator + (Plus p1, Plus p2)
    {
        return new Plus(p1.x + p2.x, p1.y + p2.y);
    }
    
    // - 演算子のオーバーロード
    public static Plus operator - (Plus p)
    {
        return new Plus(-p.x,-p.y);
    }
    
    public void Show()
    {
        Console.WriteLine(x + ", " + y);
    }
}

class Demo
{
    public static void Main(string[] args)
    {
        Plus p1 = new Plus(1,2);
        Plus p2 = new Plus(2,4);
        Plus p = new Plus(0,0);
        
        // + 演算子のオーバーロードを利用
        p = p1 + p2;
        p.Show();
        
        // - 演算子のオーバーロードを利用
        p = -p;
        p.Show();
    }
}
オーバーロードしている部分を見ると、以下のようになっています。
public static Plus operator + (Plus p1, Plus p2)
引数の型が、クラス型(ここでは Plus 型)になっているのがわかります。

演算子のオーバーロードは必ず少なくとも1つにクラス型でなければ、元の + 演算子と区別が付かなくなってしまいます。
また、戻すのも Plus のオブジェクトですから、戻り値の型も Plus になっていることに注意します。

今度は、演算子を利用するところを見てください。
ここでは、演算子の左に Plus 型、右にも Plus 型なので、先ほど定義した演算子のオーバーロードがぴったりだということになります。
演算子の左辺と右辺はメソッドのオーバーロードで習得した「シグニチャ」のようになっています。

また、- 演算子は取る引数は1つと考えられますから、上のように第1引数しか定義していません。
では、3 = 5 - 2; と利用するような - 演算子はどう定義するのかというと、+ 演算子のように引数を2つ定義すれば区別されます。
このことから、引数の数ももちろんシグニチャであると考えられます。

ちなみに、引数を2つとると考えられる演算子を二項演算子といい、1つの演算子を単項演算子といいますが、たいして重要ではありません。

重要なのは使うパターンです。

これまで説明したように、演算子はメソッドと同じように利用できるので、+ 演算子を加算という本来の目的以外に利用することも可能です。
しかし、それをしてしまうと直感的どころかややこしくなってしまいます。

演算子をオーバーロードするのは単に「わかりやすさ」を追求しているだけです。
ソースを見せたように、普通にメソッドを定義して、それを利用することも可能ですが、それではわかりにくいため演算子を利用しています。

よって、演算子をオーバーロードする際に、その演算子が持つ本来の意味を無視してはいけません。しようと思えばできますが、下手なプログラマだとうわさされます。
そしてまた、オーバーロード内には複雑な動きをさせないほうがいいです。

上のサンプルで、+ や - 演算子のオーバーロードの中で、ついでに表示させる機能を実装することも可能です。
しかし、それは「拡張しすぎ」というものです。

+ は単に加算の機能だけを実装することにより、スマートにコーディングを進めることができます。
ただし、加算の機能はきちんとその + のオーバーロードの中に定義しなければ意味がありません。丸く、角がないように実装なければ演算子のオーバーロードはややこしいだけになってしまいます。

この演算子のオーバーロードはここで述べたグラフィックの座標などに有効です。

ちなみに、= 代入演算子などのの演算子はオーバーロードすることはできません。
しかし += などの複合代入演算子になると、+ をオーバーロードするだけでオーバーロードされますから、C# というのは本当によくできていると感心させられます。

[ ステップアップC# ] [ C#プログラミング応用講義 ]