Reputation: 566
I have a property (well, lots of them), that I currently have declared as object, but that I'd like to be able to restrict down to just two types. For example, I want a property that will either take a bool, or an instance of a class that contains an expression that will evaluate to a bool within the context that it is used (Expression class includes a string expression which it can parse at runtime and evaluate to a boolean, or error). Equally, I might do something similar with integer, double, DateTime etc.
How could I make a property that would only accept only a value or an instance of an Expression class that will produce that value?
I know that I may be asking something that C# won't natively do, but what would be the closest approximation?
Edit: For additional context, these are propeties of object that are being edited using a PropertyGrid in winforms, so that the user can dropdown to select one of {true/false/Expression}, where Expression opens a dialog for editing an Expression, or the user can type it into the PropertyGrid directly. For this reason, it would destroy the ergonomics to have two properties, as in the user's mind it will just be one.
Upvotes: 0
Views: 345
Reputation: 172270
Edit: For additional context, these are propeties of object that are being edited using a PropertyGrid in winforms, so that the user can dropdown to select one of {true/false/Expression}, where Expression opens a dialog for editing an Expression, or the user can type it into the PropertyGrid directly. For this reason, it would destroy the ergonomics to have two properties, as in the user's mind it will just be one.
The idiomatic solution in C# would be to create a dedicated class representing the possible choices, e.g.
class UserChoice
{
public bool? BooleanValue { get; }
public Expression ExpressionValue { get; }
// The factory methods ensure that exactly one of BooleanValue
// and ExpressionValue is not null.
public static FromBoolean(bool value) => new UserChoice(value, null);
public static FromExpression(Expression expression)
{
if (expression is null)
{
throw new ArgumentNullException(nameof(expression));
}
return new UserChoice(null, expression);
}
private UserChoice(bool? booleanValue, Expression expressionValue)
{
BooleanValue = booleanValue;
ExpressionValue = expressionValue;
}
}
This would be the basic structure. Possible improvements are:
UserChoice.True
and UserChoice.False
fields returned by FromBoolean
. You now have reference equality for True/False values.Evaluate
method that either returns the Boolean or evaluates the expression.IsBoolean
and IsExpression
convenience properties.Upvotes: 2
Reputation: 186698
Technically, you can achieve the behaviour with a help of implicit operator
(see my code below), but
do you really want "two types" property? Isn't a better design to have two properties?
// Class that evaluates to bool
public class MyBoolClass {
// Stab class which evaluates to constant bool
public class MyBoolClassStub : MyBoolClass {
internal MyBoolClassStub(bool value) {
Value = value;
}
public bool Value { get; }
public override bool ToBool() => Value;
}
// Stub class which returns true whenever evaluates to bool
public static MyBoolClassStub EverTrue { get; } = new MyBoolClassStub(true);
// Stub class which returns flase whenever evaluates to bool
public static MyBoolClassStub EverFalse { get; } = new MyBoolClassStub(false);
public virtual bool ToBool() {
//TODO: here we evaluate to bool; put relevant code
}
public static implicit operator bool(MyBoolClass value) =>
value?.ToBool() ?? throw new ArgumentNullException(nameof(value));
public static implicit operator MyBoolClass(bool value) =>
value ? EverTrue : EverFalse;
}
Having such a class declaration you can declare property as follow:
private MyBoolClass m_MyProperty;
public MyBoolClass MyProperty {
get {
if (m_MyProperty is null)
return false; //TODO: default value here
return m_MyProperty;
}
set {
m_MyProperty = value;
}
}
And use it
// We can assign bool (which will be turned into Stub class)
MyProperty = true;
// We can read bool
if (MyProperty)
Console.WriteLine("true");
// We can assign "class evaluated to bool"
MyProperty = new MyClass();
Console.WriteLine(MyProperty ? "yes" : "no");
Upvotes: 2