275

I have a class that I want to use to store "properties" for another class. These properties simply have a name and a value. Ideally, what I would like is to be able to add typed properties, so that the "value" returned is always of the type that I want it to be.

The type should always be a primitive. This class subclasses an abstract class which basically stores the name and value as string. The idea being that this subclass will add some type-safety to the base class (as well as saving me on some conversion).

So, I have created a class which is (roughly) this:

public class TypedProperty<DataType> : Property
{
    public DataType TypedValue
    {
        get { // Having problems here! }
        set { base.Value = value.ToString();}
    }
}

So the question is:

Is there a "generic" way to convert from string back to a primitive?

I can't seem to find any generic interface that links the conversion across the board (something like ITryParsable would have been ideal!).

CRABOLO
  • 8,605
  • 39
  • 41
  • 68
Rob Cooper
  • 28,567
  • 26
  • 103
  • 142

11 Answers11

442

I am not sure whether I understood your intentions correctly, but let's see if this one helps.

public class TypedProperty<T> : Property where T : IConvertible
{
    public T TypedValue
    {
        get { return (T)Convert.ChangeType(base.Value, typeof(T)); }
        set { base.Value = value.ToString();}
    }
}
Wahid Bitar
  • 13,776
  • 13
  • 78
  • 106
lubos hasko
  • 24,752
  • 10
  • 56
  • 61
  • I've been wondering for a few days how to deserialize a stream into a generic type. Thanks :) – Trap Sep 30 '08 at 22:13
  • 3
    I agree, although `Convert.ChangeType` is not very universal and extensible solution, it works for most basic types. if something better is needed, it's no problem to wrap this method into something bigger like Tim suggested or use different conversion method altogether. – lubos hasko Jan 11 '10 at 15:11
  • 18
    I would definitely add the where T:IConvertible – MikeT Nov 07 '13 at 10:45
  • 8
    Type T should not be IConvertible, but Type of base.Value should. – chapluck Sep 15 '15 at 13:52
87

lubos hasko's method fails for nullables. The method below will work for nullables. I didn't come up with it, though. I found it via Google: http://web.archive.org/web/20101214042641/http://dogaoztuzun.com/post/C-Generic-Type-Conversion.aspx Credit to "Tuna Toksoz"

Usage first:

TConverter.ChangeType<T>(StringValue);  

The class is below.

public static class TConverter
{
    public static T ChangeType<T>(object value)
    {
        return (T)ChangeType(typeof(T), value);
    }

    public static object ChangeType(Type t, object value)
    {
        TypeConverter tc = TypeDescriptor.GetConverter(t);
        return tc.ConvertFrom(value);
    }

    public static void RegisterTypeConverter<T, TC>() where TC : TypeConverter
    {

        TypeDescriptor.AddAttributes(typeof(T), new TypeConverterAttribute(typeof(TC)));
    }
}
Markus Safar
  • 6,324
  • 5
  • 28
  • 44
Tim Coker
  • 871
  • 6
  • 2
  • i would add a Fallback convert options for Enums and Other Complex structures, but good call. – Tomer W Mar 27 '12 at 14:10
  • 2
    Why the RegisterTypeConverter? Do we need to register the converters before hand? (unfortunately the link is dead, so I couldn't read up on it) – Cohen Nov 30 '12 at 09:40
  • For multiple conversions you should probably create `tc` (the `TypeConverter`) one time only. `TypeConverter` is slow because it uses reflection to search for the `TypeConverterAttribute`. If you initialize a single private `TypeConverter` field, then you should be able to re-use the `TypeConverter` many times. – Kevin P. Rice Jan 21 '13 at 08:44
  • 1
    Works fine, but if T is an `object`, throws an exception. I was able to workaround that by using ` if (typeof(T).IsPrimitive) { return TConverter.ChangeType(StringValue); } else { object o=(object)StringValue; return (T)o; }` as replacement for the Usage sample `TConverter.ChangeType(StringValue)` – Matt Feb 24 '16 at 17:26
  • if i do TConverter.ChangeType(item.Key) and T is int and item.key is int then i recive exception Int32Converter cannot convert from System.Int32 - how to fix this ? – d00lar Oct 21 '22 at 12:14
21

For many types (integer, double, DateTime etc), there is a static Parse method. You can invoke it using reflection:

MethodInfo m = typeof(T).GetMethod("Parse", new Type[] { typeof(string) } );

if (m != null)
{
    return m.Invoke(null, new object[] { base.Value });
}
Markus Safar
  • 6,324
  • 5
  • 28
  • 44
dbkk
  • 12,643
  • 13
  • 53
  • 60
12
TypeDescriptor.GetConverter(PropertyObject).ConvertFrom(Value)

TypeDescriptor is class having method GetConvertor which accept a Type object and then you can call ConvertFrom method to convert the value for that specified object.

Irshad
  • 3,071
  • 5
  • 30
  • 51
Dinesh Rathee
  • 141
  • 1
  • 2
  • I personally think this interface is better for handling convertion instead of the method Convert.ChangeType since you need to implement the IConvertible interface on all your class. – Sauleil Nov 29 '19 at 04:01
9

With inspiration from the Bob's answer, these extensions also support null value conversion and all primitive conversion back and fourth.

public static class ConversionExtensions
{
        public static object Convert(this object value, Type t)
        {
            Type underlyingType = Nullable.GetUnderlyingType(t);

            if (underlyingType != null && value == null)
            {
                return null;
            }
            Type basetype = underlyingType == null ? t : underlyingType;
            return System.Convert.ChangeType(value, basetype);
        }

        public static T Convert<T>(this object value)
        {
            return (T)value.Convert(typeof(T));
        }
}

Examples

            string stringValue = null;
            int? intResult = stringValue.Convert<int?>();

            int? intValue = null;
            var strResult = intValue.Convert<string>();
Ghominejad
  • 1,572
  • 16
  • 15
4

Check the static Nullable.GetUnderlyingType. - If the underlying type is null, then the template parameter is not Nullable, and we can use that type directly - If the underlying type is not null, then use the underlying type in the conversion.

Seems to work for me:

public object Get( string _toparse, Type _t )
{
    // Test for Nullable<T> and return the base type instead:
    Type undertype = Nullable.GetUnderlyingType(_t);
    Type basetype = undertype == null ? _t : undertype;
    return Convert.ChangeType(_toparse, basetype);
}

public T Get<T>(string _key)
{
    return (T)Get(_key, typeof(T));
}

public void test()
{
    int x = Get<int>("14");
    int? nx = Get<Nullable<int>>("14");
}
Bob C
  • 391
  • 2
  • 10
4

You could possibly use a construct such as a traits class. In this way, you would have a parameterised helper class that knows how to convert a string to a value of its own type. Then your getter might look like this:

get { return StringConverter<DataType>.FromString(base.Value); }

Now, I must point out that my experience with parameterised types is limited to C++ and its templates, but I imagine there is some way to do the same sort of thing using C# generics.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
1

I used lobos answer and it works. But I had a problem with the conversion of doubles because of the culture settings. So I added

return (T)Convert.ChangeType(base.Value, typeof(T), CultureInfo.InvariantCulture);
anhoppe
  • 4,287
  • 3
  • 46
  • 58
1

You can do it in one line as below:

YourClass obj = (YourClass)Convert.ChangeType(YourValue, typeof(YourClass));

Happy coding ;)

Hemendra
  • 179
  • 1
  • 1
  • 5
0
public class TypedProperty<T> : Property
{
    public T TypedValue
    {
        get { return (T)(object)base.Value; }
        set { base.Value = value.ToString();}
    }
}

I using converting via an object. It is a little bit simpler.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mastahh
  • 51
  • 4
0

Yet another variation. Handles Nullables, as well as situations where the string is null and T is not nullable.

public class TypedProperty<T> : Property where T : IConvertible
{
    public T TypedValue
    {
        get
        {
            if (base.Value == null) return default(T);
            var type = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
            return (T)Convert.ChangeType(base.Value, type);
        }
        set { base.Value = value.ToString(); }
    }
}
Todd Menier
  • 37,557
  • 17
  • 150
  • 173