2016-05-30 4 views
0

抽象ゲッターGetValueで基本クラスCommandを宣言したいと思います。次に、これをたとえばTalkCommandSpendCommandにサブクラス化します。C#のサブクラスに依存する動的な戻り値の型?

どちらのクラスは、しかし、私は(何を話を?)GetValueためstringタイプを返すようにTalkCommandにしたいと私はSpendCommandintタイプを返したい、GetValueを実装する必要があります。 (いくらですか?)

Commandベースクラスでこれを書く方法はわかりません。この設計はC#で可能ですか?

答えて

3

In addition to Manfred's answer,今後のヒントをご紹介したいと思います。

ジェネリック医薬品はあなたにGetValue戻り値の型を定義するための貴重なツールを提供しているが、次のアップキャストを実行することができwouln't:代わりに、抽象クラスとして、あなたのコマンドを定義する

abstract class Command<T> 
{ 
    public abstract T GetValue(); 
} 

class TalkCommand : Command<string> 
{ 
    public override string GetValue() 
    { 
     return "Test"; 
    } 
} 

// TalkCommand isn't Command<object> 
Command<object> cmd = new TalkCommand(); 

、私は定義を開始でしょうcovarianceとのインタフェースとして、それ:

public interface ICommand<out T> 
{ 
    T GetValue(); 
} 

全体抽象クラスはICommand<out T>を実現することができます。

abstract class Command<T> : ICommand<T> 
{ 
    public abstract T GetValue(); 
} 

は、今あなたがICommand<object>ICommand<string>の実装をアップキャストすることができます:あなたは二つの世界のベストを引き出すため

ICommand<string> cmd = new TalkCommand(); 
ICommand<object> cmd2 = cmd; 

をこれは、良いです:

  • あなたのコマンドは、強力な戻り値の型を持っています。
  • コマンドは一般的にICommand<object>の実装として扱うことができ、これはGetValueと呼び出し、objectを返します。提供する汎用引数がわからないか、まったく必要ない場合があります。
1

基本クラスCommandが汎用である場合、またはobjectで作業しても構いません。あなたは常に知られているインスタンスタイプで作業する場合、データが入力すると、あなたがキャストせずに必要なことができるので

一般的な例

は、これが最善のアプローチとなります。

abstract class Command<T> 
{ 
    public abstract T GetValue(); 
} 

class TalkCommand : Command<string> 
{ 
    public override string GetValue() 
    { 
     return "Test"; 
    } 
} 

class SpendCommand : Command<int> 
{ 
    public override int GetValue() 
    { 
     return 1234; 
    } 
} 

オブジェクトの例

これは、WPFはIListまたはIEnumerableようDependencyPropertiesと非ジェネリックコレクションで何をするかはかなり、非常に普遍的なアプローチです。

abstract class Command 
{ 
    public abstract object GetValue(); 
} 

class TalkCommand : Command 
{ 
    public override object GetValue() 
    { 
     return "Test"; 
    } 
} 

class SpendCommand : Command 
{ 
    public override object GetValue() 
    { 
     return 1234; 
    } 
} 

ハイブリッド例

あなたは両方を使用することができるようにしたい場合は、非ジェネリック基本クラスを必要とし、そこから別の抽象ジェネリッククラスを継承します。

abstract class Command 
{ 
    public abstract object GetValue(); 
} 

abstract class Command<T> : Command 
{ 
    public T GetTypedValue() 
    { 
     return (T)GetValue(); 
    } 
} 

class TalkCommand : Command<string> 
{ 
    public override object GetValue() 
    { 
     return "Test"; 
    } 
} 

class SpendCommand : Command<int> 
{ 
    public override object GetValue() 
    { 
     return 1234; 
    } 
} 
+0

逆の方向に進むことができます。 'Command 'は*主実装*であり、 'Command'ジェネリック型パラメータなしは' Command 'の派生クラスです。そうすれば、コマンドの一般的な使用は、オブジェクトから 'T 'への不必要なキャストを避けることができます –

+0

@MatíasFidemraizer私は同意しません。 'コマンド'をタイプ( 'T')を知らずに使用する必要があるとき、' Command 'を作成することはできませんが、' Command'から継承しているため、その中で定義されているすべての関数を使用できます。 (IEnumerable :IEnumerable'など) –

+0

これはプロジェクトにも依存するかもしれません...おそらく私は間違っていますが、共分散/逆ジェネリックではこれらの能力があるので、ジェネリックスが時間の無駄で複雑すぎると思っている場合を除き、クラス継承を使う代わりに解決できるものよりも多くの問題がある(私はあなたについて話していない、私はジェネリックプログラミングが大好きです!) –

2

戻り値の型がサブクラスによって異なる必要がある場合は、このメソッドを基底クラスに持たない方がよいでしょう。

基本型の背後にあるアイデアは、すべてのサブクラスを統一的に処理できるようにすることです。 1つは書くことができるはずです

Command cmd = ... // One of subclasses 
<some-type> val = cmd.GetValue(); 

<some-type>を実際のタイプに置き換えてください。しかし、あなたのケースでは、とstringにはobject以外の共通の基底型はありません。したがって、異なる戻り型を持つことは無意味になります。

あなたはこのように、一般的なようGetValueを実装できます。

abstract class Command { 
    abstract T GetValue<T>(); 
    ... 
} 

それは、基本クラスでGetValueを持つことの目的を倒すことになる、GetValueの型パラメータとして所望のタイプを渡すために、発信者を必要とします。

可能な修正:発信者が型を知っているから離れて滞在することができます訪問者です一つのアプローチ - このようなCommand「プッシュ」の呼び出し元に値、することができますインターフェイス作る:今

IValueGetter { 
    void SetInt(int v); 
    void SetString(string v); 
} 

abstract class Command { 
    abstract void GetValue(IValueGetter g); 
} 
class TalkCommand { 
    override void GetValue(IValueGetter g) { 
     g.SetString("hello"); 
    } 
} 
class BuyCommand { 
    override void GetValue(IValueGetter g) { 
     g.SetInt(123); 
    } 
} 

を呼び出し元はの種類を知らずにCommandと対話できます。 intを生成するコマンドはSetIntをコールし、stringを生成するコマンドはSetStringをコールします。

+0

ジェネリックが.NETにヒットする前に、このアプローチは有効ではありませんか?:\ –

+0

@MatíasFidemraizer "可能な修正"アプローチは間違いなく有効です。 – dasblinkenlight

+0

まあ、一日の終わりには、同じ問題に対する多くの有効な解決策があります。ところで、私はあなたのアプローチはいくつかの問題があると信じています。たとえば、新しいタイプの値を設定する必要があるときは、インターフェイスを拡張し、新しい 'SetXXX'メソッド全体を実装する必要があります。結局のところ、これはジェネリックで簡単に解決できませんか? –