views:

144

answers:

4

Given the class:

public class Foo
{
    public string Name { get; set; }
}

Is it possible to have a Foo instance created from a string through Convert.ChangeType:

Type type = typeof(Foo);
object value = "one";

value = System.Convert.ChangeType(value, type);

This is how a 3rd party API is attempting to rebuild objects. Someone mentioned this is possible with implicit operators, but from my understanding that will let me do the following, not create the object:

Foo foo = new Foo() { Name = "one" };
string fooAsString = foo;  // implicit conversion -- no cast needed

Is there a way to create the object this way? Also, I do have the ability to change the Convert.ChangeType if there is another way to do this.

Update: The reason I am asking is because it throws and exception:

Invalid cast from 'System.String' to 'JibbaJabba+Foo'.

and adding the operator did not resolve the issue.

A: 

object value1 = "one";

Foo value2 = (Foo) System.Convert.ChangeType(value, typeof(Foo));

Edit:


Ok then, if you are trying to create type of Foo from string, you could use reflection:

String controlToCreate = "Foo";
Type typeofControl = Type.GetType(controlToCreate,true);
kofucii
That does not work
blu
The update does not work either. The second part of the answer does not relate to the question.
blu
@kofucii: The OP specifically wants to make this work with `ChangeType` because it is being used by a 3rd party library.
Dan Tao
+2  A: 

According to the MSDN documentation:

For the conversion to succeed, value must implement the IConvertible interface, because the method simply wraps a call to an appropriate IConvertible method. The method requires that conversion of value to conversionType be supported.

Looking at the IConvertible interface, it has a ToType method. You could try that, maybe? (Disclaimer: I haven't. It's just a thought.)

Edit: In your case, it seems that you want to convert from a string to a Foo. Since the string type (obviously) does not define a conversion to Foo in its IConvertible implementation, I believe you're out of luck.


Update: I don't want to suggest that this is how you should always approach this sort of problem, but...

I took a look at the code for Convert.ChangeType in Reflector. It's long; I won't reproduce it here. But basically it's doing as the documentation says: it only works if:

  • The value parameter is a non-null instance of a type that implements IConvertible, or:
  • The type of the value parameter and the conversionType parameter are the same (so: Convert.ChangeType(myFoo, typeof(Foo)) would also work, though it'd be pretty useless).

Then, it cycles through all the types supported by IConvertible (which obviously does not include any user-defined types) and ultimately uses ToType as a fallback.

So, we need to look at the string type's implementation of ToType.

Sadly, it is one unfortunate line:

return Convert.DefaultToType(this, type, provider);

What does DefaultToType do? Exactly the same thing as ChangeType (minus the ToType fallback, obviously to avoid infinite recursion).

So this just simply isn't going to work.

If you're absolutely tied to this 3rd party library that's using Convert.ChangeType behind the scenes, I would recommend contacting the library's developer and asking them to extend their API in some way that will allow you to accomplish what you're trying to accomplish. Some possiblities might be:

  • Accepting an optional Converter<string, T> or Func<string, T> delegate parameter, as suggested by Ben Voigt in a comment.
  • Accepting a TypeConverter parameter
  • Accepting a parameter of some type that implements an interface like IParser<T>

Anyway, best of luck.

Dan Tao
I see that, good catch in MSDN as to why the exception happens. Looks like I am hosed and will have to find an alternate approach. I will +1 and leave the question open for a bit and see if anyone else has an idea.
blu
@blu: Yeah, you never know. Sometimes the docs reveal only part of the story.
Dan Tao
+1  A: 

Direct cast from string won't work as Dan Tao already pointed out. Could you maybe implement your own wrapper for the string and use that? Something like

class MyString: IConvertible {

  public string Value { get; set; }

  ...
  object IConvertible.ToType(Type conversionType, IFormatProvider provider) {
    if (conversionType == typeof(Foo))
      return new Foo { Name = Value };
    throw new InvalidCastException();
  }

}
...
MyString myString = new MyString{Value="one"};
Foo myFoo = (Foo)Convert.ChangeType(myString, typeof(Foo));

Don't know if it is a useful idea but anyway..

Patko
It's an interesting idea, but I have a *feeling* the 3rd party library the OP's trying to use requires strings as input (e.g., it may be a deserialization library).
Dan Tao
Yeah, I have the same feeling :)
Patko
A: 

An implicit operator won't work here? If Foo is a class you can modify then I've used something like this in the past, which also allows you to compare Foo instances to a string.

public class Foo
{
  public string Name { get; set; }

  public static implicit operator Foo(string value)
  {
    return new Foo { Name = value };
  }
}

...
Foo foo = "fooTest";
Console.WriteLine("Foo name: {0}", foo.Name);
...

Edit: If you must use ChangeType then as far as I know you're out of luck. If you can modify the API to use a TypeConverter, you can use something like the following.

...
Type type = typeof(Foo);
object value = "one";

var converter = TypeDescriptor.GetConverter(type);
if (converter.CanConvertFrom(value.GetType()))
{
  object newObject = converter.ConvertFrom(value);
  Console.WriteLine("Foo value: {0}", newObject.ToString());
}
...

public class FooConverter : TypeConverter
{
  public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  {
    return sourceType == typeof(string);
  }

  public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
  {
    var name = value as string;
    if (name != null)
      return new Foo { Name = name };
    else
      return base.ConvertFrom(context, culture, value);
  }
}

[TypeConverter(typeof(FooConverter))]
public class Foo
{
  public string Name { get; set; }

  public override string ToString()
  {
    return Name;
  }
}
MattP
I think the OP understands how the implicit operator works. What he's trying to do is design a type such that when a 3rd party library calls `Convert.ChangeType` on a string and passes his custom type as the `type` parameter, an instance of his custom type will be instantiated. In other words `Convert.ChangeType` is an avenue which this code *must* go through (based on the OP's requirements).
Dan Tao
I see your point. It's not clear from his comment, *"Also, I do have the ability to change the Convert.ChangeType if there is another way to do this."* whether this means he can modify the API source or not.
MattP