質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

Q&A

3回答

3072閲覧

C#の継承元(ベースクラス)を継承する側から、または継承関係定義後に指定したい

hirotamasami

総合スコア5

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

継承

継承(インヘリタンス)はオブジェクト指向プログラミングに存在するシステムです。継承はオブジェクトが各自定義する必要をなくし、継承元のオブジェクトで定義されている内容を引き継ぎます。

0グッド

0クリップ

投稿2020/09/18 06:02

前提・実現したいこと

C#の継承についてですが、
以下のような継承関係のクラスを
ライブラリにします。

LibBase.cs

C#

1class LibBase 2{ 3}

LibSuper.cs

C#

1class LibSuper :LibBase 2{ 3}

LibBaseはLibSuper以外にも
ライブラリ内の複数のクラスで継承元として使用されます。
LibSuperは
ライブラリ使用側で共通で使用されます。

上記を使用する側では
LibSuperを用いる場合、
これをそのまま使用するのですが
この時LibBaseの機能を拡張して
LibSuperを使用したい場合、
使用する側で
LibBaseをパワーアップしたクラス
UserBaseをLibSuperに継承している状態にしたいです。
LibSuperをライブラリを使用する側で
定義すれば可能ですが、
LibSuperにも共通要素がほとんどですので、
ライブラリ側に定義したいんですが、
なにか方法はございますでしょうか?

このようなサイトでの質問もまだ慣れておらず、情報不足ありましたら
ご指摘ください。
宜しくお願い致します。

試したこと

・ジェネリックを用いて継承
LibSuperでジェネリックを用いて指定したクラスを継承させようとしましたが、
できませんでした。

C#

1class LibSuper<Base> : Base where Base: LibBase 2{ 3}

エラーメッセージ:
エラー CS0689 Base' は型パラメーターであるため、派生させることはできません。

補足情報(FW/ツールのバージョンなど)

・OS バージョン Windows10

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2020/09/18 06:09

何をどうしたいのか全くわかりません。LibSuper をユーザーが継承すればいいのでは?
fana

2020/09/18 06:31

> LibBaseをパワーアップ パワーアップとは…? 何か話がわかる記述(メソッドとか)をコードに示されると話がわかりやすいのではないかと.
hirotamasami

2020/09/18 06:35 編集

コメントありがとうございます。 LibSuperを用いた時に共通で使用したい機能などを持たせたい場合は LibSuperを継承したクラスUserSuperを作成し、 それに機能を実装すれば大丈夫だと思いますが、 LibBaseはLibSuper以外にもLibSuper2などに継承元として使用されています。 LibBaseはLibSuperやLibSuper2に共通で必要な要素が定義されているわけですが、 ライブラリを使用する側でLibSuper、LibSuper2 を継承したクラスUserSuper、UserSuper2の両方で必要な機能を新たに実装したい場合 LibSuper、LibSuper2の両方に全く同じ機能を持たせれば大丈夫ですが、 もちろんそんなことはしたくありませんので、 LibSuper、LibSuper2の継承元(LibBase)にその機能を定義したいです。 ライブラリのLibBase自体を修正すれば可能ですが、 ライブラリは複数のユーザーが使用するので、 ライブラリ使用側の共通の要素以外は 実装したくないので、 ライブラリ使用側で定義する方法はないかという質問になります。 よろしくお願いします。
hirotamasami

2020/09/18 06:54 編集

コメントありがとうございます。 >パワーアップとは…? LibBaseは複数のクラスで継承元として使用されております ```C# class LibSuper :LibBase { } ``` ```C# class LibSuper2 :LibBase { } ``` 上記がライブラリに定義されており ライブラリ使用側は このクラスをそのまま使用するなり、継承して使用するなりするわけですが、 この時LibSuper、LibSuper2の両方で必要な要素があった場合 ライブラリ使用側で LibBaseを継承したクラス(LibBaseをパワーアップしたクラス)UserBaseを作成し LibSuper、LibSuper2の継承元がそれを継承していることにできるかということです "試したこと"に記載させていただいたような形で記述できれば一番ベストだったんですが、 ライブラリ側 class LibSuper<Base> : Base where Base: LibBase { } ライブラリ使用側 class UserBase:LibBase { public int count { get; set;} } ```C# class UserSuper :LibSuper<UserBase> { } ``` ```C# class UserSuper2 :LibSuper2<LibBase> { } ``` よろしくお願いします。
fana

2020/09/18 06:52

> UserSuper、UserSuper2の両方で必要な機能 というのは,LibSuperやLibBaseと何か関係があるのでしょうか? 例えば, UserSuperとUserSuper2が,ある共通の機能のためのメソッドfunc()を持つのだとして,それをLibSuperやLibBaseの側から呼ばねばならないものでしょうか? それとも単に,func()はUserSuperやUserSuper2に備わっていれば良いだけの存在でしょうか?
Zuishin

2020/09/18 06:58

クラスが肥大化しすぎている可能性がありますね。一つのクラスが様々な機能を持っているんだと思いますが、これを別のクラスに分けてパーツを提供すれば、継承にこだわることなくユーザーは必要な機能を使用できます。 「継承より合成(委譲)」でググってみてください。
退会済みユーザー

退会済みユーザー

2020/09/18 07:20 編集

拡張する可能性がある要素をインターフェースにしておいて、それをLibBaseにプロパティなりで持たせておけばいいんじゃないですかね。 設計寄りの話だと思うので、何故そういう事をしたいのか具体的な用途を提示して貰った方が具体的な解決策が出てくる気がします。
退会済みユーザー

退会済みユーザー

2020/09/18 07:27

何をしたいのか理解できていませんが、virtual / override で何とかできる話ではないということですか?
hirotamasami

2020/09/18 07:52

>>UserSuperとUserSuper2が,ある共通の機能のためのメソッドfunc()を持つのだとして,それをLibSuperやLibBaseの側から呼ばねばならないものでしょうか? それとも単に,func()はUserSuperやUserSuper2に備わっていれば良いだけの存在でしょうか? はい、LibSuperやLibBaseをから使用したい場合もございます。 例えば LibBaseにDataクラスのインスタンスを確認(保持しているフィールド変数なりを)し 状態によってbool型の値を返すvirtualメソッド Check(Data data)があるとし、 LibBaseでのそのメソッドの処理では DataCheck1(Data data)の確認のみ行うとします。 class LibBase { public virtual bool Check(Data data) { return DataCheck1(data); } public bool DataCheck1(Data data) { //・・・ } } それをユーザー側では、 もともとあった確認処理 DataCheck1(Data data)と 新たな確認処理 DataCheck2(Data data) が必要だったとします。 class UserBase :LibBase { public override bool Check(Data data) { bool result = base.Check(data); return result && DataCheck2(data); } public bool DataCheck2(Data data) { //・・・ } } ※くどくて申し訳ありませんが  Check(Data data)を  使用した際  LibSuper,LibSuper2は  どちらもDataCheck2(Data data)  の確認が必要です これらを実装した際 LibSuperのインスタンスなどを UserBaseや LibBaseにキャストした時 にCheck(Data data) を使用した時 UserBase userBase = (UserBase)libBase; 新たに実装した DataCheck2(Data data) の処理もちゃんと使用できるようにしたいです。 >>「継承より合成(委譲)」でググってみてください。 合成(委譲)というものが、使えるかもしれないんですね。 かしこまりました。 ありがとうございます。 確認してみます。 >>拡張する可能性がある要素をインターフェースにしておいて、それをLibBaseにプロパティなりで持たせておけばいいんじゃないですかね。 設計寄りの話だと思うので、何故そういう事をしたいのか具体的な用途を提示して貰った方が具体的な解決策が出てくる気がします。 上記の >>UserSuperとUserSuper2が,ある共通の機能のためのメソッドfunc()を持つのだとして,それをLibSuperやLibBaseの側から呼ばねばならないものでしょうか? それとも単に,func()はUserSuperやUserSuper2に備わっていれば良いだけの存在でしょうか? の返答文に記載いたしました処理などをユーザーサイドから自由に変更でき、かつその変更内容が同一の場合、 1か所にまとめたい形になります。 また返答文内のDataCheck1(Data data)はそのライブラリを使用する場合必ず行いますので、 できればインターフェースにして、 可能な限り実装先で定義するのは避けたい形になります。 >>何をしたいのか理解できていませんが、virtual / override で何とかできる話ではないということですか? ライブラリで定義したvirtulの要素を ユーザー側からoverrideする時それが同一処理の時、1か所にまとめたいという質問になります。 よろしくお願いします。
退会済みユーザー

退会済みユーザー

2020/09/18 08:15

> ユーザー側からoverrideする時それが同一処理の時、1か所にまとめたいという質問になります。 「同一処理」のときは override しないということでよさそうな気がしますし、「1か所にまとめたい」というのも理解できないです。理解力が無くてすみませんがギブアップです。
hirotamasami

2020/09/18 08:18

>>「同一処理」のときは override しないということでよさそうな気がしますし、「1か所にまとめたい」というのも理解できないです。理解力が無くてすみませんがギブアップです。 いろいろ考えていただきありがとうございます。 他の方々の案も参考にさせていただき 解決しだい更新します
fana

2020/09/18 08:25

要は,こんな話かな? 継承関係が LibBase ← LibSuper ← UserSuper ↑ LibSuper2 ← UserSuper2 という状態で,LibBaseの仮想メソッドをUserSuperとUserSuper2がoverrideするとき, そのコードが全く同じになるならば2つ書くのを避けたい. もしも既存の継承関係に対して後から下記のように UserBase を(枝分かれする前のところに)差し挟むことができたならば, UserBaseでoverrideを書けば済むが,それはできないのか? という. LibBase ↑ (UserBase) ← LibSuper ← UserSuper ↑ LibSuper2 ← UserSuper2
hirotamasami

2020/09/18 08:31

LibBase ↑ (UserBase) ← LibSuper ← UserSuper ↑ LibSuper2 ← UserSuper2 はい、その通りです! わかりにくい説明で申し訳ございませんでした。 もちろん継承にこだわる必要はございませんが、 今のところそこで悩んでる段階になります。
guest

回答3

0

(「質問への追記・修正、ベストアンサー選択の依頼」に書かれた内容に基づいて書きます)

LibBase ← LibSuper ← UserSuper ↑ LibSuper2 ← UserSuper2

という継承関係において,
LibBaseの仮想メソッドCheck()を UserSuper, UserSuper2 がoverrideするとき,それが両者共に

CSharp

1public override bool Check(Data data) 2{ 3 bool result = base.Check(data); 4 return result && DataCheck2(data); 5}

という "同一の" 形に「たまたま」なるのだとして,
そのことだけを理由とした余計な継承階層を加える必要はないと考えます.

DataCheck2()の内容をUserSuper, UserSuper2 の両者に "重複して" 実装しないこと」を目指すくらいで良いのではないでしょうか.

例えば…

CSharp

1//共通実装 2static class CommonProc 3{ 4 public static bool DataCheck2( Data data /*, dataのチェックに必要な何かがあれば引数とする*/ ) 5 { 6 ... 7 } 8} 9 10//UserSuper, UserSuper2 のCheck()の実装 11public override bool Check(Data data) 12{ 13 bool result = base.Check(data); 14 //共通の実装を用いる 15 return result && Helper.CommonProc( data /*, このインスタンスが保持しているdataのチェックに必要な物があれば引数に渡す*/ ); 16}

要は,単なる「関数化」による再利用(C#の言葉ではないかもしれないけど)です.


他,

  • 「Dataをチェックする手段」を外から設定としてLibBaseに与える
  • Dataの側にcheck()を持たせて必要ならそっちを仮想メソッドにする

とかする形ならば,LibBase.Check()は仮想にしなくてもよいですね.

投稿2020/09/18 10:03

編集2020/09/18 10:13
fana

総合スコア11708

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hirotamasami

2020/09/24 04:58

回答ありがとうございます。 こちらの >「Dataをチェックする手段」を外から設定としてLibBaseに与える などは 「質問への追記・修正、ベストアンサー選択の依頼」で 他の方も書いてくださってた、 合成(委譲)を少し確認してみたのですが、 こちらの概念に近い作り方をするということですね 合成(委譲)という用語は知りませんでしたが、 こちらの方法はすでに用いており、 今回の継承の問題が発生しているところでも使用しておりました。 今回の例でいうCheck(Data data)を別のクラスで持たせて それを使用するように作成することは可能ですが、 少々問題がございまして、 これは後日別の質問として掲載する予定でしたが、 継承元のクラスをジェネリックを設定して、 作成しているものがございます。 これらを用いたものを 合成(委譲)の作り方で作成いたしましたところ、 継承元でこれを定義した場合、  ・継承先で指定しなければならない要素が増える  ・継承元での余分な定義が増える 例 //BaseDataを継承するクラスが存在します。 public class SuperData : BaseData { } //BaseOriginalDataを継承するクラスが存在します。 public abstract class SuperOriginalData<Op> : BaseOriginalData<SuperData, Op> where Op : Option { List<Data> dataList; Op option; } ///OriginaDataを管理するクラス public abstract class OriginalDataManager<OData, Op, DataType> where OData : BaseOriginalData<DataType, Op> // BaseOriginalDataを継承(またはそのままBaseOriginalDataを使用)していればOKだが使う側でDataTypeなどは指定しているはずなのに、再定義しなければならない where Op : Option where DataType : BaseData //DataType は使わないがBaseOriginalDataを制約するために定義するしかない { OData originalData Op option; } のようになりせっかくOriginalDataManagerだけでいろんな機能を持たせられるように作っても、 上記のような状態になったりします。 もちろん >単なる「関数化」による再利用 で仰ってるように 関連付けなど特にせずに 単一で静的クラスなどを作ってそこから操作するでも可能ですが、 どっちにしろ外側のクラスが増えるとどんどん管理が難しくなりそうなので、 できれば、継承して単一のインスタンスに追いやりたかったのですが、 やはり難しそうですね
fana

2020/09/24 06:43

> これは後日別の質問として掲載する予定でしたが 解決したい事柄に関して何かしらの特殊事情があるのであれば,その旨をはっきりさせた新規質問とすると良いだろうと思います. (既に存在していて変えられない部分がある状況での話なのか,全て書き換えてもよい話なのか等にも依るでしょうし.)
hirotamasami

2020/09/24 07:51

そうですね。 その件に関しましては、 新規で新たに質問として作成させていただきます ありがとうございます。 その件がもしうまく解決すれば、 こちらの問題も 仰っていただいたように 無理に多重に継承する必要もなくなる可能性もありますし 近いうちに投稿いたします。
guest

0

LibBaseが動物、UserBaseが哺乳類、LibSuperが猿、みたいな考え方をすると良いよ。

投稿2020/09/18 07:54

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hirotamasami

2020/09/18 09:00 編集

ライブラリ側で(Lib) 動物(Base),猿(Super1),人間(Super2) 猿 (Super1):動物(Base) 人間(Super2):動物(Base) が定義されていたとすれば ユーザー側(User)は そのライブラリを用いて 哺乳類の機能が必要な 超猿 (Super1):猿 超人間(Super2):人間 を作ろうとした時 このままだと哺乳類の機能を 超猿(Super1)、超人間(Super2)にそれぞれ全く同じものを実装しなければならないので、 これらにどうやって簡潔に哺乳類(Base)の機能を持たせられるか?という質問になります。 ※この設計だとライブラリ側に哺乳類を定義していいと思いますが よろしくお願いします。
退会済みユーザー

退会済みユーザー

2020/09/18 09:04

ん? 哺乳類は動物を継承してて、猿は哺乳類を継承できるけど、哺乳類は猿を継承できない。 そんな感じ。
guest

0

なんかめんどくさいこと考えてるようだけど、
例えば、LibBaseが車、UserBaseがスポーツカー、LibSuperがGT-R、みたいな考え方をすると良いよ。

投稿2020/09/18 06:54

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2020/09/18 07:05

I don't understand what you mean.
Zuishin

2020/09/18 07:05

「良い」理由が何一つありません。
退会済みユーザー

退会済みユーザー

2020/09/18 07:23

回答になってないと思います
退会済みユーザー

退会済みユーザー

2020/09/18 07:25

めちゃめちゃ分かりやすく書いたのに、なんでわからんの?
fana

2020/09/18 07:33

車の話に疎いので… その考え方を用いることによって何をどのように解決に導くことができるのか,という,その具体的なところを把握することができませんでした. Sorry.
Zuishin

2020/09/18 07:33

どの特徴がどれの何と一致しているのか全く意味不明です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問