2017-03-27 22 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

這個實現的問題是你正在嘗試使用的兩種類型。 Int和float是struct,這意味着一旦你將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); 
    } 
} 

安大略他的情況下,你將能夠創造一個接口IParameter的名單,並添加有具體事例:

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

嘗試添加非通用接口。這裏有一個例子:

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 Value'屬性訪問,在'object'字段中存儲基元'T'都需要裝箱/取消裝箱。另外,如果明確實現'IParameter',則可以使用相同的屬性名稱。 –

1

如果將值更改爲一個對象,你就可以將值設置爲任何你喜歡的類型:

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; 
    } 
} 

更新:使用對象,而不是動態的,刪除按照@Pieter Witvoet建議的ValueType

+3

爲什麼不使用'object'? 'dynamic'主要用於動態語言互操作,並且附加成本(後期綁定)。 –

+0

感謝@Pieter Witvoet,我更新了代碼以使用對象:) – Weggo

+0

另一件事:爲什麼必須單獨設置類型?這很容易被濫用:'價值=「你好」; ValueType = typeof(int);'。但爲什麼要存儲它呢? 'IntParameter'應該總是返回'typeof(int)',那麼爲什麼不讓基類屬性抽象,並在每個派生'Parameter'類中覆蓋它呢?注意'IntParameter'和'FloatParameter'之間的差別很小 - 你可以使用一個通用的'Parameter '類(當然,ValueType'返回'typeof(T)')。 –

1

不,這是不可能的。

你要做的就是讓一個接口(或基類)公開一個未定義類型的屬性,然後能夠檢索該值並動態地將其分配給DoSomethingWithValue的正確覆蓋。

您可以實現的是將屬性定義爲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); 
} 

FloatParameterVisitFloat,並且IntParameterVisitInt

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); 
}