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

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

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

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

Q&A

2回答

5250閲覧

C# enumの値を継承クラスによって書き換える方法

Taffy

総合スコア33

C#

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

0グッド

0クリップ

投稿2021/04/21 01:36

編集2021/04/21 02:28

開発環境は Visual Studio Express 2017 for Windows Desktop
言語はC#、.NET Framework 4.7.2を使用して Windowsフォームアプリケーションを開発しています。

システム改造のため、既存クラスを基底クラスとした、「派生クラス」を作成する方向で設計をしています。
その中で、いわゆるC/C++の#define定義の代わりに定義値を多数宣言して使われているDefineクラスがあります。
Defineクラスでは多数の「const」で定義された変数が存在しています。

また、下記の例ですと各定義値 Define.MAX_LENGTH といった形で呼び出されており、
呼び出されている箇所の数・種類も多いことから、
Defineクラスの改造にあたっては、Defineクラスを基底クラスとし 改造後は派生クラスを使用することが
困難(ソースコード変更量が多すぎるため、現実的な実装ではない) と考えています。

C#

1public static class Define 2{ 3 enum EType 4 { 5 A, 6 B, 7 C, 8 All 9 }; 10 11 // ここで多数の定数定義が行われている   12 public const int MAX_LENGTH = 256; 13 public const string XXX= "XXXX"; 14 15 ・・・ 16}

呼び出し元に影響を与えないよう、Defineクラスを使用するものとし
Defineクラス内部で 2つ以上の基底クラスを継承させ
enum型の定義をそれぞれから呼び出して書き換えることはできないでしょうか?


補足 やりたいことのイメージですが、下記にサンプルコードを示します。
基底クラス Person を作り、派生クラス Employeeを作ります。
それぞれのクラスで Season という 同名の enum を用意します。

   各クラス内部で それぞれに作成したSeason を使うことはできるのですが、
基底クラス・派生クラスの 呼び出し元で インスタンスを使って Seasonを指定することができないとわかりました。

   上記のような実装をしたい場合の解決案について教えていただけないでしょうか。

   

C#

1 public partial class Form1 : Form // 実際の開発では、Defineクラス で使用する予定 2 { 3 public Form1() 4 { 5 InitializeComponent(); 6 } 7 8 private void btn_Click(object sender, EventArgs e) 9 { 10 Button btn = (Button)sender; 11 12 Person p; 13 14 if (btn.Name == "button1") 15 { 16 textBox1.Text = ((int)Person.Season.Max).ToString(); // ここでクラス名を指定しなければならない 17 } 18 else 19 { 20 textBox1.Text = ((int)Employee.Season.Max).ToString();// 同上 21 } 22  } 23 } 24 25 class Person //基底クラス 26 { 27 public enum Season 28 { 29 Spring, 30 Summer, 31 Autumn, 32 Winter, 33 Max 34 } 35 } 36 37 partial class Employee : Person // 派生クラス 38 { 39 string syumi; 40 string[] strSeason = { "春", "夏", "秋", "冬" }; 41 42 public new enum Season // 派生クラスのenumは明示的にnewを付ける(うっかり継承先に同名メソッドを記述していないことを明記) 43 { 44 Spring, 45 Summer, 46 Autumn, 47 Winter, 48 Max = Spring + 1 49 } 50 }

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2021/04/21 02:17 編集

> Defineクラス内部で 2つ以上の基底クラスを継承させ > enum型の定義をそれぞれから呼び出して書き換えることはできないでしょうか? ちょっと意味が良く分からないです。そもそも、enumは書き換えも継承も出来ません。 コンパイルは通らないとして、どういう事がしたいかのイメージっぽいコードを質問に追記してみては。 元になるC++のコードがあるなら、それでもいいですし。
fana

2021/04/21 02:18

やりたいことがわからないです(Defineはstaticクラスなのでそもそも継承できないでしょうし). 何を達成したいのでしょうか? Define.ETypeの各フィールド値をどうしたいのですか? それらフィールド値を使用する側はどのようにして使用する想定なのですか?
fana

2021/04/21 02:47

「呼び出し元」は,Define.ETypeのフィールドを用いる部分で, 「Define.EType.A」 と記述している.この記述は変えたくない. で,この Define.EType.A の値を,各呼び出し元で異なる値にしたい? (仮に,数パターンの値の候補を用意できたとして,各所に記述されている「Define.EType.A」がそのどれを取得する記述なのかを何が決定するのか…?)
Taffy

2021/04/21 03:55

>「呼び出し元」は,Define.ETypeのフィールドを用いる部分で, >「Define.EType.A」 と記述している.この記述は変えたくない. → その通りです。   Define.EType.A の値を,各呼び出し元で異なる値としたいと考えています。 各所に記述されている 「Define.EType.A 」が、そのどれを取得するかを決める方法ですが、 configファイルにパラメータを追加するため、その値で判別することを考えています。
gentaro

2021/04/21 04:53

スパゲッティコードの作り方を質問してるようにしか見えない…。そもそも継承元・継承先で同名の異なる列挙を定義するとか暴挙でしかない。 列挙の意味が異なる時点で最低限名前を分けないと混乱するし、所属するクラスによって異なる値が必要になるなら、列挙をそのまま公開するんじゃなくて、最終的に使いたい値を返すプロパティにすれば良いだけ。 「元のコードが云々」ってのは言い訳にすぎない。本来リファクタリングが先にあるべきだし、百歩譲ってその部分に手を付けられないなら、既存コードを無視して別の独立した「綺麗な」実装をしておいて、後から残りの部分を統合することを検討した方が良い。
BluOxy

2021/04/21 06:19

enum に new をつけて上書きするのも非現実的な実装だと思います。なぜ Define.EType の定義を書き替えたいと考えたのでしょうか。根本の問題を記載すると別解があるかもしれません。少なくとも、enum の定義は動的に書き換えることはできないので、現在の質問内容ではそれ以上の回答ができません。
Taffy

2021/04/21 07:33

なぜ Define.EType の定義を書き替えたいと考えたのか ですが Define.EType.All で 定義の件数を取るようにしています。 (例えば、ループ処理で for(int i=0; i<(int)Define.EType.All; i++) といったように 使用しています。) 改造で定義の数が減るとき、通常ならば Define.EType = A + 1 などに 書き換えると思いますが、そうするとパラメータ切替のみで 改造前のソフトウェアとの互換性が取れなくなるため、この方法を取ることができませんでした。 そもそも、forループにenumの定義を使用しているのが良くないのかもしれません。配列のサイズならばまだマシだったのかもしれません。 定義の件数を管理するプロパティをDefineクラスの中に定義しておき ( public static 型のプロパティ{get; private set} ) Defineクラスのコンストラクタで パラメータの値に応じて (int)Define.EType.All にするか ((int)Define.A) + 1 にするか分け、すべてのループで使われている (int)Define.EType.All を新しく作成したプロパティの値にしようかと考えています。
退会済みユーザー

退会済みユーザー

2021/04/21 07:47 編集

一応回答に対するコメントは付けて貰えませんかね。 (方法が使えるか使えないか、何故使えないか)
fana

2021/04/21 08:08

質問文前半のDefineクラスの話と,後半の補足のPersonとEmployeeの話との関係性が(自分の頭が足りてないせいか)どうにもわからない… 前者は static クラスだからそもそも継承の余地は無いんじゃないかと思うので,後者の継承関係がある状態における話との繋がりが「?」な感じ,というか…
guest

回答2

0

Defineクラス内部で 2つ以上の基底クラスを継承させ

enum型の定義をそれぞれから呼び出して書き換えることはできないでしょうか?

できません。
radianさんの回答にもある通り、enumの使用はやめましょう。
下記のような定義にしても enum の時と同様の値を取得できます。ただし enum のメソッドは呼び出せません。

C#

1public class EType { 2 public EType (int a, int b, int c, int all) { 3 A = a; 4 B = b; 5 C = c; 6 All = all; 7 } 8 9 public int A { get; } 10 public int B { get; } 11 public int C { get; } 12 public int All { get; } 13}

個人的には Define クラスが問題だと思います。それ等の定数をそのアプリケーションで利用する目的は何でしょうか。その目的がそのまま命名になるべきです。例えば、config ファイルからロードした値をメモリに保持したいという目的があるなら、私なら ApplicationConfig のような命名にするかもしれません。これでも役割が広すぎる名前なのでそのアプリケーションの業務処理に従ってもう少し詳細の名前をつけると思います。

Define クラスを static にするのはやめて下記のようにインターフェースを利用するとどうでしょう。

C#

1public interface IApplicationConfig 2{ 3 EType EType { get; } 4 int MAX_LENGTH { get; } 5 string XXX { get: } 6} 7 8public class OldApplicationConfig : IApplicationConfig 9{ 10 public EType { get; } = new EType(0,1,2,3); 11 public int MAX_LENGTH { get; } = 256; 12 public string XXX { get; } = "XXXX"; 13} 14 15public class NewApplicationConfig : IApplicationConfig 16{ 17 public EType { get; } = new EType(0,1,2,4); 18 public int MAX_LENGTH { get; } = 1024; 19 public string XXX { get; } = "XXYY"; 20}

インターフェースに記述したメンバーは宣言をしただけで、実装は何も記述されていません。一方、そのインターフェースを実装したクラスではインターフェースの定義に沿った実装をしています。

Form1 クラスで EType が必要と仮定しましょう。Form1 クラスで EType プロパティを定義します。
EType の状態は IApplicationConfig を実装したクラスから取得します。

Form1 クラスは IApplicationConfig 実装したクラスの実体など知りたくありませんから、コンストラクタから注入されてくるようにします。

C#

1public partial class Form1 : Form  // 実際の開発では、Defineクラス で使用する予定 2{ 3 private readonly IApplicationConfig _config; 4 public EType EType => _config.EType; 5 6 public Form1 (IApplicationConfig config) { 7 _config = config; 8 InitializeComponent (); 9 } 10 11 private void btn_Click (object sender, EventArgs e) { 12 Button btn = (Button) sender; 13 if (btn.Name == "button1") { 14 textBox1.Text = EType.All.ToString (); 15 } else { 16 textBox1.Text = EType.B.ToString (); 17 }  18 } 19}

あとはアプリケーションの都合に応じて注入する実体を指定します。

C#

1public class Program { 2 /// <summary> 3 /// アプリケーションのメイン エントリ ポイントです。 4 /// </summary> 5 [STAThread] 6 static void Main () { 7 Application.EnableVisualStyles (); 8 Application.SetCompatibleTextRenderingDefault (false); 9 10#if 改修前 11 Application.Run (new Form1 (new OldApplicationConfig())); 12#else 13 Application.Run (new Form1 (new NewApplicationConfig())); 14#endif 15 } 16}

このように制作することでアプリケーション都合の状態(IApplicationConflig を実装したクラスの実装)を修正するだけでよくなり、

ソースコード変更量が多すぎるため、現実的な実装ではない

という問題を回避することができます。

投稿2021/04/22 02:29

編集2021/04/23 01:08
BluOxy

総合スコア2663

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

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

0

enumをやめて、ValueObjectにしてみるとか。

enum 型の代わりに Enumeration クラスを使用する

投稿2021/04/21 02:48

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

fana

2021/04/21 03:04

(enumとして公開されてて既にそれを利用している個所がある状態だと, それがenumであることに依存したコードを書いてる可能性はないのかな…? とか)
退会済みユーザー

退会済みユーザー

2021/04/21 03:15

演算子のオーバーロードとかで解決出来るかもしれませんが、その辺は何とも言えないですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

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

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

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問