10

这个问题的最佳解决方案是什么?我正在尝试创建一个函数,该函数具有多个类类型的可选参数,其中 null 是一个有意义的值,不能用作默认值。如中,

public void DoSomething(Class1 optional1, Class2 optional2, Class3 optional3)
    {
        if (! WasSpecified(optional1)) { optional1 = defaultForOptional1; }
        if (! WasSpecified(optional2)) { optional2 = defaultForOptional2; }
        if (! WasSpecified(optional3)) { optional3 = defaultForOptional3; }

        // ... 做实际的工作 ...
    }

我不能使用Class1 optional1 = null,因为 null 是有意义的。由于这些可选参数的编译时常量要求,我无法使用某些占位符类实例Class1 optional1 = defaultForOptional1,因此我提出了以下选项:

  1. 为每个可能的组合提供重载,这意味着此方法有 8 个重载。
  2. 为每个可选参数包含一个布尔参数,指示是否使用默认值,我将签名弄乱了。

有没有人为此想出一些聪明的解决方案?

谢谢!

编辑:我最终写了一个包装类,所以我不必一直重复Boolean HasFoo

    /// <summary>
    /// A wrapper for variables indicating whether or not the variable has
    /// been set.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public struct Setable<T>
    {
        // According to http://msdn.microsoft.com/en-us/library/aa288208%28v=vs.71%29.aspx,
        // "[s]tructs cannot contain explicit parameterless constructors" and "[s]truct
        // members are automatically initialized to their default values."  That's fine,
        // since Boolean defaults to false and usually T will be nullable.

        /// <summary>
        /// Whether or not the variable was set.
        /// </summary>
        public Boolean IsSet { get; private set; }

        /// <summary>
        /// The variable value.
        /// </summary>
        public T Value { get; private set; }

        /// <summary>
        /// Converts from Setable to T.
        /// </summary>
        /// <param name="p_setable"></param>
        /// <returns></returns>
        public static implicit operator T(Setable<T> p_setable)
        {
            return p_setable.Value;
        }

        /// <summary>
        /// Converts from T to Setable.
        /// </summary>
        /// <param name="p_tee"></param>
        /// <returns></returns>
        public static implicit operator Setable<T>(T p_tee)
        {
            return new Setable<T>
            {
                IsSet = true
              , Value = p_tee
            };
        }
    }
4

5 回答 5

12

我至少会考虑为参数创建一个新类型:

public void DoSomething(DoSomethingOptions options)

... DoSomethingOptions 可能如下所示:

public class DoSomethingOptions
{
    private Class1 class1;
    public bool HasClass1 { get; private set; }

    public Class1 Class1 
    {
        get { return class1; }
        set
        {
            class1 = value;
            HasClass1 = true;
        }
    }

    ... for other properties ...
}

然后你可以调用它:

DoSomething(new DoSomethingOptions { Class1 = null, Class2 = new Class2() });

您最终不会得到一组指数级的重载,您仍然可以合理紧凑地调用它。

这类似于Process使用ProcessStartInfo.

于 2012-05-23T20:36:57.357 回答
7

为每个可能的组合提供重载,这意味着此方法有 8 个重载。

这是我的偏好。它使情况非常清晰和可维护。在内部,您可以映射到单个初始化例程以减少重复代码。

于 2012-05-23T20:33:59.553 回答
6

我更喜欢让null意思是“什么都没有”,并且有一个static readonlytype 的成员Class1Class2等等 on Class1Class2等等。命名None。然后,null您可以按照最初的意图将 null 用作“无”,而不是有意义。

如果这令人困惑:

public class Class1
{
    public static readonly Class1 None = new Class1();
}
public static Class2
{
    public static readonly Class2 None = new Class2();
}

请注意,如果null在您的情况下意味着“无”以外的其他内容(如“MissingData”或其他内容),您应该这样命名该成员。另请注意:这对于将来阅读和使用您的代码的其他人来说会更有意义。

于 2012-05-23T20:37:16.267 回答
2

您可以创建一个Flags可以传递的枚举,以标记要使用的类。

[Flags]
public enum DoSomethingOptions
{
    None = 0,
    UseClass1 = 1,
    UseClass2 = 2,
    UseClass3 = 4,
    etc..
}

DoSomething(Class1 class1, ..., DoSomethingOptions options = DoSomethingOptions.None) { ... }

然后只需将该枚举传入以标记要使用的类。我确实想知道为什么您null以前的意思不是null?虽然这可能是一个解决方案,但我真的想说“重新考虑你的设计”。

于 2012-05-23T20:49:08.073 回答
1

是的,尝试使用一个对象。定义一个封装可能选择的类。在对象中设置选项时,如果它是通过使用原始属性的设置器设置的,则可以将其存储在同一对象中。

一个例子:

internal class SettingsHolder
{
    public SettingsHolder()
    {
        IsOriginalPropADefault = true;
    }

    private Class1 originalProp;
    public Class1 OriginalProp
    {
        get
        {
            return originalProp;
        }
        set
        {
            originalProp = value;
            IsOriginalPropADefault = false;
        }
    }

    public bool IsOriginalPropADefault { get; private set; }

}
于 2012-05-23T20:36:36.407 回答