C# 同一类型的多个参数的编译时方法调用验证

C# 同一类型的多个参数的编译时方法调用验证,c#,methods,compiler-errors,C#,Methods,Compiler Errors,下面是问题的演示: 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 pro

下面是问题的演示:

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
类型的10个),则这将成为一个问题:

问题:调用方法时是否有一种方法来验证参数,这样程序员就不太容易出错


如果参数类型不同,则这不是问题。我认为包装成新类型可能会有所帮助:

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();
    }
}
确实如此,但我不确定这种方法是否有效。我怀疑这是否是个好主意。它是?还是有更好的

如果我总是这样调用方法(使用方法调用),我知道什么是绝对安全的


这不会给代码带来任何开销,但会导致大量的键入。包装更好,因为包装只需一次(创建新类型很容易),但可能会有开销。

如果两个参数属于同一类型,则无法在编译时、运行时或其他时间检测参数变量的名称是否与参数的名称相对应。这是一个悬而未决的问题,但我会给你一些想法

    正如MeHZAD建议的,考虑按某种类型分组参数。例如,代替<代码>双距离(双x1,双y1,双x2,双y2),考虑<代码>双距离(点P1,点P2)< /代码>

    一般来说,如果你的方法有超过4-5个参数,考虑重构。也许你的方法试图做太多的事情,逻辑可以划分

    >P>如果您真正想做的是执行一些检查,例如确保<代码> A ,请考虑查看。您也可以使用
    Debug.Assert()
    ,但这只在运行时有效

  • 我不推荐你建议的那种隐式转换。对我来说,
    aaa=1.1
    除了编译时检查参数之外,不应该有任何语义上的用途,这让我觉得既刻板又不直观。您的最终目标是使代码总体上更易于维护


一个方法永远不应该有10个参数

一旦你有4个参数,开始考虑使用一个新的类来包含这些参数…作为一个例子,考虑用户在网站上导航的偏好…

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 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; }
    }
类程序
{
静态双函数(列表l){返回l[0]。_值*1000+l[1]。_值*l[1]。_值;}
静态void Main(字符串[]参数)
{
A=1.1d;
B=2.2d;
WriteLine(Func(newlist(){a,b}));
Console.WriteLine(Func(newlist(){a,b}));//编译时错误!耶!
Console.WriteLine(Func(new List(){a,b})+123.123d-a*2);//隐式转换能力
Console.ReadKey();
}
}
班级家长
{
公共双_值{get;set;}
}
A类:家长
{
公共静态隐式运算符A(双值){返回新的A(){u value=value};}
公共静态隐式运算符双(A值){返回值。_值;}
}
B类:家长
{
公共静态隐式运算符B(双值){返回新的B(){u value=value};}
公共静态隐式运算符双(B值){返回值。_值;}
}

< /代码>如果你有十个参数,你可以把每一组相关的参数包装成一个单独的类型,你是否考虑过?也许确保你不会给出错误的参数的唯一方法是对每个参数有不同的类型,并且永远不要在它们的未封装的形式中使用它们。它将基本上关闭一条可能犯错误的途径。不管这是不是一个现实的方法,以及它是否真的能提高整个程序的每行缺陷率和可维护性,都是一个好主意。@MehrzadChehraz,好主意。对我来说,这听起来像是一个答案,我只是需要考虑一下,给我一些时间。制作一个专用类型来保存方法的参数是个好主意。然而,如果有许多这样的情况,那就不是了。在给定示例中,
UserPreferences
User
的属性。实际上不需要专门为构造对象创建
UserPreferences
类型,它们可以是
User
的属性,并在构造后填充,除非您在其他地方使用
UserPreferences
类型(例如,与复制属性值相比,将首选项从一个用户复制到另一个用户将更加优雅)是的,在现实世界中,你不需要一个
UserPreference
对象来创建
User
,但它比有一个具有4个参数的构造函数要优雅得多,我只是想分享一个概念,它的自组是一个好主意,但在我的实际案例中并不适用。重构实际上是不可能的,因为这种方法必须在很多地方调用,一些参数可能是某个对象的属性,或者必须指定。事实上,我总是可以要求为方法创建对象(根据@Aydinand的回答),或者更确切地说是使方法非静态(因此在调用方法之前,我必须创建一个实例,并在创建过程中填充属性,然后在方法中使用)关于最后一个注释,考虑使用<代码> FUNC(a,123)。;
,其中
B
是透明的
double
。但是是的,我也觉得它很粗糙。你的方法到底是做什么的?有很多命名参数的方法并不好,但有时这是不可避免的。你能不能至少发布你方法的签名?方法是用来通过使用给定的数组来计算统计值的y的数据和更多的参数,让我们称它们为配置,其中一些属于静态配置,一些属于特定于数据的动态配置,还有一些调整方法结果本身(不属于任何一种配置)。我希望在一个地方有公式(显然),我不希望将完整的配置传递给m
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 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; }
    }