C# class property with "two types"

179 Views Asked by At

I there any way in c# to create a property which will be basically a bool but can be set via string? What I want to achive is simple:

string stringValue = "true";
object.BoolValue = stringValue ;
bool boolValue = object.BoolValue;

My idea was using get; set; but it always crashes on conversion. I can convert that string in object constructor but what if I want to change it later? Do I need a separate method for conversion (it is not only "true"/"false" but in that setter there should be a list of true values and false values and the rest is argument exception)?

Thanks for any ideas on how to achive this.

UPDATE: Code from OPs comment

private bool _readOnly;

public bool ReadOnly {
    get => _readOnly;
    set => _readOnly = Convert.ToBoolean(value);
}

public DeviceProperty(string readOnly) {
    ReadOnly = readOnly;
}

UPDATE: Solution

Down here you can find a solution with explicit conversion which would works just fine in my application. But regarding best practices in programming I created method to set internal boolean to true/false and then can read it normally.

string stringValue = "true";
_ = object.SetBoolValue = stringValue ;
bool boolValue = object.BoolValue;


public bool SetBoolValue(string stringValue)
{
    string[] trueValues = ..........
    string[] falseValues = .........

    if(trueValues contains stringValue)
    {
        BoolValue = true;
        return true;
    }
    if(falseValues contains stringValue)
    {
        BoolValue = false;
        return true;
    }
    return false;
}

Most important for me is a possibility to create logical operation on this boolValue easily so that is the reason I created Set method and not Get method.

UPDATE: Solution 2...

I am not publishing any code here again but just an update that my last statement about storing as bool to have easier access to logical operations on it shown to be wrong. Because "one changes its mind" and the lists of true and false values change. All the time. And also the way you would not expected. Something what was false is now true etc... And the result bool has to be set based on current setup not something from yesterday. So the object remembers string value and have method GetBool() to say if its true or not based on current lists...

4

There are 4 best solutions below

5
enisn On BEST ANSWER

You can't modify built-in types but you can create a new type that defines Implicit Operator inside and you can set both string and bool to this type. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/user-defined-conversion-operators

Also you can define an Explicit operator too, to cast your object to both of types without actually casting.

public struct MyBoolString
{
    private bool _value;

    public static implicit operator MyBoolString(bool value)
    {
        return new MyBoolString { _value = value };
    }

    public static implicit operator bool(MyBoolString value)
    {
        return value._value;
    }

    public static implicit operator MyBoolString(string value)
    {
        return new MyBoolString { _value = bool.Parse(value) };
    }

    public static implicit operator string(MyBoolString value)
    {
        return value._value.ToString();
    }
}

Then you can use it like that

MyBoolString myBoolString = "true";
myBoolString = true;

bool myBool = myBoolString;
string myString = myBoolString;

And as a property:

public MyBoolString Foo { get; set; }

WARNING: This might be not the best practice for your situation. If you create a new type that means you have to handle serialization & deserialization logic of the type. I.E. database providers can't know your custom type or use it in the wrong way. It's not suggested as a best practice if you use the approach to carry data.

0
MakePeaceGreatAgain On

A property that gets its value from that field for instance?

class MyClass
{
    string myString = "true";
    bool myBool => Convert(myString);
    bool Convert(string myString) => 
        myString.ToLower() == "true" || 
        myString == "1" 
        /* ||  further truish expressions */ ;
}
1
Mateus Zampol On

I think you are trying to achieve something like this?


class Foo
{
    private bool _boolean;

    public string StringBoolean
    {
        get
        {
            return _boolean ? "true" : "false";
        }
        set
        {
            if (value.Equals("true")) _boolean = true;
            else if (value.Equals("false")) _boolean = false;
            else throw new InvalidOperationException();
        }
    }
}
0
Chris Schaller On

The direct answer is NO and that this is generally unnecessary because the caller can use what ever mechanism they need to cast or convert the input value to the property value on your Type.

I would advise against setting implicit types for primitive values or specifically strings. The failure domain is so large and you move conversion checks from compile time to runtime that it is hard to enforce quality of your code.

It is a far better design to force the caller to manage the value conversion and handle the errors than it is to try and do the same thing implicitly inside your class where you have no concept of the active context or a way to communicate back to the caller gracefully.

We can however use multiple properties to se the same underlying field, or we could use an additional method to set the string version of the value and convert it.

NOTE: It is hard to determine your reasoning for trying to implement this at all. A primary concern in applications is data consistency, so your Type should define the only acceptable input type, plowing multiple ways to set the same field can be ambiguous is any value accepted, but only values that can be parsed to the underlying type valid in the typed property? Should it throw an exception of type conversion fails or should the typed value remain default?

public class BoolValue
{
    private bool _boolValue;
    public string AsString
    { 
        get => _boolValue.ToString();
        // Will throw exception if the string was not a valid bool
        set => _boolValue = Boolean.Parse(value);
    }
    public string AsSafeString
    { 
        get => _boolValue.ToString(); 
        set
        {
            if (Boolean.TryParse(value, out bool b)
                _boolValue = b;
        }
    }
    public bool Value
    {
        get => _boolValue; 
        set => _boolValue = value;
    }
}

You can see we can create a number of properties that all set the same field. You could also have used specific methods instead of properties, this makes a lot more sense for supporting setting the value from other types but the whole concept is still a huge code smell.

Reversing the storage model is not much better, but if the input string values might be other non-boolean values and you need to retain them then you could use an implementation like this:

public class BoolValue
{
    private string _inputValue;
    public string AsString
    { 
        get => _inputValue;
        set => _inputValue = value;
    }
   
    public bool? Value
    {
        get => Boolean.TryParse(_inputValue, out bool b) ? b : (bool?)null;
        set => _inputValue = value.ToString();
    }
}