除了类参数的 null 之外的 C# 可选参数
本文关键字:参数 null | 更新日期: 2023-09-27 18:35:19
这个问题的最佳解决方案是什么? 我正在尝试创建一个函数,该函数具有多个类类型的可选参数,其中 null 是一个有意义的值,不能用作默认值。 如,
公共无效 做某事(类 1 可选 1、类 2 可选 2、类 3 可选 3) { 如果(!WasSpecified (optional1)) { optional1 = defaultForOptional1; } 如果(!WasSpecified (optional2)) { optional2 = defaultForOptional2; } 如果(!WasSpecified (optional3)) { optional3 = defaultForOptional3; } //...做实际工作... }
我不能使用Class1 optional1 = null
因为 null 是有意义的。 我不能使用某些占位符类实例Class1 optional1 = defaultForOptional1
由于这些可选参数的编译时常量要求,我想出了以下选项:
- 提供每种可能的组合的重载,这意味着此方法有 8 个重载。
- 为每个可选参数包含一个布尔参数,指示是否使用默认值,这使签名混乱。
有没有人为此想出一些聪明的解决方案?
谢谢!
编辑:我最终编写了一个包装类,所以我不必继续重复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
};
}
}
我至少会考虑为参数创建一个新类型:
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
采取的方法。
提供每种可能的组合的重载,这意味着此方法有 8 个重载。
这是我的偏好。 它使情况非常清晰和可维护。 在内部,您可以映射到单个初始化例程以减少重复的代码。
我宁愿让null
的意思是"什么都没有",并在Class1
、Class2
等上有一个类型为 Class1
、Class2
等的 static readonly
成员命名为 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"或其他内容),则应相应地命名成员。 另请注意:这对于将来阅读和使用您的代码的其他人来说更有意义。
您可以创建一个可以传递的Flags
枚举来标记要使用的类。
[Flags]
public enum DoSomethingOptions
{
None = 0,
UseClass1 = 1,
UseClass2 = 2,
UseClass3 = 4,
etc..
}
DoSomething(Class1 class1, ..., DoSomethingOptions options = DoSomethingOptions.None) { ... }
然后只需将该枚举传入即可标记要使用的类。我确实想知道为什么你用null
来表示 null 以外的其他含义?虽然这可能是一个解决方案,但我真的很想说"重新考虑你的设计"。
是的,尝试使用对象。定义一个封装可能选择的类。在对象中设置选择时,如果它是通过使用原始属性的 setter 设置的,则可以存储在同一对象中。
举个例子:
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; }
}