2017-03-27 3 views
3

私は、次がある場合:ジェネリッククラスPolymorphisim

public abstract class Parameter<T> 
{ 
    protected T value; 

    public virtual T Value 
    { 
     get { return value; } 
     set { this.value = value; } 
    } 

    protected Parameter(T startingValue) 
    { 
     value = startingValue; 
    } 
} 

public class FloatParameter : Parameter<float> 
{ 
    public FloatParameter(float startingValue) : base(startingValue){} 
} 

public class IntParameter : Parameter<int> 
{ 
    public override int Value 
    { 
     get { return value; } 
     set { this.value = value > 100 ? 100 : value; } 
    } 

    public IntParameter(int startingValue) : base (startingValue) {} 
} 

派生型のいずれかを含むことができ、いくつかのList<Parameter>を作成する方法はありますか?たとえば、次のようなものがあります。

// no type specified in Parameter 
List<Parameter> storedParameters = new List<Parameter>(); 
storedParameters.Add(new FloatParameter(2f)); 
storedParameters.Add(new IntParameter(7)); 

foreach(Parameter p in storedParameters) 
{ 
    DoSomethingWithValue(p.Value); 
} 

また、この実装に欠陥がある場合は、これを行うより良い方法がありますか?私がここにいるのは、やや素朴な感じです。

+0

あなたがオレンジのリストでマンゴーを保持したい理由は? –

+0

あなたの例では 'Value'にはどのようなタイプがありますか? –

+0

この実装の問題は、使用しようとしている2つのタイプです。 Intとfloatは構造体です。つまり、ひとつにTを代入すれば、もう一方を追加することはできません。 – Weggo

答えて

3

私は、このようなケースを管理するために見る唯一の方法は持っていて、一般的なタイプを管理するために使用するインタフェース、このようなものが動作するはずです:

public interface IParameter 
{ 
    void DoSomething(); 
} 

public abstract class Parameter<T> 
{ 
    protected T value; 

    public T Value 
    { 
     get { return value; } 
     set { this.value = value; } 
    } 

    protected Parameter(T startingValue) 
    { 
     value = startingValue; 
    } 
} 

public class FloatParameter : Parameter<float>, IParameter 
{ 
    public FloatParameter(float startingValue) : base(startingValue) { } 
    public void DoSomething() 
    { 
     Console.WriteLine(value); 
    } 
} 

public class IntParameter : Parameter<int>, IParameter 
{ 
    public IntParameter(int startingValue) : base(startingValue) { } 

    public void DoSomething() 
    { 
     Console.WriteLine(value); 
    } 
} 

彼の場合オンタリオあなたが作成することができるだろうインターフェイスIPパラメータのリストを追加して、特定のインスタンスを追加します。

var list = new List<IParameter>(); 
list.Add(new FloatParameter(1F)); 
list.Add(new IntParameter(1)); 

foreach (var item in list) 
{ 
     item.DoSomething(); 
} 
+0

これはこれを行うには本当に良い方法です。しかし、 'item' foreach変数から' Parameter'オブジェクトのプロパティにアクセスする方法を考えることができますか? –

+0

@DannyHerbertそれはできません。コンパイラは、どのような特定の型がリストに格納されているかを知ることはできません(実際には、プログラムが実行されるまで利用できないユーザー入力に依存する可能性があります)。したがって、実際にそこにいることを知ることができないため、より特定のタイプのプロパティへのアクセスを提供する方法はありません。 – Kyle

2

ノンエネリックインターフェイスを追加するようにしてください。代わりに、動的および削除の使用目的:UPDATED

class Program 
{ 
    static void Main(string[] args) 
    { 

     // no type specified in Parameter 
     var storedParameters = new List<ParameterBase>(); 
     storedParameters.Add(new FloatParameter(3.5F)); 
     storedParameters.Add(new IntParameter(7)); 

     foreach (var p in storedParameters) 
     { 
      Console.WriteLine(p.Value); 
     } 
    } 
} 

public class ParameterBase 
{ 
    protected object value; 

    public virtual object Value 
    { 
     get { return value; } 
     set { this.value = value; } 
    } 
} 

public class FloatParameter : ParameterBase 
{ 
    public FloatParameter(float value) 
    { 
     Value = value; 
    } 
} 

public class IntParameter : ParameterBase 
{ 
    public IntParameter(int value) 
    { 
     Value = value; 
    } 
} 

:あなたがオブジェクトに値を変更した場合

public class Program 
{ 
    static void Main(string[] args) 
    {   
     try 
     { 
      List<IParameter> storedParameters = new List<IParameter>(); 
      storedParameters.Add(new FloatParameter(2f)); 
      storedParameters.Add(new IntParameter(7)); 

      foreach (IParameter p in storedParameters) 
      { 
       Console.WriteLine(p.ToString()); 
      } 

     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
     } 
    } 
} 

public interface IParameter 
{ 
    object value { get; } 
} 

public class Parameter<T> : IParameter 
{ 
    public object value { get; protected set; } 

    public virtual T Value 
    { 
     get { return (T)value; } 
     set { this.value = value; } 
    } 


    protected Parameter(T startingValue) 
    { 
     value = startingValue; 
    } 
} 

public class FloatParameter : Parameter<float> 
{ 
    public FloatParameter(float startingValue) : base(startingValue){ } 
} 

public class IntParameter : Parameter<int> 
{ 
    public override int Value 
    { 
     get { return (int)value; } 
     set { this.value = value > 100 ? 100 : value; } 
    } 

    public IntParameter(int startingValue) : base (startingValue) { } 
} 
+2

プリミティブ 'T'を' object'フィールドに格納するには、 'T Value'プロパティでアクセスするたびにboxing/unboxingが必要です。また、 'IParameter'を明示的に実装する場合、同じプロパティ名を使用することもできます。 –

1

はあなたが好きなタイプに値を設定することができるようになります。次に例を示します。 @Pieter Witvoetが提案したValueType

+3

なぜ 'object'を使わないのですか? 'dynamic'は主に動的言語interopのためのものであり、追加コスト(late binding)があります。 –

+0

ありがとう@Pieter Witvoet、私はオブジェクトを使用するコードを更新しました:) – Weggo

+0

別のもの:なぜ型を別に設定する必要がありますか?これは簡単に悪用される可能性があります: 'Value =" hello "; ValueType = typeof(int); '。しかし、なぜそれをすべて保管していますか? 'IntParameter'は常に' typeof(int) 'を返さなければなりません。なぜなら、基本クラスのプロパティをabstractにして、派生したそれぞれの' Parameter'クラスでそれをオーバーライドするのはなぜですか?どのように 'IntParameter'と' FloatParameter'の間に少しの違いがあるのか​​注意してください - 代わりに 'Parameter 'クラス( 'ValueType'が' typeof(T) 'を返す)を使うことができます。 –

1

いいえ、できません。

しようとしているのは、未定義型のプロパティを公開するインターフェイス(または基本クラス)を用意し、その値を取得して適切なオーバーライドに動的にディスパッチできるようにすることです。DoSomethingWithValue

genericsを使用する代わりに、プロパティをdynamicとして定義することができます。

public class Parameter 
{ 
    protected dynamic value; 

    public dynamic Value 
    { 
     get { return value; } 
     set { this.value = value; } 
    } 

    public Parameter(dynamic startingValue) 
    { 
     value = startingValue; 
    } 
} 

public class MyStuff { 
    public void DoStuff() 
    { 
     List<Parameter> storedParameters = new List<Parameter>(); 
     storedParameters.Add(new Parameter(2f)); 
     storedParameters.Add(new Parameter(7)); 

     foreach (Parameter p in storedParameters) 
     { 
      DoSomethingWithValue(p.Value); 
     } 
    } 
} 

そうでない場合は、Double dispatchを実装する必要があります。

1

共通インターフェイスを定義し、ビジターパターンを使用することで、これを実行できます。

public interface IParameterVisitor 
{ 
    void VisitInt(int value); 
    void VisitFloat(float value); 
} 

public interface IParameter 
{ 
    void Accept(IParameterVisitor visitor); 
} 

以前の実装は、ビットを修正しなければなりません:

public abstract class Parameter<T> : IParameter 
{ 
    protected T value; 

    public virtual T Value 
    { 
     get { return value; } 
     set { this.value = value; } 
    } 

    protected Parameter(T startingValue) 
    { 
     value = startingValue; 
    } 

    public abstract void Accept(IParameterVisitor visitor); 
} 

FloatParameter意志VisitFloat、およびIntParameterますVisitInt

public class FloatParameter : Parameter<float> 
{ 
    public FloatParameter(float startingValue) : base(startingValue) { } 
    public override void Accept(IParameterVisitor visitor) 
    { 
     visitor.VisitFloat(this.value); 
    } 
} 

public class IntParameter : Parameter<int> 
{ 
    public override int Value 
    { 
     get { return value; } 
     set { this.value = value > 100 ? 100 : value; } 
    } 

    public override void Accept(IParameterVisitor visitor) 
    { 
     visitor.VisitInt(this.value); 
    } 

    public IntParameter(int startingValue) : base(startingValue) { } 
} 

例えば私たちの訪問者:

public class MyVisitor : IParameterVisitor 
{ 
    public void VisitInt(int value) 
    { 
     Console.WriteLine($"Visiting an int: {value}"); 
    } 

    public void VisitFloat(float value) 
    { 
     Console.WriteLine($"Visiting a float: {value}"); 
    } 
} 

最後に、用法:

var parameters = 
    new List<IParameter> {new FloatParameter(0.5f), new IntParameter(1)}; 
var visitor = new MyVisitor(); 
foreach (IParameter parameter in parameters) { 
    parameter.Accept(visitor); 
} 
関連する問題