QA@IT

C# abstract、 orverrid、 orverload の扱い方について教えてください

5239 PV

お世話になっています。

実装のやり方で教えていただきたいところがあります。

ある抽象クラス(class1)があります。
・class1
abstract class class1
{

abstract public int aa(int a);
abstract public int aa(int a,string a);
}

抽象クラスを継承している、class2,class3
があります。class2の方は、aa(int ),オーバーロードのaa(int a,string a)
のメソッドを使用したく、二つのメソッドに対してclass2内でoverride
すればOKかと思います。しかしclass3の方は、aa(int )だけ実装したい場合
は、どのようにすればよいのでしょうか?(ビルドエラーとなる)
ともに実装させ、class3で
は、使用しないようにというようなことでしょうか?
よい方法はありませんでしょうか? 仕様でしょうか?

なぜそうしたいのかといいますと、修正するところがclass1,class2
(class2でoverloadの方を使用したい)
だけですむというところを目指しています。

.class2
class Class2 : class1
{
public override int aa(int a)
{
throw new NotImplementedException();
}
public override int aa(int a,string a)
{
throw new NotImplementedException();
}
}
.class3
class Class3 : class1
{
public override int aa(int a)
{
throw new NotImplementedException();
}
}

ご教授願います。

回答

オブジェクト指向では、子が親よりシンプルになるということは許されません。子は親が持っている属性をかならず引き継ぐべきであり、親が持っているものを子が隠蔽することはできません。
したがって、Class3 が class1 を継承している時点で、class1 で抽象的に定義されたメソッドはかならず Class3 も持たないといけません。Class3 で aa(int a,string a) を実装するか、あるいは Class3 を abstract のままにするかのいずれかしか選択肢はありません。

> なぜそうしたいのかといいますと、修正するところがclass1,class2
> (class2でoverloadの方を使用したい)
> だけですむというところを目指しています。

これが、オブジェクト指向とは別のところから出てきた要求であり、オブジェクト指向の枠組みとは合わないのだろうと推測します。

編集 履歴 (0)

Class2とClass3が少なからず関係している前提で密結合になって構わないのならば
aa(int, string)をClass2でsealedしてしまうのもアリかもしれません。
継承の前後関係がごちゃごちゃするのでスマートではないかもしれませんが、
Class3からaa(int, string)の呼出しは不可能です。

Class1に存在するメソッドをClass3から呼び出し、その結果、aa(int, string)が
呼び出されるようなものがある場合を想定してですが。

そもそもClass1やClass3からaa(int, string)が呼び出される想定のものでないならば、
ledsunさんの回答通りの書き方がスマートでしょう。

abstract public class Class1
{
    abstract public int aa(int a);
    abstract public int aa(int a, string b);

    public void hoge()
    {
        aa(999);
    }

    public void fuga()
    {
        aa(999, "Class1::fuga()");
    }
}

public class Class2 : Class1
{
    public override int aa(int a)
    {
        hoge();
        fuga();
        return aa(a, "Class2::aa()");
    }

    public override sealed int aa(int a, string b)
    {
        hoge();
        fuga();
        MessageBox.Show(a.ToString(), b);
        return a;
    }
}

public class Class3 : Class2
{
    public override int aa(int a)
    {
        aa(123);
        aa(a, "Class3::aa()");
        hoge();
        fuga();
        return a;
    }
}
編集 履歴 (0)

次のようにclass2でオーバーロード関数を実装するのが自然に思えます。

abstract class class1
{
    abstract public int aa(int a);
}

class Class2 : class1
{
    public override int aa(int a)
    {
       // 何か処理する
    }

    public int aa(int a,string a)
    {
       // ここだけオーバーロード
    }
} 

class Class3 : class1
{
    public override int aa(int a)
    {
       // 何か処理する
    }
}

class1に public int aa(int a,string a) を定義しないといけない理由があるのでしょうか?

編集 履歴 (0)

unibonさん
レスありがとうございます。

確かに別の要求のためです。
virtualを使うことで回避できそうなのですが
何かおかしいところがありそうでしょうか?

編集 履歴 (0)
  • 私の先日の回答は class1 を変更できないと勝手に仮定した上でのものになっていました。class1 を変更可能ならおっしゃるように virtual を使えば実現はできるのだろうと思います。ただ、私は virtual は普段は使わないものだと思います。もしも virtual 以外で実現する方法があれば、私は virtual 以外でやるほうが良いと思います。 -
  • virtual 以外でやる方法としては new を使う方法もあると思います。
    http://www.atmarkit.co.jp/ait/articles/0209/11/news001.html
    のような感じです。
    -
  • でも new も virtual も使うと難解になってしまうと思うので、できたらどちらも使わないほうが無難かなと思います。new か virtual のどちらかを使うとしたら、私は new のほうがまだ少しだけ平易になるのではないかと考えます。ただ new も virtual も使わずに済めば使わないほうが一番楽で良いと思います。 -
  • 勘違いしてました。new だと virtual の代替にはなりませんね。私の new についてのコメントは全部取り消します。 -

・class1
abstract class class1
{
abstract public int aa(int a);
virtual public int aa(int a,string a){return 0;}
}
virtual関数でうまくいきました。

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。