簡體   English   中英

C#的通用約束

[英]Generic constraints with C#

我想創建一個通用(原文如此)解決方案,以使一個類具有一堆屬性。 這些屬性應為簡單類型(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()));
    }
}

添加參數“名稱”和“位置”的兩行會引發錯誤:

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>'.

我怎樣才能做到這一點我想要的?

where TType : ParameterValue<TType>

這是一個遞歸通用的限制,這將簡化為TType : ParameterValue<XYZParameterValue>其中XYZParameterValue : ParameterValue<TType>這是不是你想要的,因為在你的情況下,實際的類型(如string繼承其相應ParameterValueParameterValue<string> )。

使用由通用類型相同的類型實現/繼承的通用接口/基類時,您的通用約束將起作用,例如,由類型T實現的IComparable<T>接口(即System.String : IComparable<System.String> )。

相反,我將執行以下操作:

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前綴表示“ type”,因此T就足夠了。

您的約束應替換為第二個參數類型的更改:

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

調用應如下所示:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM