C#中的新类型定义

时间:2014-02-05 17:08:24

标签: c# types

我正在寻找定义新类型并在C#中使用它的可能性,如下所示:

班级定义:

public class Position
{
    public double180 Longitude { get; set; } // double180 is a type within a range -180 and 180
    public double90 Latitude { get; set; } // double90 is a type within a range of -90 and 90
}

用法:

var position = new Position
{
     Longitude = 45,
     Latitude = 96 // This line should give an error while initializing the object
};

7 个答案:

答案 0 :(得分:11)

您不一定需要新类型。您可以手动编写一个验证值的setter,而不是使用auto属性:

public double Latitude
{
    get
    {
        return mLatitude;
    }

    set
    {
        if (value > 90 || value < -90)
        {
            throw new ArgumentOutOfRangeException("Invalid latitude");
        }

        mLatitude = value;
    }
}

private double mLatitude;

如果要重用此代码,可以定义自己的类型并在其中使用上面的setter;然后提供适当的构造函数和转换运算符。

答案 1 :(得分:7)

您最好添加System.ComponentModel.DataAnnotations并使用[Range],如下所示:

public class Position
{
    [Range(-180, 180)]
    public double Longitude { get; set; }

    [Range(-90, 90)]
    public double Latitude { get; set; }
}

答案 2 :(得分:7)

一种类型可能有点过分,但如果你想要一种类型,这是一个好的开始:

struct Double180 : IEquatable<Double180>
{
    private readonly double value;

    public Double180(double d)
    {
        if (d < -180 || d > 180)
        {
            throw new ArgumentOutOfRangeException("d");
        }

        this.value = d;
    }

    public static implicit operator double(Double180 d)
    {
        return d.value;
    }

    public static explicit operator Double180(double d)
    {
        return new Double180(d);
    }

    public override string ToString()
    {
        return this.value.ToString();
    }

    public bool Equals(Double180 other)
    {
        return this.value == other.value;
    }

    public override bool Equals(object obj)
    {
        return obj is Double180 && this.Equals((Double180)obj);
    }

    public override int GetHashCode()
    {
        return this.value.GetHashCode();
    }

    public static bool operator ==(Double180 a, Double180 b)
    {
        return a.Equals(b);
    }

    public static bool operator !=(Double180 a, Double180 b)
    {
        return !a.Equals(b);
    }
}

当然,还有更多的接口要实现,例如IConvertibleIComparable<Double180>会很好。

正如您所看到的,您知道它的起源,但您不知道它的结束位置。

其他答案所建议的setter验证器可能是一个更好的主意。

答案 3 :(得分:4)

使用double并让设置者检查值:

private double _longitude;
public double Longitude
{
    get
    {
        return _longitude;
    }
    set
    {
        if(value < -180 || value > 180)
        {
            throw new ArgumentException("value");
        }
        _longitude = value;
    }
}

答案 4 :(得分:4)

向设置器添加验证步骤:

private double m_Latitude;

public double Latitude
{
  get{return m_Latitude;}

  set
  {
    if(value < -90 || value > 90) throw new ArgumentException("value");

    m_Latitude = value;
  }
}

请注意,当您提供属性的实现时,您需要添加一个成员变量来存储基础属性值。

答案 5 :(得分:1)

我基本上明白了:验证setter中的输入。在类型定义方面,似乎Structs是最好的。最后,我将在我的项目中使用以下内容。

public struct Coordinate
{
    private readonly double _x;
    private readonly double _y;

    /// <summary>
    /// Longitude
    /// </summary>
    public double X
    {
        get { return _x; }
    }

    /// <summary>
    /// Latitude
    /// </summary>
    public double Y
    {
        get { return _y; }
    }

    /// <summary>
    /// Initiates a new coordinate.
    /// </summary>
    /// <param name="x">Longitude [-180, 180]</param>
    /// <param name="y">Latitude [-90, 90]</param>
    public Coordinate(double x, double y)
    {
        if (x < -180 || x > 180)
            throw new ArgumentOutOfRangeException(
                "x", "Longitude value must be in range of -180 and 180.");

        if (y < -90 || y > 90)
            throw new ArgumentOutOfRangeException(
                "y", "Latitude value must be in range of -90 and 90.");

        _x = x;
        _y = y;
    }
}

然后我会像这样使用

var position = new Coordinate(46.32, 34.23);

感谢大家的宝贵意见。

答案 6 :(得分:1)

我喜欢将文档作为系统的一部分:

public class Position
{
    /// <summary>
    /// ...
    /// 
    /// A value within a range -180 and 180
    /// </summary>
    public double Longitude { get; set; }

    /// <summary>
    /// ...
    /// 
    /// A value within a range -90 and 180
    /// </summary>
    public double Latitude { get; set; }
}

必须对所有相关模块进行测试,以符合其依赖关系的规范。 以测试为导向的开发是一种方式。 合同驱动开发是另一个。

如果你坚持使用运行时检查值进行“defencive编程”,那么只需使用构造函数

public class Position
{
    /// <summary>
    /// ...
    /// 
    /// A value within a range -180 and 180
    /// </summary>
    public double Longitude { get; private set; }

    /// <summary>
    /// ...
    /// 
    /// A value within a range -90 and 180
    /// </summary>
    public double Latitude { get; private set; }

    public Position(double longitude, double latitude)
    {
        if (longitude < -180 || longitude > 180)
        {
            throw new ArgumentOutOfRangeException();
        }

        if (latitude < -90 || latitude > 90)
        {
            throw new ArgumentOutOfRangeException();
        }

        Longitude = longitude;

        Latitude = latitude;
    }
}

或使用构建器

public class Position
{
    public double Longitude { get; private set; }

    public double Latitude { get; private set; }

    /// <summary>
    /// Protects from invalid positions. Use <see cref="Position.Builder"/>
    /// </summary>
    private Position() { }

    /// <summary>
    /// Builds valid positions
    /// </summary>
    public class Builder
    {
        public double Longitude { get; set; }

        public double Latitude { get; set; }

        public Position Build()
        {
            if (Longitude < -180 || Longitude > 180)
            {
                throw new ArgumentOutOfRangeException();
            }

            if (Latitude < -90 || Latitude > 90)
            {
                throw new ArgumentOutOfRangeException();
            }

            return new Position() { Latitude = this.Latitude, Longitude = this.Longitude };
        }
    }
}

用法:

Position p = new Position.Builder()
{
    Latitude = 2,
    Longitude = 5
}.Build();

要点:

  • 运行时检查(“防御性编程”):
    • 带检查的公共设定者(见其他答案)
    • 带检查的公共构造函数
    • “构建器执行检查的构建器模式”
  • 测试时间检查:
    • 测试驱动
    • 合同驱动