如何在C#中使用可选参数包装方法?

时间:2013-12-20 10:10:26

标签: c# default-value optional-parameters

我有以下C#类(在此简化):

internal static class Assertions {
    public static void Assert(bool condition, string message = "Assertion failed") {
        if (!condition) { throw new System.Exception(message); }
    }

    public static void NotNull(object o, string message = "Assertion failed") {
        Assert(!Object.ReferenceEquals(o, null), message);
    }

    public static void EtCaetera(..., string message = "Assertion failed") {
        Assert(..., message);
    }
}

如您所见,我的方法Assertions.Assert()带有可选参数string message = "Assertion failed"

当我围绕该方法编写包装器时,我希望包装器具有默认参数string message,但我想避免重复默认值("Assertion failed"),因为这违反了DRY原则:如果我想将消息"Assertion failed"更改为"I crashed",我将不得不在许多地方更改该默认值。

如何传递可选参数的“缺失”?我正在寻找类似的东西:

public static void NotNull(object o, string message = Type.Missing) {
    Assert(!Object.ReferenceEquals(o, null), message);
}

另一个选择是不使用可选参数并为每个方法提供两个版本,但这很快就会变得很麻烦。

3 个答案:

答案 0 :(得分:3)

可选参数在编译时解析,并且不会替换特殊值,因此您没有很多选项。

我的建议,如果你不想重复自己,就是引入一个特殊的价值(模仿Type.Missing是什么):

internal static class Assertions {
    public static void Assert(bool condition, string message = null) {
        if (!condition) {
            throw new System.Exception(message ?? "Assertion failed");
        }
    }
}

internal static class Wrapper {
    public static void Assert(bool condition, string message = null) {
        Assertions.Assert(condition, message);
    }
}

这还有另一个(IMO大)优势:如果您更改了错误消息(或者将其本地化),则无需更改所有代码(现有的编译库将 更新 )。不要忘记,在您的原始代码中,这样的调用:

Assertions.Assert(value > 0);

将被翻译(并编译,即使您使用const字段):

Assertions.Assert(value > 0, "Assertion failed");

因此,即使您将更改默认消息,编译的程序集也不会更新。

答案 1 :(得分:1)

我更喜欢使用null作为可选参数的默认值。

internal static class Assertions {
    private const string DefaultMessage = "Assertion failed";

    public static void Assert(bool condition, string message = null) {
        message = message ?? DefaultMessage;
        if (!condition) { throw new System.Exception(message); }
    }

    public static void NotNull(object o, string message = null) {
        message = message ?? DefaultMessage;
        Assert(!Object.ReferenceEquals(o, null), message);
    }

    public static void EtCaetera(..., string message = null) {
        message = message ?? DefaultMessage;
        Assert(..., message);
    }
}

答案 2 :(得分:0)

需要在调用的第一个方法(即包装器)上指定默认值,因为这是将值应用于参数的位置。 Type.Missing是一个特殊值,在COM互操作中有意义。以下是您可以尝试的一些可能符合您需求的选项。

  1. 在基本方法上使用OptionalAttibute并在重写方法上指定默认值。

    class Class2 : Class1
    {
        public override string MethodWithOptParams(string message = "default message")
        {
            return base.MethodWithOptParams(message);
        }
    }
    
    class Class1
    {
        public virtual string MethodWithOptParams([Optional]string message)
        {
            return message;
        }
    }
    
  2. 将默认值声明为常量,并将相同的常量应用为默认值。

    class Class2 : Class1
    {
        public override string MethodWithOptParams(string message = DefaultMessage)
        {
            return base.MethodWithOptParams(message);
        }
    }
    
    class Class1
    {
        protected const string DefaultMessage = "default message";
    
        public virtual string MethodWithOptParams(string message = DefaultMessage)
        {
            return message;
        }
    }
    
  3. 在包装器中使用null作为默认值,并对基本方法的两个替代调用进行编码。

    class Class2 : Class1
    {
        public override string MethodWithOptParams(string message = null)
        {
            if (message == null)
            {
                return base.MethodWithOptParams();
            }
            else
            {
                return base.MethodWithOptParams(message);
            }
        }
    }
    
    class Class1
    {
        public virtual string MethodWithOptParams(string message = "default message")
        {
            return message;
        }
    }