C# 构造函数限制
我想知道是否有一种方法可以限制构造中的值。这是我的密码:C# 构造函数限制,c#,class,restriction,C#,Class,Restriction,我想知道是否有一种方法可以限制构造中的值。这是我的密码: class Student : Human { private double Grade; public Student(string FirstName, string LastName, double Grade) : base(FirstName, LastName) { this.FirstName = FirstName; this.LastName =
class Student : Human
{
private double Grade;
public Student(string FirstName, string LastName, double Grade)
: base(FirstName, LastName)
{
this.FirstName = FirstName;
this.LastName = LastName;
this.Grade = Grade;
}
}
当我成为一名新学生时,我想将分数限制在>=2.00和之间,你可以检查它,并在运行时抛出一个异常,如下所示:
if (grade < 2.00 || grade > 6.00)
throw new ArgumentOutOfRangeException("grade");
double userInput = GetUserInputForGrade();
Grade grade = (Grade)userInput; // perform explicit cast.
Student student = new Student(firstName, lastName, grade);
但是,有一种方法可以使用代码契约获取此类事件的编译时警告。您可以下载,也可以在那里找到文档。它只与Visual Studio的非Express版本集成,由Microsoft编写。它将检查方法调用是否可能遵守您指定的约定。您的代码将变成:
using System.Diagnotistics.Contracts;
public Student(string firstName, string lastName, double grade)
: base(firstName, lastName)
{
#region Contract
Contract.Requires<ArgumentOutOfRangeException>(grade >= 2.00);
Contract.Requires<ArgumentOutOfRangeException>(grade <= 6.00);
#endregion
this.FirstName = firstName;
this.LastName = lastName;
this.Grade = grade;
}
使用System.Diagnostics.Contracts;
公立学生(字串姓氏、字串姓氏、双年级)
:base(firstName,lastName)
{
#区域合同
合同要求(等级>=2.00);
合同要求(等级<代码>班级学生:人
{
私人双年级;
公立学生(字串姓氏、字串姓氏、双年级)
:base(FirstName,LastName)
{
this.FirstName=FirstName;
this.LastName=LastName;
这个。等级=等级;
如果(等级>=2,等级班级学生:人类
{
私人双年级;
公立学生(字串姓氏、字串姓氏、双年级)
:base(FirstName,LastName)
{
如果(等级<2 | |等级>6)
抛出新ArgumentOutOfRangeException(“分数必须介于2和6之间”);
this.FirstName=FirstName;
this.LastName=LastName;
这个。等级=等级;
}
}
公立学生(string FirstName、string LastName、双年级)
:base(FirstName,LastName)
{
如果(Grade>=2.0 | | Grade,则可以引发异常:
class Student : Human
{
private double Grade;
public Student(string FirstName, string LastName, double Grade)
: base(FirstName, LastName)
{
if (Grade >= 2 && Grade <= 6) {
throw new ArgumentOutOfRangeException();
}
this.FirstName = FirstName;
this.LastName = LastName;
this.Grade = Grade;
}
}
班级学生:人类
{
私人双年级;
公立学生(字串姓氏、字串姓氏、双年级)
:base(FirstName,LastName)
{
如果(Grade>=2&&Grade如果你想有编译时检查的机会,你必须使用代码契约。它们本质上是运行时的异常,因此相当于带异常的参数验证
public Student(string firstName, string lastName, decimal grade)
{
Contract.Requires(grade >= 2);
Contract.Requires(grade <= 6);
FirstName = firstName;
LastName = lastName;
Grade = grade;
}
public Student(string firstName、string lastName、十进制分数)
{
合同要求(等级>=2);
Contract.Requires(grade还有一个选项,可能有点复杂,但可以确保给定的范围,根本不使用double
,而是创建一个名为grade
的自定义类。您的grade
类将包含从和到double
的强制转换,并将强制执行验证。
这意味着所有年级验证逻辑都在Grade
结构中,Student
类只接收一个Grade
对象,不必担心它
public struct Grade
{
public static readonly double MinValue = 2.0;
public static readonly double MaxValue = 6.0;
private double value;
public static explicit operator double(Grade grade)
{
return grade.value + MinValue;
}
public static explicit operator Grade(double gradeValue)
{
if (gradeValue < MinValue || gradeValue > MaxValue)
throw new ArgumentOutOfRangeException("gradeValue", "Grade must be between 2.0 and 6.0");
return new Grade{ value = gradeValue - MinValue };
}
}
Edit:我已经用@EricLippert的建议更新了代码,以使类向开发人员公开其最小/最大值
编辑2:根据@JeppeStigNielsen的建议再次更新。value
字段现在存储与MinValue的偏移量,因此调用default(Grade)
将返回有效值(等于MinValue)无论0是否在有效范围内。不要认为您可以将其设置为编译时错误,但是,假设Grade
是用户输入,您可以运行检查以确保它在您的范围内,然后让用户知道是否失败。您真的想要双精度?还是想要十进制?请记住,双精度不能仅代表分母上不是二次幂的分数。ArgumentOutOfRangeException为+1,但与区域不一致。为什么不合并为一个Requires子句?Contract.Requires(grade>=2.00&&grade@Andy代码契约过去不擅长处理复合要求/保证和不变量。我不确定情况是否仍然如此,但为了这一点和可读性(长行等)我将它们分开。异常由程序员决定,因为我们没有足够的信息说明为什么分数不应该在1到7之间,不应该意味着应该抛出一个特定的异常,例如ArgumentOutOfException。@SpaceApple有一条一般规则,你决不能只抛出异常。这违反了.Net最佳实践。True.ArgumentException在这种情况下也是无效的,因为它可能无法满足程序员的需要。但我确实理解您的观点。但是,您会丢失所有运算符、所有比较和相等方法,并且任何接受double
的方法将不再接受您的Grade
@Virtlink:因此,如果你错过了它们。我喜欢这个想法,但我会将常量设置为公共静态只读字段。公共是因为我们希望用户能够知道double何时超出范围。静态是因为它的类型范围。只读不是常量,因为范围将来可能会改变。@EricLippert此结构存在default(Grade)的问题
(或等效地new Grade()
)将创建Grade
结构的“非法”值0.0
。在这种情况下不会运行您的检查。解决方法之一是存储与2.0
相比的多余值(MinValue
)在实例字段中。例如,如果实际分数为2.5
,则存储0.5
。然后,您的转换运算符必须进行相加(分别减去)MinValue
在它们返回之前return
@JeppeStigNielsen:非常好的一点!记住结构的默认值始终是合法值,这一点很重要。第一个示例中的条件是反向的。它在2到6之间的等级上抛出,而不是在那些等级上抛出,除非是在这些等级上抛出。@LawrenceJohnston谢谢,我已经看到了修正了它。
class Student : Human
{
private double Grade;
public Student(string FirstName, string LastName, double Grade)
: base(FirstName, LastName)
{
if (Grade >= 2 && Grade <= 6) {
throw new ArgumentOutOfRangeException();
}
this.FirstName = FirstName;
this.LastName = LastName;
this.Grade = Grade;
}
}
class Student : Human {
private double Grade;
public Student(string FirstName, string LastName, double Grade)
: base(FirstName, LastName)
{
System.Diagnotistics.Contracts.Contract.Requires<ArgumentOutOfRangeException>(Grade >= 2 && Grade <= 6);
this.FirstName = FirstName;
this.LastName = LastName;
this.Grade = Grade;
}
}
public Student(string firstName, string lastName, decimal grade)
{
Contract.Requires(grade >= 2);
Contract.Requires(grade <= 6);
FirstName = firstName;
LastName = lastName;
Grade = grade;
}
public struct Grade
{
public static readonly double MinValue = 2.0;
public static readonly double MaxValue = 6.0;
private double value;
public static explicit operator double(Grade grade)
{
return grade.value + MinValue;
}
public static explicit operator Grade(double gradeValue)
{
if (gradeValue < MinValue || gradeValue > MaxValue)
throw new ArgumentOutOfRangeException("gradeValue", "Grade must be between 2.0 and 6.0");
return new Grade{ value = gradeValue - MinValue };
}
}
double userInput = GetUserInputForGrade();
Grade grade = (Grade)userInput; // perform explicit cast.
Student student = new Student(firstName, lastName, grade);