Generic Type Conversion from String

Generic type conversion FROM string

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();}
}
}

Convert string to generic type c# (Convert string to T)

you can use Convert.ChangeType

T GetValue<T>(string name)
{
string item = getstuff(name);
return (T)Convert.ChangeType(item, typeof(T));
}

if you need to limit input types only for int and DateTime, add condition like below

if (typeof(T) != typeof(int) && typeof(T) != typeof(DateTime))
{
// do something with other types
}

KOTLIN convert string to generic type

You can write generic inline function with reified type parameter:

inline fun <reified T> read() : T {
val value: String = readLine()!!
return when (T::class) {
Int::class -> value.toInt() as T
String::class -> value as T
// add other types here if need
else -> throw IllegalStateException("Unknown Generic Type")
}
}

Reified type parameter is used to access a type of passed parameter.

Calling the function:

val resultString = read<String>()
try {
val resultInt = read<Int>()
} catch (e: NumberFormatException) {
// make sure to catch NumberFormatException if value can't be cast
}

Generic integer type conversion from and to string

I found a solution with all your ideas, thanks a lot. I've tested the following code, it works the way I want and I made it with a generic sense; I could test other methods to check code efficiency, but it works for me and I hope this idea works for anyone else:

public class HexCnv<T> 
{
private MethodInfo _mParse;
private MethodInfo _mToString;
private T _value;
private bool _isPrimitive;

public HexCnv(T v)
{
Type t = typeof(T);

_isPrimitive = t.IsPrimitive;
_value = v;
_mParse = t.GetMethod("Parse", new Type[] { typeof(string), typeof(NumberStyles) });
_mToString = t.GetMethod("ToString", new Type[] { typeof(string) });
}

public HexCnv(string v)
{
Type t = typeof(T);

_isPrimitive = t.IsPrimitive;
_mParse = t.GetMethod("Parse", new Type[] { typeof(string), typeof(NumberStyles) });
_mToString = t.GetMethod("ToString", new Type[] { typeof(string) });
FromString(v);
}

private void FromString(string s)
{
if (_isPrimitive && _mParse != null)
_value = (T)_mParse.Invoke(null, new object[] { s.TrimStart('0', 'x'), NumberStyles.HexNumber });
}

public new string ToString()
{
string sOut = string.Empty;

if (_isPrimitive) sOut = "0x" + _mToString.Invoke(_value, new object[] { "x2" }).ToString();
return sOut;
}

public static implicit operator T(HexCnv<T> v) { return v._value; }

public static implicit operator HexCnv<T>(T v) { return new HexCnv<T>(v); }

public static implicit operator HexCnv<T>(string v) { return new HexCnv<T>(v); }
}

As you can see I changed the static methods, and made it works with simple assigning, see next snippet:

        HexCnv<short> mm = "0x1f55";

Console.WriteLine("MM = {0}", (int) mm);
Console.WriteLine("MM-HEX = {0}", mm.ToString());

//This produce the following result
// MM = 8021
// MM-HEX = 0x1f55

HexCnv<short> mm = "0x8f55"; //IDE show no errors on this, but
HexCnv<short> mm = 0x8f55; //It'll give an error on this expression,
//cuz the number is higher than "short"

Conversion from string to generic type

That is not how it works. You can, however, use polymorphism, to achieve a useful result.

Solution with polymorphism

Base generic (and abstract) property

public abstract class Property<T> {
T value;
public abstract void setValue(String input);
}

Property for Strings

public class StringProperty extends Property<String> {
@Override
public void setValue(String input) {
this.value = input;
}
}

Property for integers

public class IntegerProperty extends Property<Integer> {
@Override
public void setValue(String input) {
this.value = Integer.valueOf(input);
}
}

Not sure what your actual goal is, but this approach might work.

Note, that input instanceof T will fail, because of type erasure. It's not gonna work.


Solution with Class<T> as argument

To elaborate more on your approach, this would work - but it's UGLY.

Ugly and not very convenient. No idea why you'd want it, tbh.

class Property<T> {

public T value;
private final Class<T> clazz;

public Property(Class<T> clazz) {
super();
this.clazz = clazz;
}

@SuppressWarnings("unchecked")
public void setValue(String input) {
if (clazz.isAssignableFrom(String.class)) {
value = (T) input;
} else if (clazz.isAssignableFrom(Integer.class)) {
value = (T) Integer.valueOf(input);
} else if (clazz.isAssignableFrom(Boolean.class)) {
value = (T) Boolean.valueOf(input);
} else if (clazz.isAssignableFrom(Double.class)) {
value = (T) Double.valueOf(input);
} else {
throw new IllegalArgumentException("Bad type.");
}
}
}

Used like so:

Property<String> ff = new Property<>(String.class);
ff.setValue("sdgf");

Property<Integer> sdg = new Property<>(Integer.class);
sdg.setValue("123");

System.out.println(ff.value);
System.out.println(sdg.value);

Solution with Reflection

Apparently, it's possible to figure out the parameter used to instantiate property.

This little magic formula gives you just that:

(Class<?>) getClass().getTypeParameters()[0].getBounds()[0]

I don't even know how I managed to find it. Well, here we go:

class Property<T> {

T value;

@SuppressWarnings("unchecked")
public void setValue(String input)
{
// BEHOLD, MAGIC!
Class<?> clazz = (Class<?>) getClass().getTypeParameters()[0].getBounds()[0];

if (clazz.isAssignableFrom(String.class)) {
value = (T) input;
} else if (clazz.isAssignableFrom(Integer.class)) {
value = (T) Integer.valueOf(input);
} else if (clazz.isAssignableFrom(Boolean.class)) {
value = (T) Boolean.valueOf(input);
} else if (clazz.isAssignableFrom(Double.class)) {
value = (T) Double.valueOf(input);
} else {
throw new IllegalArgumentException("Bad type.");
}
}
}

And don't look at me, I wouldn't use that. I have some common sense.

Casting string to generic type that is a string

You basically need to go via object when casting to a generic type:

return (T)(object) v.ToString()

and

return (T)(object) v;

I would use is rather than catching an InvalidCastException though.

See Eric Lippert's recent blog post for more details of why this is necessary.

In particular:

Because the compiler knows that the only way this conversion could possibly succeed is if U is bool, but U can be anything! The compiler assumes that most of the time U is not going to be constructed with bool, and therefore this code is almost certainly an error, and the compiler is bringing that fact to your attention.

(Substitute T for U and string for bool...)

generic type conversion in C#

The only thing I can offer is to improve robustness by using .TryParse() for the parsing instead of .Parse()

class Program
{
static void Main(string[] args)
{
var i = Parse<int>("100");
var x = Parse<double>("3.1417");
var s = Parse<string>("John");
var d = Parse<Decimal>("1234.56");
var f = Parse<DateTime>("4/1/2044");
var q = Parse<byte>("4A");

Decimal? p = Parse<decimal>("Not Decimal");
}

public static dynamic Parse<T>(string text)
{
var toType = typeof(T);
if (toType == typeof(int))
{
if (int.TryParse(text, out int x))
{
return x;
}
}
else if (toType == typeof(short))
{
if (short.TryParse(text, out short x))
{
return x;
}
}
else if (toType == typeof(double))
{
if (double.TryParse(text, out double x))
{
return x;
}
}
else if (toType == typeof(decimal))
{
if (decimal.TryParse(text, out decimal x))
{
return x;
}
}
else if (toType == typeof(DateTime))
{
if (DateTime.TryParse(text, out DateTime x))
{
return x;
}
}
else if (toType == typeof(byte))
{
if (byte.TryParse(text, System.Globalization.NumberStyles.HexNumber, null, out byte x))
{
return x;
}
}
else if (toType == typeof(string))
{
return text;
}
return null;
}
}

Generic Type Converter - TypeConverter or Convert.ChangeType

In the first example of yours you can get rid of the try catch block by calling CanConvertTo() and CanConvertFrom() beforehand.

public static bool TryParse<T>(string source, out T value)
{
TypeConverter converter = TypeDescriptor.GetConverter(typeof (T));
if (converter.CanConvertTo(typeof (T)) && converter.CanConvertFrom(typeof (string)))
{
value = (T)converter.ConvertFromString(source);
return true;
}
else
{
value = default (T);
return false;
}
}

In the second example why not make it even more generic and pass in a generic type?

Convert only works if the type implements the interface IConvertible so you can check for that, on the other hand it stil doesn't ensure that the conversion will be possible.

        public static bool TryChangeType<T, TR>(T input, out TR output) where T : IConvertible
{
bool result = false;
try
{
Type type = Nullable.GetUnderlyingType(typeof(TR));
output = (TR)Convert.ChangeType(input, type);
result = true;
}
catch(Exception)
{
output = default(TR);
}
return result;
}

It would be nice to only catch the exceptions you know of:

            catch(InvalidCastException)
{
output = default(TR);
//Conversion is not unsupported
}
catch(FormatException)
{
output = default(TR);
//string input value was in incorrect format
}
catch(InvalidCastException)
{
output = default(TR);
//Conversion is not unsupported
}
catch(OverflowException)
{
output = default(TR);
//narrowing conversion between two numeric types results in loss of data
}

This might not answer the question fully, but you were asking for possible improvements so I thought why not.

How to convert a string to generic Type

If I understand your question correctly you want somethiong like this:

public IList GetReferenceDataByType(string referenceType)
{
// this works and returns the appropriate type correctly
var type = this.GetEntity(referenceType);

var method = this.GetType().GetMethod("GetReferenceData");
var generic = method.MakeGenericMethod(type);
return (IList) generic.Invoke(this, new object[] { null });
}

Note that IList<T> does not implement IList so that cast may fail.



Related Topics



Leave a reply



Submit