C# generic string parse to any object

33,204

Solution 1

Perhaps the first thing to try is:

object value = Convert.ChangeType(text, info.PropertyType);

However, this doesn't support extensibility via custom types; if you need that, how about:

TypeConverter tc = TypeDescriptor.GetConverter(info.PropertyType);
object value = tc.ConvertFromString(null, CultureInfo.InvariantCulture, text);
info.SetValue(obj, value, null);

Or:

info.SetValue(obj, AwesomeFunction("20.53", info.PropertyType), null);

with

public object AwesomeFunction(string text, Type type) {
    TypeConverter tc = TypeDescriptor.GetConverter(type);
    return tc.ConvertFromString(null, CultureInfo.InvariantCulture, text);
}

Solution 2

public T Get<T>(string val)
{
    if (!string.IsNullOrWhiteSpace(val))
        return (T) TypeDescriptor.GetConverter(typeof (T)).ConvertFromString(val);
    else 
        return default(T);
}

Solution 3

Here's a simple version:

object ConvertToAny(string input)
{
    int i;
    if (int.TryParse(input, out i))
        return i;
    double d;
    if (double.TryParse(input, out d))
        return d;
    return input;
}

It will recognize ints and doubles, but everything else is returned as a string. The problem with handling enums is that there's no way to know what enum a value belongs to and there's no way to tell whether it should be a string or not. Other problems are that it doesn't handle dates/times or decimals (how would you distinguish them from doubles?), etc.

If you're willing to change your code like this:

PropertyInfo info = typeof(MyObject).GetProperty("SomeProperty"); 
info.SetValue(obj, AwesomeFunction("20.53", info.PropertyType), null); 

Then it becomes substantially easier:

object ConvertToAny(string input, Type target)
{
    // handle common types
    if (target == typeof(int))
        return int.Parse(input);
    if (target == typeof(double))
        return double.Parse(input);
    ...
    // handle enums
    if (target.BaseType == typeof(Enum))
        return Enum.Parse(target, input);
    // handle anything with a static Parse(string) function
    var parse = target.GetMethod("Parse",
                    System.Reflection.BindingFlags.Static |
                    System.Reflection.BindingFlags.Public,
                    null, new[] { typeof(string) }, null);
    if (parse != null)
        return parse.Invoke(null, new object[] { input });
    // handle types with constructors that take a string
    var constructor = target.GetConstructor(new[] { typeof(string) });
    if (constructor != null)
        return constructor.Invoke(new object[] { input });
}

Edit: Added a missing parenthesis

Solution 4

I know this doesn't answer your question, but have you looked at Dapper micro ORM?
It's über-simple (compared to LINQ to SQL or, for that reason, Entity Framework) and does what you want.

Consider this:

public class Dog
{
    public int? Age { get; set; }
    public Guid Id { get; set; }
    public string Name { get; set; }
    public float? Weight { get; set; }    
}            

var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select * from Dogs where Id = @Id",
    new { Id = 42 }
).First();

Dapper itself is packaged in a single file and, reportedly, is used by StackOverflow team (apart from Linq to SQL).

Share:
33,204
mike
Author by

mike

Updated on May 30, 2020

Comments

  • mike
    mike almost 4 years

    I am storing object values in strings e.g.,

    string[] values = new string[] { "213.4", "10", "hello", "MyValue"};
    

    is there any way to generically initialize the appropriate object types? e.g., something like

    double foo1 = AwesomeFunction(values[0]);
    int foo2 = AwesomeFunction(values[1]);
    string foo3 = AwesomeFunction(values[2]);
    MyEnum foo4 = AwesomeFunction(values[3]);
    

    where AwesomeFunction is the function I need. The ultimate use is to intialize properties e.g.,

    MyObject obj = new MyObject();
    PropertyInfo info = typeof(MyObject).GetProperty("SomeProperty");
    info.SetValue(obj, AwesomeFunction("20.53"), null);
    

    The reason I need such functionality is I am storing said values in a database, and wish to read them out via a query and then initialize the corresponding properties of an object. Is this going to be possible? The entire object is not being stored in the database, just a few fields which I'd like to read & set dynamically. I know I can do it statically, however that will get tedious, hard to maintain, and prone to mistakes with numerous different fields/properties are being read.

    EDIT: Bonus points if AwesomeFunction can work with custom classes which specify a constructor that takes in a string!

    EDIT2: The destination type can be know via the PropertyType, in the specific case where I want to use this type of functionality. I think Enums Would be easy to parse with this e.g.,

    Type destinationType = info.PropertyType;
    Enum.Parse(destinationType, "MyValue");