2017-03-27 43 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); 
}