What is the best solution to this problem? I'm trying to create a function that has several optional parameters of class types for which null is a meaningful value and cannot be used as a default. As in,
public void DoSomething(Class1 optional1, Class2 optional2, Class3 optional3) { if (! WasSpecified(optional1)) { optional1 = defaultForOptional1; } if (! WasSpecified(optional2)) { optional2 = defaultForOptional2; } if (! WasSpecified(optional3)) { optional3 = defaultForOptional3; } // ... do the actual work ... }
I can't use Class1 optional1 = null
because null is meaningful. I can't use some placeholder class instance Class1 optional1 = defaultForOptional1
because of the compile-time constant requirement for these optional parameters I've come up with the following options:
Has anyone out there come up with some clever solution for this?
Thanks!
edit: I ended up writing a wrapper class for so I didn't have to keep repeating 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
};
}
}
I would at least consider creating a new type for the parameter:
public void DoSomething(DoSomethingOptions options)
... where DoSomethingOptions could look like this:
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 ...
}
Then you can call it with:
DoSomething(new DoSomethingOptions { Class1 = null, Class2 = new Class2() });
You don't end up with an exponential set of overloads, and you can still call it reasonably compactly.
This is similar to the approach that Process
takes with ProcessStartInfo
.
Provide overloads with every possible combination, which means 8 overloads for this method.
This is my preference. It makes the situation very clear and maintainable. Internally, you can map to a single initialization routine to reduce duplicated code.
I'd prefer making null
mean "nothing," and have a static readonly
member of type Class1
, Class2
, etc. on Class1
, Class2
etc. named None
. Then instead of making null
meaningful you can use null as "nothing" as it was originally intended.
In case that's confusing:
public class Class1
{
public static readonly Class1 None = new Class1();
}
public static Class2
{
public static readonly Class2 None = new Class2();
}
Note, that if null
in your case means something other than "None" (like "MissingData" or something else) you should name the member thusly. Also note: this will make a lot more sense to other people reading and using your code in the future.
You can create a Flags
enumeration that you can pass along to mark which classes to use.
[Flags]
public enum DoSomethingOptions
{
None = 0,
UseClass1 = 1,
UseClass2 = 2,
UseClass3 = 4,
etc..
}
DoSomething(Class1 class1, ..., DoSomethingOptions options = DoSomethingOptions.None) { ... }
Then simply pass that enumeration in to mark which classes to use. I do wonder why you used null
to mean something other than ? 以外的东西? Although this might be a solution, I'd really like to say "rethink your design".
Yeah, try using an object. Define a class which encapsulates the possible choices. When a choice is set in the object you can store in that same object if it was set through the use of the setter of the original property.
An example:
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; }
}
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.