0

I have a class called SystemUser as defined below:

    public class SystemUser
    {
        public int userID;
        public string userName;
        public string email;

        public SystemUser(int userId, string userName, string email)
        {
            this.userID = userId;
            this.userName = userName;
            this.email = email;
        }

        public static explicit operator System.String(SystemUser su)
        {
            return su.userName;
        }

        public static implicit operator SystemUser(System.String s)
        {
            SystemUser thing = new SystemUser(-1, s);
            return thing;
        }
    }

From what I'm able to determine, this should allow me to perform the following, where user is a variable of type object:

List<SystemUser> systemUsers = new List<SystemUser>();
systemUsers.Add((SystemUser)user); // causes exception

However, if user is a string, I always get the InvalidCastException: "Unable to cast object of type 'System.String' to type 'SystemUser'"

What am I missing here?

Jimmy
  • 2,805
  • 6
  • 43
  • 57
  • possible duplicate of [How do I provide custom cast support for my class?](http://stackoverflow.com/questions/1407689/how-do-i-provide-custom-cast-support-for-my-class) – edhedges Jun 24 '14 at 16:52
  • 2
    although this is possible technically, this is **very bad** design. – AK_ Jun 24 '14 at 17:38
  • Are you sure you don't want that second operator to be `explicit`? – Jim Mischel Jun 24 '14 at 17:40
  • @AK_, can you explain why this is such bad design? – Jimmy Jun 24 '14 at 17:58
  • @Jimmy because conversions are intended to move the same thing between different representations. i.e. the number 5 can be stored in an int or a double, but it remains the number 5. A string represents some text, a `User` object represents the abstract concept of a user within the system. these are different "things" . what you are looking for is probably the ToString() function, and a constructor reciving a string parameter, or maybe Json Serialization – AK_ Jun 24 '14 at 18:06
  • @AK_ That makes sense. Thank you. I think I'll have to give up on the casting in this situation. – Jimmy Jun 24 '14 at 18:08

1 Answers1

2

It sounds like your user variable is of type object. You've only provided conversion support for the following scenarios:

  • stringSystemUser
  • SystemUserstring

While your user variable might contain a reference to an instance of string, it can't be implicitly downcast to string.

string (or any other type, for that matter) can be implicitly upcast to object (since string is object evaluates to true), the converse is not possible. An object instance can't be implicitly downcast to string since object is string evaluates to false.

You can:

  • explicitly cast your object to string and then to SystemUser, or

    object     user     = GetStringRepresentingUser() ;
    SystemUser instance = (SystemUser)(string)user ;
    
  • change the data type of user to string

    string     user     = GetStringRepresentingUser() ;
    SystemUser instance = (SystemUser)user ;
    

Edited to Note:

Adding overloads may help you here. You can do something like this:

public static SystemUser GetSystemUser( object o )
{
  SystemUser instance ;
  if ( o is string )
  {
    instance = GetSystemUser( (string) o ) ;
  }
  else
  {
    instance = (SystemUser) o ;
  }
  return instance ;
}
public static SystemUser GetSystemUser( string s )
{
  return (SystemUser) s ;
}

You might also consider using the Parse()`TryParse()` idiom:

public static SystemUser Parse( string s )
{
  // parse the string and construct a SystemUser instance
  // throw a suitable exception if the parse is unsuccessful
}
public static bool TryParse( string s , out SystemUser instance )
{
  // as above, except no exception is thrown on error.
  // instead, return true or false, setting instance to default(SystemUser) (null)
  // if the conversion wasnt' possible.
}

Using Parse()/TryParse() is likely to get more easily understood as well.

Not matter how you do it, at some point you're going to have to look at your object and check its type:

  • If its actually a string, excplicity downcast to string and deserialize (parse) it back into an instance of your class, or...

  • If it's not a string, it must be an instance of your type: excplicity downcast it.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • Would it be possible to avoid the string cast by writing my custom cast on object instead of string? `user` can be either of type SystemUser or string before the cast, so I'm trying to avoid having to check the type. – Jimmy Jun 24 '14 at 18:01
  • No. You'll get `error CS0553: 'Foo.Bar.implicit operator Foo.Bar(object)': user-defined conversions to or from a base class are not allowed`. Why don't you use *overloading* to accomplish what you want? (see my answer for details). – Nicholas Carey Jun 24 '14 at 18:43
  • Well that makes a lot more sense than what I was trying to do. Thanks for your help! – Jimmy Jun 24 '14 at 19:28