2017-02-26 74 views
0

我想創建一個通用(原文如此)的解決方案,讓一個類有一堆屬性。這些屬性應該是簡單類型(bool,int,float等)或複雜類型(向量,顏色等)。他們都應該有一種手段來解析他們從文本到他們的類型。C#的通用約束條件

的一個複雜類型,我打算在這裏採用的是矢量:

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

現在這裏是參數確保所有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; 
    } 
} 

下面是向量的定義:

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

我如何得到這個做我想要的?

回答

1
where TType : ParameterValue<TType> 

這是一個遞歸通用的限制,這將簡化爲TType : ParameterValue<XYZParameterValue>其中XYZParameterValue : ParameterValue<TType>這是不是你想要的,因爲在你的情況下,實際的類型(例如string繼承其相應ParameterValueParameterValue<string>)。使用由相同類型的,它是通用的過,像IComparable<T>接口,其由類型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前綴表示「類型」,因此T就足夠了。

+0

我又是得太多 – imekon

1

你的約束應與第二參數類型的變化來代替:

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

的呼聲應該這樣做:

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