回答編集履歴

5

修正

2024/10/16 02:41

投稿

Manabu
Manabu

スコア60

test CHANGED
@@ -118,7 +118,6 @@
118
118
  最後に迷いましたが原因の正確な切り分けの為に原文を一部訂正します
119
119
 
120
120
  >ジェネリック型であるTをIHogeで制限すると、TがIHogeであることは保障されますが、その子となるクラスの範囲までは確約されません
121
- よってvar hoge=new T()はIHoge hoge=new IHoge()と解釈されます
122
121
 
123
122
  ここは解釈違いで、単純に```T=Hoge```の場合に```CreateInstance```から返される戻り値が```IHoge```であることで、```Hoge```のインスタンスに```IHoge```を代入する形となって発生する変換エラーでした
124
123
  よって```IHoge```を```T(Hoge)```に変換することが出来ず、またそれ故にキャストによってエラーが回避されます

4

訂正

2024/10/16 02:39

投稿

Manabu
Manabu

スコア60

test CHANGED
@@ -113,3 +113,41 @@
113
113
  静的メソッドを利用することで、余計なインスタンス化を抑止します
114
114
 
115
115
  ```IHoge```のみを実装する型とそれ以外、すなはち**必要な実装を伴う型**とを明確に区別する為に、ここでは```FactoryIHoge```に```IHoge```を実装していません
116
+
117
+ # 捕捉
118
+ 最後に迷いましたが原因の正確な切り分けの為に原文を一部訂正します
119
+
120
+ >ジェネリック型であるTをIHogeで制限すると、TがIHogeであることは保障されますが、その子となるクラスの範囲までは確約されません
121
+ よってvar hoge=new T()はIHoge hoge=new IHoge()と解釈されます
122
+
123
+ ここは解釈違いで、単純に```T=Hoge```の場合に```CreateInstance```から返される戻り値が```IHoge```であることで、```Hoge```のインスタンスに```IHoge```を代入する形となって発生する変換エラーでした
124
+ よって```IHoge```を```T(Hoge)```に変換することが出来ず、またそれ故にキャストによってエラーが回避されます
125
+
126
+ しかしダウンキャストでは```IHoge```から必ず目的の派生型に変換できるとは限らないため、危険な操作であることに違いはありません
127
+ そのため原文はそのまま残しておきます
128
+
129
+ 以上を踏まえると、原形のコードは以下の形に修正することも可能です
130
+ ```
131
+ abstract public class HogeUseBase<T> where T : IHoge, new()
132
+ {
133
+ public List<IHoge> hogelist = new List<IHoge>();
134
+
135
+ public void CreateHoge(){
136
+ var hoge = new T();
137
+ hogelist.Add(hoge.CreateInstance());
138
+ }
139
+
140
+ abstract public void UseHoge();
141
+ }
142
+
143
+ public class HogeUse : HogeUseBase<Hoge>
144
+ {
145
+ override public void UseHoge(){
146
+ foreach(Hoge h in this.hogelist){
147
+ Console.Write(h.Prop1);
148
+ }
149
+ }
150
+ }
151
+ ```
152
+ ```foreach```の暗黙的なダウンキャストについては冒頭で説明した通りです
153
+

3

追加の提案

2024/10/16 01:41

投稿

Manabu
Manabu

スコア60

test CHANGED
@@ -66,3 +66,50 @@
66
66
  ジェネリック型のダウンキャストでは基本的にこの手法を採るようにすると安全です
67
67
 
68
68
  うっかり最新版準拠のコードを貼ってしまったので書き直しました
69
+
70
+ # 多重継承デザイン
71
+ インスタンス化を回避したいという旨に沿い、こちらの方法も考えました
72
+ ジェネリック型を多重継承仕様で制約することにより、実質的に実装を強制するパターンです
73
+
74
+ メソッドを分離するための基底型を定義し、インターフェイスと併せて派生型に継承させます
75
+ これにより、ジェネリック型で受け付けるクラスの性質を特定の静的メソッドを実装した型に制限します
76
+
77
+ ```
78
+ public interface IHoge{
79
+ }
80
+
81
+ public abstract class FactoryIHoge{
82
+ public static T CreateInstance<T>() where T:IHoge,new()
83
+ =>new T();
84
+ }
85
+
86
+ public class Hoge : FactoryIHoge,IHoge
87
+ {
88
+ public string Prop1 { get; }="Success!";
89
+ }
90
+
91
+ public class Hoge2 : FactoryIHoge,IHoge
92
+ {
93
+ }
94
+
95
+ abstract public class HogeUseBase<T> where T:FactoryIHoge,IHoge,new()
96
+ {
97
+ public List<T> hogelist = new List<T>();
98
+
99
+ public void CreateHoge()
100
+ =>hogelist.Add(FactoryIHoge.CreateInstance<T>());
101
+
102
+ abstract public void UseHoge();
103
+ }
104
+
105
+ public class HogeUse : HogeUseBase<Hoge>
106
+ {
107
+ override public void UseHoge(){
108
+ foreach(var h in this.hogelist)
109
+ Console.WriteLine(h.Prop1);
110
+ }
111
+ }
112
+ ```
113
+ 静的メソッドを利用することで、余計なインスタンス化を抑止します
114
+
115
+ ```IHoge```のみを実装する型とそれ以外、すなはち**必要な実装を伴う型**とを明確に区別する為に、ここでは```FactoryIHoge```に```IHoge```を実装していません

2

コードを修正

2024/10/15 16:39

投稿

Manabu
Manabu

スコア60

test CHANGED
@@ -8,10 +8,16 @@
8
8
  ```:C#
9
9
  using System;
10
10
  using System.Collections.Generic;
11
+
11
-
12
+ public class Program
13
+ {
14
+ public static void Main()
15
+ {
12
- var hoge=new HogeUse();
16
+ var hoge=new HogeUse();
13
- hoge.CreateHoge();
17
+ hoge.CreateHoge();
14
- hoge.UseHoge();
18
+ hoge.UseHoge();
19
+ }
20
+ }
15
21
 
16
22
  public interface IHoge<T> where T:IHoge<T>,new()
17
23
  {
@@ -20,7 +26,7 @@
20
26
 
21
27
  public class Hoge : IHoge<Hoge>
22
28
  {
23
- public string? Prop1 { get; }="Success!";
29
+ public string Prop1 { get; }="Success!";
24
30
 
25
31
  public Hoge CreateInstance()=>new Hoge();
26
32
  }
@@ -59,3 +65,4 @@
59
65
 
60
66
  ジェネリック型のダウンキャストでは基本的にこの手法を採るようにすると安全です
61
67
 
68
+ うっかり最新版準拠のコードを貼ってしまったので書き直しました

1

補足を追加

2024/10/15 16:30

投稿

Manabu
Manabu

スコア60

test CHANGED
@@ -53,4 +53,9 @@
53
53
  ```:Console
54
54
  Success!
55
55
  ```
56
+ 因みに```foreach```には**暗黙のダウンキャスト**が備わっており、基底クラスから**任意のサブクラス**へ動的にキャストしてくれます
56
57
 
58
+ そのためジェネリック型を返す基底型のメソッドを、任意の派生型を返すメソッドとしてサブクラスでオーバーライドしても、**共変戻り値**という機能によって問題なく実装でき、かつそれによって与えられたインスタンスの型を```foreach```が暗黙に検出してくれます
59
+
60
+ ジェネリック型のダウンキャストでは基本的にこの手法を採るようにすると安全です
61
+