简体   繁体   中英

How can I wrap a method with optional parameters in C#?

I have the following C# class (simplified here):

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);
    }
}

As you can see, I have a method Assertions.Assert() with an optional parameter string message = "Assertion failed" .

When I write a wrapper around that method, I'd like the wrapper to have a default parameter string message , but I'd like to avoid repeating the default value ( "Assertion failed" ) because that violates the DRY principle: if I want to change the message "Assertion failed" to "I crashed" , I'll have to change that default value in many places.

How can I pass-through the "missingness" of the optional parameter? I'm looking for something like:

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

ANother option would be not to use optional parameters and provide two versions of each method, but that would quickly get cumbersome.

Optional parameters are resolved at compile time and they're not replaced for special values so you don't have many options here.

My suggestion, if you don't want to repeat yourself, is to introduce a special value (to mimic what Type.Missing is):

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);
    }
}

This has another (IMO big) advantage: if you change your error message (or you'll localize it ) you won't have to change all your code (and existing compiled libraries will be updated ). Don't forget that, in your original code, a call like this:

Assertions.Assert(value > 0);

Will be translated (and compiled, even if you use a const field) to:

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

So, even if you'll change your default message, compiled assemblies won't get updated.

I prefer to use null as the default value for optional arguments.

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);
    }
}

The default value needs to be specified on the first method called (ie your wrapper) as this is where the value is applied to the parameter. Type.Missing is a special value that has meaning in COM interop. Here are some options you could try that may suit your needs.

  1. Use the OptionalAttibute on the base method and specify the default value on the overridden method.

     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. Declare your default values as constants in and apply the same constant as the default value.

     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. Use null as the default value in your wrapper and code the two alternative calls to the base method.

     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; } } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM