通用类多态性

时间:2017-03-27 11:38:58

标签: c# .net oop generics polymorphism

如果我有以下内容:

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

或者,如果这个实现存在缺陷,有没有更好的方法呢?我在这里感觉有些天真。

5 个答案:

答案 0 :(得分: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的List并添加特定的实例:

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

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

答案 1 :(得分: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 :(得分: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 :(得分: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

答案 4 :(得分: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);
}

FloatParameterVisitFloatIntParameterVisitInt

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