C# です。
型 (is) で分岐すると到達不能になってしまうことがあります。
class Program
{
abstract class B {}
class D1 : B { public void Guitar() { Console.WriteLine("guitar"); } }
class D2 : B { public void Baseball() { Console.WriteLine("baseball"); } }
class D3 : D1 { public void Cards() { Console.WriteLine("cards"); } }
static void play(IEnumerable<B> b)
{
foreach (var d in b)
{
if (d is D1) { ((D1)d).Guitar(); }
else if (d is D2) { ((D2)d).Baseball(); }
else if (d is D3) { ((D3)d).Cards(); /* unreachable */ }
}
}
}
class B に virtual な Play() を追加して D1, D2, D3 で override すればいいのですが、B, D1, D2, D3 を変更できない場合には、どのようなテクニックがあるでしょうか。
教えてください。
http://rextester.com/VYLYMV11858
A 回答 (6件)
- 最新から表示
- 回答順に表示
No.6
- 回答日時:
なんかよく分かりませんが、ただラップしたいならインターフェースとラップクラスを用意すればできますよね。
using System;
using System.Collections.Generic;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var a = new IWrap[] { new W1(), new W2(), new W3() };
Console.WriteLine("ふつう");
foreach (var b in a)
{
b.Play();
}
Console.WriteLine("メソッド");
ConsoleApp1.Program.play(a);
Console.Read();
}
static void play(IEnumerable<IWrap> b)
{
foreach (var d in b)
{
d.Play();
}
}
// START 変更不可クラス群
abstract class B
{
}
class D1 : B
{
public void Guitar()
{
Console.WriteLine("guitar");
}
}
class D2 : B
{
public void Baseball()
{
Console.WriteLine("baseball");
}
}
class D3 : D1
{
public void Cards()
{
Console.WriteLine("cards");
}
}
// END 変更不可クラス群
// START ラップ用クラス群
interface IWrap
{
void Play();
}
class W1 : IWrap
{
public void Play()
{
var d = new D1();
d.Guitar();
}
}
class W2 : IWrap
{
public void Play()
{
var d = new D2();
d.Baseball();
}
}
class W3 : IWrap
{
public void Play()
{
var d = new D3();
d.Cards();
}
}
// END ラップ用クラス群
}
}
何百とあるクラスのラップクラスを用意するのが面倒だから、当該クラスのラップクラスがないにも関わらずラップクラスとして取得したいという話ならラップ云々の話ではないと思います。
もはや元々の質問と異なるので、一度締めて再度正確な質問を改めて投稿した方がよいでしょう。
どういうクラスの実装は一切変更できず、どういうラッパークラスを用意して、何が問題なのかを実際のコードで具体的に。
また、VSの質問ならば、ここではなくMSDNフォーラム(https://social.msdn.microsoft.com/forums/ja-jp/h …で質問(カテゴリ:Visual Studio Development → Visual C#)で質問した方が有識者からのアドバイスは得易いかと思います。
No.5
- 回答日時:
ついでに。
よくわかりませんが、例えば、D1, D2, D3というようなクラスが大量にあるとするならば、すでに回答があるように、基本的にはAdapterパターンのようにラッパークラスを用意して、それを処理するか(http://qiita.com/shoheiyokoyama/items/bd1c692db4 …)、特定のクラスまたはメソッド内で現行動作のように、if()やswitch()を利用して各クラスおよびメソッドを把握して実行するかのどちらかしかないでしょう。
んで、そういう話ではなくて、D1, D2, D3クラスであるかどうか、そのメソッドはなんなのか、を把握したくないという話なら、ルールが決まっているならメソッドを直接叩くという荒業もあるでしょう。
以下コードは、条件に合致する場合にうまく動作します。
スマートなコードかどうかは分かりませんが・・・。
合致するものが2件以上ある場合は1件目を正として実行します。
【条件】
・publicである。
・abstractでない。
・引数がない。
・対象クラス内で初めて定義されたメソッドである。
・対象クラスに、上記条件のメソッドは必ず1つである。
using System;
using System.Collections.Generic;
using System.Linq;
namespace Rextester
{
class Program
{
static void Main(string[] args)
{
play(new B[] { new D1(), new D2(), new D3(), });
Console.Read();
}
abstract class B { }
class D1 : B { public void Guitar() { Console.WriteLine("guitar"); } }
class D2 : B { public void Baseball() { Console.WriteLine("baseball"); } }
class D3 : D1 { public void Cards() { Console.WriteLine("cards"); } }
static void play(IEnumerable<B> b)
{
foreach (var d in b)
{
var methods = d.GetType().GetMethods().Where(
x => x.IsPublic &&
!x.IsAbstract &&
x.GetParameters().Length == 0 &&
x.DeclaringType.Name == d.GetType().Name
).Select(x => x);
if (methods.Count() < 1)
{
throw new ArgumentException("呼び出し可能なメソッドがないクラスが引数に設定された");
}
var method = methods.First();
method.Invoke(d, null);
}
}
}
}
ありがとうございます。問題を整理しました。
(1) B の既知の派生クラス D1, D2, ... のすべてに (B ではない) 共通点 W { Play() } を見出して、アダプターパターン (委譲) で W を D1, D2, ... それぞれに W1 : W { D1 ee }, W2 : W { D2 ee }, ... と実装しました。
(2) D1, D2, ... のインスタンスは (コンポジットパターンで) すべて B として格納されています (ここでは簡単のため IEnumerable<B>)。
(3) D1, D2, ... には継承関係があります。
(4) IEnumerable<B> には未知の (undocumented) クラスがありえますが、既知の (documented) クラス D1, D2, ... のいずれかの派生クラスです。
(5) IEnumerable<B> のそれぞれをそれぞれに最も適した W1, W2, ... でラップした IEnumerable<W> を作りたいです。
No.4
- 回答日時:
//Rextester.Program.Main is the entry point for your code. Don't change it.
//Compiler version 4.0.30319.17929 for Microsoft (R) .NET Framework 4.5
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Rextester
{
public class Program
{
public static void Main(string[] args)
{
play(new B[] { new D1(), new D2(), new D3(), });
}
abstract class B {}
class D1 : B { public void Guitar() { Console.WriteLine("guitar"); } }
class D2 : B { public void Baseball() { Console.WriteLine("baseball"); } }
class D3 : D1 { public void Cards() { Console.WriteLine("cards"); } }
static void play(IEnumerable<B> b)
{
foreach (var d in b)
{
var type = d.GetType();
if (type == typeof(D1)) { ((D1)d).Guitar(); }
else if (type == typeof(D2)) { ((D2)d).Baseball(); }
else if (type == typeof(D3)) { ((D3)d).Cards(); }
}
}
}
}
No.3
- 回答日時:
「あるクラスか、その子孫クラス」ではなく、「ある特定のクラス」と判定する方法あったはず、と調べたところ
GetType が使えるとわかりました
http://ufcpp.net/study/csharp/oo_polymorphism.html
あとは、D1,D2,D3をラップしたwD1,wD2,wD3を作って、playメソッドをオーバーライドする、とか。
ありがとうございます。
実は問題はその適切なラッパーを選択する部分なのです
abstract class W { public abstract void Play(); }
abstract class W<T> : W { public W(T w) { this.EE = w; } public T EE { get; protected set; } }
class W1: W<D1> { public W1(D1 w) : base(w) {} public override void Play() { this.EE.Guitar(); } }
class W2: W<D2> { public W2(D2 w) : base(w) {} public override void Play() { this.EE.Baseball(); } }
class W3: W<D3> { public W3(D3 w) : base(w) {} public override void Play() { this.EE.Cards(); } }
IEnumerable<W> Wrap(IEnumerable<B> b) {
foreach (var d in b) {
if (d is D1) yield return W1((D1)d);
else if (d is D2) yield return W2((D2)d);
else if (d is D3) yield return W3((D3)d);
}
IEnumerable<B> に class D3ex : D3 {} のインスタンスが含まれている場合、それは最適な W3 で包みたいです。
No.2
- 回答日時:
しつこくてごめんなさい。
評価の順番を変えればいいんですもんね。
あと、「型スイッチ」というのがあり、上から順番に評価されるので、
switch( d ) {
case D3 :
;
case D2 :
;
case D1 :
;
}
とやるのも一興かと。
継承関係は調べてませんが、B, D1, D2, ... は百個近くあります。
今、考えているのは、
1. Type.IsSubcalssOf() を半順序として、T4 で if else を並べ替える (B, D1, D2, ... は外部の DLL にあります)。
2. オーバーロード Play(D1 d1), Play(D2 d2), Play(D3 d3) を用意して、実行時コード生成でコンパイラにオーバーロードを選択させる。
3. たぶん 2 と同じことですが、(dynamic) にキャストして実行時にオーバーロードを選択させる。
です。
No.1
- 回答日時:
なるほど、やってみると D3 が D1 でもあり、Bでもあるということですな。
d is D1 → true,
d is D3 → true, と。
であれば
最初の if( d is D1 ) を
if( ( d is D1 ) && !( d is D3 ) ) // D1 だけど D3 じゃないとき
にするとか。
私は馬鹿なので、もうちょっとスマートな書き方があるはずです。
勉強になりました。ありがとうございます。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C#にて別クラスの関数を使いたい
-
Java
-
import と extends について
-
[Android+Javamail]送信できません
-
C# 可変長変数のやりとりについて
-
visual studioのデザイナ画面で...
-
c++,ある関数のクラスから別の...
-
構造 他のクラスの構造体を別...
-
クラス間でのデータ参照
-
Java リフレクションについて
-
C#でほかのファイルにある自作...
-
ClassLoader.getSystemResource...
-
範囲外の数値を代入したらエラ...
-
JAVA、JAVA Scriptについて教え...
-
SwingでgetContentPaneのエラー...
-
Javaコンストラクタthisとsuper...
-
javaでメインクラスが見つから...
-
explicitの定義は?
-
if (is) {} else if (is) {} el...
-
回答が分かりません。どなたか...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
関数内の変数に<summary>コメン...
-
c++,ある関数のクラスから別の...
-
範囲外の数値を代入したらエラ...
-
クラス間でのデータ参照
-
構造 他のクラスの構造体を別...
-
C#にて別クラスの関数を使いたい
-
C#のクラスライブラリでメッセ...
-
C# インターフェイスの実装
-
C++でfriendクラスにしているの...
-
継承したクラス側のクラス名の取得
-
Java リフレクションについて
-
ひとつのファイルにクラスは1つ?
-
オブジェクトのデータをもとにX...
-
親クラスから子クラスへアクセス。
-
visual studioのデザイナ画面で...
-
import と extends について
-
無名パッケージからのインポート
-
Java
-
C#でほかのファイルにある自作...
-
C# log4netの使い方
おすすめ情報