同一类型的多个参数的编译时方法调用验证
Compile-time method call validation for multiple parameters of the same type
这里是问题的演示:
class Program
{
static double Func(double a, double b) { return a * 1000 + b * b; }
static void Main(string[] args)
{
var a = 1.1d;
var b = 2.2d;
Console.WriteLine(Func(a, b));
// this is the problem, function doesn't recognize when a and b
// "accidentally" exchanged, target is to make this row a compile-time error
Console.WriteLine(Func(b, a));
}
}
如果方法有很多参数(例如 double
类型中的十个),这就会成为一个问题:
double Func(double parameter1, double parameter2, ..., double parameter10);
问题:有没有办法在调用方法时验证参数,让程序员不容易出错?
如果参数类型不同,这不是问题。我认为 将 包装成新类型可能会有所帮助:
class A
{
private double _value;
public static implicit operator A(double value) { return new A() { _value = value }; }
public static implicit operator double(A value) { return value._value; }
}
class B
{
private double _value;
public static implicit operator B(double value) { return new B() { _value = value }; }
public static implicit operator double(B value) { return value._value; }
}
class Program
{
static double Func(A a, B b) { return a * 1000 + b * b; }
static void Main(string[] args)
{
A a = 1.1d;
B b = 2.2d;
Console.WriteLine(Func(a, b));
Console.WriteLine(Func(b, a)); // compile-time error! yay!
Console.WriteLine(Func(a, b) + 123.123d - a * 2); // implicit conversion power
Console.ReadKey();
}
}
确实如此,但我不确定这种方法是否有效。我怀疑这是否是个好主意。是吗?或者有更好的吗?
我知道如果我总是这样调用方法(使用 named arguments 方法调用)我可以绝对安全
Func(a:a, b:b);
这应该不会带来任何代码开销,但会带来大量输入。包装更好,因为它只完成一次(创建新类型很容易),但它可能有开销。
如果两个参数的类型相同,则无法在编译时、运行 时或其他时间检测到参数变量的名称与参数的名称相对应。这是一个悬而未决的问题,但我会为您提供一些想法。
正如 Mehrzad 所建议的,考虑按某种类型对参数进行分组。例如,考虑 double Distance(Point p1, Point p2)
而不是 double Distance(double x1, double y1, double x2, double y2)
一般来说,如果你的方法有超过4-5个参数,考虑重构。也许您的方法试图做太多事情并且逻辑可以划分?
如果您真正想要做的是执行一些检查,例如确保 a < b
,请考虑调查 Code contracts。您也可以使用 Debug.Assert()
,但这仅适用于 运行 时间。
我不会推荐您建议的那种隐式转换。对我来说,A a = 1.1
除了编译时检查参数之外应该没有语义目的,这让人感觉很老套和不直观。您的最终目标是使代码整体更易于维护。
一个方法永远不应该有 10 个参数。
一旦你有了大约 4 个参数,开始考虑使用新的 class 来包含这些参数......例如,考虑用户在网站上导航的偏好......
void Main()
{
UserPreferences preference = new UserPreferences
{
BackgroundColor = "#fff",
ForegroundColor = "#000",
Language = "en-GB",
UtcOffSetTimeZone = 0
};
User aydin = new User(preference);
}
public class User
{
public User(UserPreferences preferences)
{
this.Preferences = preferences;
}
public UserPreferences Preferences { get; set; }
}
public class UserPreferences
{
public string BackgroundColor { get; set; }
public string ForegroundColor { get; set; }
public int UtcOffSetTimeZone { get; set; }
public string Language { get; set; }
}
使用继承的class类似这样的东西
class Program
{
static double Func(List<Parent> l) { return l[0]._value * 1000 + l[1]._value * l[1]._value; }
static void Main(string[] args)
{
A a = 1.1d;
B b = 2.2d;
Console.WriteLine(Func(new List<Parent>() {a,b}));
Console.WriteLine(Func(new List<Parent>() { a, b })); // compile-time error! yay!
Console.WriteLine(Func(new List<Parent>() { a, b }) + 123.123d - a * 2); // implicit conversion power
Console.ReadKey();
}
}
class Parent
{
public double _value { get; set; }
}
class A : Parent
{
public static implicit operator A(double value) { return new A() { _value = value }; }
public static implicit operator double(A value) { return value._value; }
}
class B : Parent
{
public static implicit operator B(double value) { return new B() { _value = value }; }
public static implicit operator double(B value) { return value._value; }
}
这里是问题的演示:
class Program
{
static double Func(double a, double b) { return a * 1000 + b * b; }
static void Main(string[] args)
{
var a = 1.1d;
var b = 2.2d;
Console.WriteLine(Func(a, b));
// this is the problem, function doesn't recognize when a and b
// "accidentally" exchanged, target is to make this row a compile-time error
Console.WriteLine(Func(b, a));
}
}
如果方法有很多参数(例如 double
类型中的十个),这就会成为一个问题:
double Func(double parameter1, double parameter2, ..., double parameter10);
问题:有没有办法在调用方法时验证参数,让程序员不容易出错?
如果参数类型不同,这不是问题。我认为 将 包装成新类型可能会有所帮助:
class A
{
private double _value;
public static implicit operator A(double value) { return new A() { _value = value }; }
public static implicit operator double(A value) { return value._value; }
}
class B
{
private double _value;
public static implicit operator B(double value) { return new B() { _value = value }; }
public static implicit operator double(B value) { return value._value; }
}
class Program
{
static double Func(A a, B b) { return a * 1000 + b * b; }
static void Main(string[] args)
{
A a = 1.1d;
B b = 2.2d;
Console.WriteLine(Func(a, b));
Console.WriteLine(Func(b, a)); // compile-time error! yay!
Console.WriteLine(Func(a, b) + 123.123d - a * 2); // implicit conversion power
Console.ReadKey();
}
}
确实如此,但我不确定这种方法是否有效。我怀疑这是否是个好主意。是吗?或者有更好的吗?
我知道如果我总是这样调用方法(使用 named arguments 方法调用)我可以绝对安全
Func(a:a, b:b);
这应该不会带来任何代码开销,但会带来大量输入。包装更好,因为它只完成一次(创建新类型很容易),但它可能有开销。
如果两个参数的类型相同,则无法在编译时、运行 时或其他时间检测到参数变量的名称与参数的名称相对应。这是一个悬而未决的问题,但我会为您提供一些想法。
正如 Mehrzad 所建议的,考虑按某种类型对参数进行分组。例如,考虑
double Distance(Point p1, Point p2)
而不是 一般来说,如果你的方法有超过4-5个参数,考虑重构。也许您的方法试图做太多事情并且逻辑可以划分?
如果您真正想要做的是执行一些检查,例如确保
a < b
,请考虑调查 Code contracts。您也可以使用Debug.Assert()
,但这仅适用于 运行 时间。我不会推荐您建议的那种隐式转换。对我来说,
A a = 1.1
除了编译时检查参数之外应该没有语义目的,这让人感觉很老套和不直观。您的最终目标是使代码整体更易于维护。
double Distance(double x1, double y1, double x2, double y2)
一个方法永远不应该有 10 个参数。
一旦你有了大约 4 个参数,开始考虑使用新的 class 来包含这些参数......例如,考虑用户在网站上导航的偏好......
void Main()
{
UserPreferences preference = new UserPreferences
{
BackgroundColor = "#fff",
ForegroundColor = "#000",
Language = "en-GB",
UtcOffSetTimeZone = 0
};
User aydin = new User(preference);
}
public class User
{
public User(UserPreferences preferences)
{
this.Preferences = preferences;
}
public UserPreferences Preferences { get; set; }
}
public class UserPreferences
{
public string BackgroundColor { get; set; }
public string ForegroundColor { get; set; }
public int UtcOffSetTimeZone { get; set; }
public string Language { get; set; }
}
使用继承的class类似这样的东西
class Program
{
static double Func(List<Parent> l) { return l[0]._value * 1000 + l[1]._value * l[1]._value; }
static void Main(string[] args)
{
A a = 1.1d;
B b = 2.2d;
Console.WriteLine(Func(new List<Parent>() {a,b}));
Console.WriteLine(Func(new List<Parent>() { a, b })); // compile-time error! yay!
Console.WriteLine(Func(new List<Parent>() { a, b }) + 123.123d - a * 2); // implicit conversion power
Console.ReadKey();
}
}
class Parent
{
public double _value { get; set; }
}
class A : Parent
{
public static implicit operator A(double value) { return new A() { _value = value }; }
public static implicit operator double(A value) { return value._value; }
}
class B : Parent
{
public static implicit operator B(double value) { return new B() { _value = value }; }
public static implicit operator double(B value) { return value._value; }
}