【问题标题】:Generic constraints with C#C# 的通用约束
【发布时间】:2017-02-26 12:06:11
【问题描述】:

我想创建一个通用(原文如此)解决方案,以拥有一个具有一堆属性的类。这些属性应该是简单类型(bool、int、float 等)或复杂类型(矢量、颜色等)。他们都应该有办法将它们从文本解析为类型。

我将在这里使用的一种复杂类型是 Vector:

public class Vector
{
    private float _x, _y, _z;

    public Vector(float x, float y, float z)
    {
        _x = x;
        _y = y;
        _z = z;
    }

    public Vector() : this(0.0f, 0.0f, 0.0f)
    {

    }

    public float X
    {
        get { return _x; }
        set { _x = value; }
    }

    public float Y
    {
        get { return _y; }
        set { _y = value; }
    }

    public float Z
    {
        get { return _z; }
        set { _z = value; }
    }
}

这是我的参数基类,它只是给它一个名字:

public class Parameter
{
    protected string _name;

    public Parameter() : this("untitled")
    {

    }

    public Parameter(string name)
    {
        _name = name;
    }

    public string Name => _name;
}

这是添加泛型类型 TType 值的派生类,它有一个约束:

public class Parameter<TType> : Parameter where TType : ParameterValue<TType>
{
    public Parameter(string name, TType value) : base(name)
    {
        Value = value;
    }

    public TType Value { get; set; }
}

现在是 ParameterValue 泛型类,它确保所有 TType 对象都有解析函数

public abstract class ParameterValue<TType>
{
    protected TType _value;

    public ParameterValue(TType value)
    {
        _value = value;
    }

    public TType Value
    {
        get { return _value; }
        set { _value = value; }
    }

    public abstract void Parse(string text);
}

下面是字符串的定义:

public class StringValue : ParameterValue<string>
{
    public StringValue(string value) : base(value)
    {

    }

    public override void Parse(string text)
    {
        _value = text;
    }
}

Vector 的定义如下:

public class VectorValue : ParameterValue<Vector>
{
    public VectorValue(Vector value) : base(value)
    {

    }

    public override void Parse(string text)
    {
        var tokens = text.Split(',');

        var x = float.Parse(tokens[0]);
        var y = float.Parse(tokens[1]);
        var z = float.Parse(tokens[2]);

        _value = new Vector(x, y, z);
    }
}

这是我的经理类,其中包含所有参数:

public class ParameterManager
{
    private Dictionary<string, Parameter> _parameters;

    public ParameterManager()
    {
        _parameters = new Dictionary<string, Parameter>();
    }

    public void AddParameter<TType>(string name, TType value) where TType : ParameterValue<TType>
    {
        _parameters[name] = new Parameter<TType>(name, value);
    }

    public TType FindParameterValue<TType>(string name) where TType : ParameterValue<TType>
    {
        var parameter = _parameters[name];
        var parameterTyped = parameter as Parameter<TType>;
        return parameterTyped?.Value;
    }
}

现在,如果我创建一个使用 ParamaterManager 的类,我会遇到问题:

public class Thing
{
    private ParameterManager _parameters;

    public Thing()
    {
        _parameters = new ParameterManager();

        _parameters.AddParameter("name", new StringValue("untitled"));
        _parameters.AddParameter("position", new VectorValue(new Vector()));
    }
}

添加参数“name”和“position”的两行抛出错误:

1>...\Thing.cs(11,13,11,37): error CS0311: The type 'ParameterProblem.StringValue' cannot be used as type parameter 'TType' in the generic type or method 'ParameterManager.AddParameter<TType>(string, TType)'. There is no implicit reference conversion from 'ParameterProblem.StringValue' to 'ParameterProblem.ParameterValue<ParameterProblem.StringValue>'.
1>...\Thing.cs(12,13,12,37): error CS0311: The type 'ParameterProblem.VectorValue' cannot be used as type parameter 'TType' in the generic type or method 'ParameterManager.AddParameter<TType>(string, TType)'. There is no implicit reference conversion from 'ParameterProblem.VectorValue' to 'ParameterProblem.ParameterValue<ParameterProblem.VectorValue>'.

我怎样才能让它做我想做的事?

【问题讨论】:

    标签: c# generics constraints


    【解决方案1】:
    where TType : ParameterValue<TType>
    

    这是一个递归通用约束,它将简化为 TType : ParameterValue&lt;XYZParameterValue&gt; 其中 XYZParameterValue : ParameterValue&lt;TType&gt; 这不是您想要的,因为在您的情况下实际类型(例如 string确实 不是 继承 其对应的ParameterValue (ParameterValue&lt;string&gt;)。

    您的通用约束在使用由 其通用的相同类型实现/继承的通用接口/基类时起作用,例如由该类型实现的 IComparable&lt;T&gt; 接口T(即System.String : IComparable&lt;System.String&gt;)。

    相反,我会执行以下操作:

    public class Parameter<T> : Parameter
    {
        public Parameter(string name, ParameterValue<T> value) : base(name)
        {
            Value = value;
        }
    
        public ParameterValue<T> Value { get; set; }
    }
    

    您也必须将 ParameterManager 方法更改为类似的形式:

    public void AddParameter<T>(string name, ParameterValue<T> value)
    {
        _parameters[name] = new Parameter<TType>(name, value);
    }
    
    public ParameterValue<T> FindParameterValue<T>(string name) 
    {
        var parameter = _parameters[name];
        var parameterTyped = parameter as Parameter<TType>;
        return parameterTyped?.Value;
    }
    

    旁注:按照一般约定命名类型约束 TType 没有任何意义,因为类型参数中的 T 前缀表示“类型”所以T 就足够了。

    【讨论】:

    • 我又想多了
    【解决方案2】:

    您的约束应该替换为第二个参数类型的更改:

    public void AddParameter<TType>(string name, ParameterValue<TType> value)
    

    调用应该是这样的:

    _parameters.AddParameter<string>("name", new StringValue("untitled"));
    _parameters.AddParameter<Vector>("position", new VectorValue(new Vector()));
    

    【讨论】:

      猜你喜欢
      • 2011-01-24
      • 2023-03-07
      • 2021-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-26
      相关资源
      最近更新 更多