C# : Custom implicit cast operator failing
Asked Answered
B

3

11

Alright, I've been trying to find any information on this for a while. I built a small class to see how hard type-safe-enums are to implement for strings, because I want to use them for database field-names and such. I've never liked the fact that enums could only be used for integers.
However, even though I have implemented an implicit operator for this class, every time I try to use it, it gives me an invalid cast exception. I'm at a loss, as I can see nothing wrong with my code at this point.
Here's the class:

/// <summary>
/// SBool - type-safe-enum for boolean strings
/// </summary>
public sealed class SBool
{

    private readonly String name;
    private readonly int value;

    // these guys were for conversions. They might work better in another case,
    //  but for this one, they weren't very helpful.
    // ((I.e. they didn't work either.))
    //private static readonly Dictionary<SBool, String> stringsByBool = new Dictionary<SBool, String>();
    //private static readonly Dictionary<String, SBool> boolsByString = new Dictionary<String, SBool>();

    public static readonly SBool True = new SBool( 1, "true" );
    public static readonly SBool False = new SBool( 0, "false" );

    private SBool( int value, String name )
    {
        this.name = name;
        this.value = value;
        //stringsByBool[this] = name;
        //boolsByString[name] = this;
    }

    private SBool( SBool sbool )
    {
        this.name = sbool.name;
        this.value = sbool.value;
        //stringsByBool[this] = name;
        //boolsByString[name] = this;
    }

    public override String ToString()
    {
        return name;
    }

    /// <summary>
    /// allows implicit casting of SBools to strings
    /// </summary>
    /// <param name="sbool">the SBool to cast into a string</param>
    /// <returns>the string equivalent of the SBool (its value)</returns>
    public static implicit operator String( SBool sbool )
    {
        if ( sbool == SBool.True )
            return SBool.True.name;
        else
            return SBool.False.name;
    }

    /// <summary>
    /// implicitly cast a string into a SBool.
    /// </summary>
    /// <param name="str">the string to attempt to cast as a SBool</param>
    /// <returns>the SBool equivalent of the string,
    /// SBool.False if not either "true" or "false".</returns>
    public static explicit operator SBool( String str )
    {
        if ( !String.IsNullOrEmpty(str) && str.ToLower() == "true" )
            return SBool.True;
        else
            return SBool.False;
    }

    public static bool operator ==( SBool left, SBool right )
    {
        return left.value == right.value;
    }

    public static bool operator !=( SBool left, SBool right )
    {
        return left.value != right.value;
    }
}


This is failing on the check of a Session variable:
if( ( (string)Session["variable"] ) == SBool.False ) with an InvalidCastException,
and I quite frankly have no idea why.

Thanks in advance; cookies for anyone who can explain why this doesn't work (cookies not available in all areas). I'm going to get other things fixed, but let me know if there's anything that is unclear. For more info on Type-Safe enums, here's one of the SO posts I based this class off of.

[MetaEDIT] disregard this. I was horribly, horribly mistaken. [/edit]

Boehmite answered 2/4, 2013 at 14:54 Comment(6)
Have you tried casting Session["variable"] to an SBool instead of a string? Or to a string and then an SBool?Software
sBool.False is a Boolean correct...? if you are checking if it's equal why not cast that value on the right side of the == to if( ( (string)Session["variable"] ) == SBool.False.ToString() The Casting error is correct btwSibylle
Most probably exception is about this cast (string)Session["variable"]High
@Lanorkin: if I'm not mistaken (string) should not throw exceptions; if Session["var"] is invalid it will simply return "" - citation @DJKRAZE: the point is to not have to ToString every time. that's why I'm using an implicit cast in the first place.Boehmite
@Boehmite Session may contain object of absolutely any type, so you can easily get InvalidCastException. Just separate your expression in two lines and check which one throws ExceptionHigh
@Lanorkin: Alright, I see what my problem is: I forgot that the Session would be assigned as an SBool instead of implicitly casting to a string during assignment. I.e: Session["var"] = SBool.True; would not use the cast. Thanks for the illuminate; that explains the whole issue.Boehmite
C
12

User defined implicit and explicit operators are entirely a compile time mechanism, not a runtime mechanism. Once the code is compiled the runtime has no idea about any user defined conversion operators.

When the compiler is doing it's type checking and sees that a Foo is expected but the actual value is a Bar it will first check through the built in language implicit conversion operators to see if an appropriate conversion exists. If it doesn't, it checks through the definition of both Foo and Bar for implicit conversion operators, if it finds one it will add in a call to the relevant static method to perform the conversion. Once you get to runtime only the built in language implicit operators will be applied.

In this case you aren't converting from SBool to a string, you're converting from object to string (as far as the compiler is concerned) and there is no conversion operator to handle that.

You need to first cast the result of the Session variable to SBool (which is what it really is) and then to some other type in order to be able to leverage the user defined conversion operators. So:

if( ( (SBool)Session["variable"] ) == SBool.False )

will work just fine.

Chinchin answered 2/4, 2013 at 15:11 Comment(6)
+1, if you implement an implicit conversion from object to SBool would it enable implicit unboxing? Probably not ... would be expensive for the compiler, undesired side effects etc.Vashtee
@Vashtee It's a class, not a struct, so there is no boxing going on here. That said, there's no reason you couldn't have a conversion between the type and object.Chinchin
If this is the case, then why would my exception specifically state that the invalid conversion is between SBool and System.String? If it thought it was an object, wouldn't it say that instead of SBool in the exception message? ((Sorry, the Message text should have been included in the original post.))Boehmite
@Boehmite For exactly the reasons that I've stated. At runtime the type is SBool, and you're trying to cast it to String. There is no conversion operator from SBool to String at runtime, and there is no way of making a user defined operator that would do so. You need to ensure that you are never in a position in which it attempt to find a conversion operator that does this at runtime by allowing the compiler to find the conversion operator at compile time. At compile time the result of the session value is object, but at runtime it's SBool.Chinchin
@oh, and to your edit in which you say the session value is a string, the error message you just mentioned above indicates otherwise, so I suggest you check on that.Chinchin
@Servy: I missed your statement about only built-in implicit operators existing at run-time, which explains a lot of my confusion. My edit was wrong. I now understand why your original answer is correct. As stated above, I thought the cast was failing on the right-side of (string)Session["variable"] == SBool.False, because I didn't realize that I hadn't casted my SBools to strings when assigning to Session. Apparently I either need more sleep or my blood-sugar is too low right now. Either way, thanks.Boehmite
M
1

As Servy mentioned these types of cast happen at compile time. However there is a way to force this check to occur at runtime by using dynamic keyword. Instead of casting to SBool cast to dynamic. This will cause cast operators to be executed at run time.

In my opinion casting to SBool is a cleaner and less error prone solution.

Metrics answered 8/8, 2013 at 12:20 Comment(0)
F
-1

Wen you're casting Session[] to a string, you're performing an explicit conversion. You don't have a SBool->String explicit operator defined, only implicit.

You need to add an explicit SBool->string conversion.

Frydman answered 2/4, 2013 at 15:6 Comment(1)
Even if he did add it, it wouldn't help at all.Chinchin

© 2022 - 2024 — McMap. All rights reserved.