I'm working on building a dynamic calculation framework, that will build a generic GUI based on the calculation types and attributes on those types.
For instance, I might have a simple Adder calculator (forgive the simplicity, but when putting together frameworks, I like to start simple and work my way up), that looks like this:
[CalculatorAttribute("This calculator add X+Y=Z")]
class AdderCalculator : CalculatorBase
{
[CalculationValueAttribute(InputOutputEnum.InputValue, "First Input")]
public int? X
{
set { m_X = value; m_Z = null;}
get { return m_X; }
}
[CalculationValueAttribute(InputOutputEnum.InputValue, "Second Input")]
public int? Y
{
set{ m_Y = value; m_Z = null;}
get { return m_Y; }
}
[CalculationValueAttribute(InputOutputEnum.OutputValue, "Output")]
public int? Z
{
get { return m_Z; }
}
public AdderCalculator()
{
m_X = m_Y = m_Z = null;
}
public override Boolean ReadyToCalc()
{
return (m_X != null && m_Y != null);
}
public override Boolean NeedToCalc()
{
return (m_Z == null);
}
public override void Calculate()
{
if(ReadyToCalc()){ m_Z = m_X + m_Y;}
}
private int? m_X, m_Y, m_Z;
}
(forgive the whitespace, I tried to reduce the size as much as possible without sacrificing too much readability).
I'm using the nullable type to allow me to distinguish unset values from set values
Now, in my GUI, I'm using TextBoxes to collect the input information. From TextBox, I can only get the Text (String). Using reflection, I know the name of the property and can get it's setter (set_X or set_Y in this case).
The type of the field is (of course) int? (nullable int), but I can use:
PropertyInfo.PropertyType.GetGenericArguments();
method to get the Int32 type (or Int64, whichever may be the case).
So, given that I can end up with a instance of a Type object, can anyone recommend a generic way to convert String to that type
What I have considered is:
Implement a string setter method in my AdderCalculator: set_X(String s)
Change my control type to NumericUpDown, as it will return a Decimal
change the type to Decimal, but Decimal can't directly convert from String either (it can use parse, but that is tougher from a generic standpoint
Can anyone provide additional insight?
Just to get the answer out where it can be clearly seen, the code I ended up with is:
// collect relevant inputs
foreach (Control c in fPanelIn.Controls)
{
if (c.Tag is PropertyInfo)
{
PropertyInfo pi = c.Tag as PropertyInfo;
if(c.Text.Length>0)
{
Type ti = pi.PropertyType;
if (ti.IsGenericType)
{
ti = ti.GetGenericArguments()[0];
}
object o = Convert.ChangeType(c.Text, ti);
pi.SetValue(Calculator, o, null);
//MethodInfo mi = calcType.GetMethod(ConstructMethodName(pi), new Type[] { typeof(String) });
//mi.Invoke(Calculator, new object[] { c.Text });
}
else
{
pi.SetValue(Calculator, null, null);
}
}
}
I still need to add some exception protection for invalid values, but this works with the property type being any numeric type (byte, short, int, int32, int64, float, double, Decimal, etc...)
Related
I'm currently refactoring a large codebase and converting it from vb.net to c#. One of the functions does some funky logic around loading columns from a datasource and putting them into properties.
With vb, the creator didn't need to worry about conversions etc. but with c# I need to consider it.
So, given a piece of code like the below, I need to convert an int to the relevant enum type.
public class SomeType
{
public SomeEnum enumValue { get; set; }
public void SetValues()
{
... GetValuesFromDataSource
... Iterates values
enumValue = SetProperty(enumValue, valueFromDataSource);
}
}
protected dynamic SetProperty(object oProperty, object oValue)
{
if (oProperty is Enum)
{
... Convert oValue to the relevant enum type and assign to oProperty
}
}
Just to add some clarity. The references to SetProperty number in the thousands, so I don't want to refactor by hand.
The oValue will come in as an int. There is other logic in the SetProperty method for other types. oProperty could be one of any of the hundreds of enums in the application.
If valueFromDataSource is an object and is just the boxed enum in question, you can use something like the following
if (valueFromDataSource is someEnum)
{
return (someEnum)valueFromDataSource
}
else if (valueFromDataSource is someOtherEnum)
{
return (someOtherEnum)valueFromDataSource
}
However in your original function you're returning back an object, which will box it again, nothing gained
Which leads me to believe your valueFromDataSource is actually an int, therefore, there is no way back, and you would at best have to guess what the enum should be
In a design scenario, your best bet is not store the enum as an int, and just box it
Second would be to leave a hint to the enum type or something
Update
can you convert an int to an Enum without knowing the type at design time?
Unfortunately the answer is no, in VB or C# or any other language, it just cant be done
If your re-factoring code, you must have some wires crossed as the VB creator couldn't do this either
Update to do it more dynamically, using an out parameter for a SetProperty Method
class Program
{
static void Main(string[] args)
{
Entity tt = default(Entity);
Test tester = default(Test);
tester = SetProperty(tester, 5);
tt = SetProperty(tt, "Test7");
Console.WriteLine(tester);
Console.WriteLine(tt);
Console.ReadLine();
}
public static T Cast<T>(T target, object Source)
{
return (T)Source;
}
public static dynamic SetProperty<T>(T target, object source)
{
Type t = typeof(T);
Type sourceType = source.GetType();
if (t.IsEnum)
{
Type enumType = t.GetEnumUnderlyingType();
if (Type.Equals(sourceType, enumType))
{
return (T)Cast(Activator.CreateInstance(enumType), source);
}
if (Type.Equals(sourceType, typeof(string)))
{
Array names = t.GetEnumValues();
foreach (var name in names)
{
if (name.ToString() == source.ToString())
{
return (T)name;
}
}
}
return default(T);
}
return null;
}
public enum Entity
{
Test5,
Test7,
Test8
}
public enum Test : int
{
Test1 = 1,
Test2 = 5,
Test4 = 7
}
}
the output would then be:
Test2
Test7
I have been playing around with converting a string to a value type in .NET, where the resulting value type is unknown. The problem I have encountered in my code is that I need a method which accepts a string, and uses a "best fit" approach to populate the resulting value type. Should the mechanism not find a suitable match, the string is returned.
This is what I have come up with:
public static dynamic ConvertToType(string value)
{
Type[] types = new Type[]
{
typeof(System.SByte),
typeof(System.Byte),
typeof(System.Int16),
typeof(System.UInt16),
typeof(System.Int32),
typeof(System.UInt32),
typeof(System.Int64),
typeof(System.UInt64),
typeof(System.Single),
typeof(System.Double),
typeof(System.Decimal),
typeof(System.DateTime),
typeof(System.Guid)
};
foreach (Type type in types)
{
try
{
return Convert.ChangeType(value, type);
}
catch (Exception)
{
continue;
}
}
return value;
}
I feel that this approach is probably not best practice because it can only match against the predefined types.
Usually I have found that .NET accommodates this functionality in a better way than my implementation, so my question is: are there any better approaches to this problem and/or is this functionality implemented better in .NET?
EDIT: Note that the ordering of types in the array is so that the "best fit" occurs as accurately as possible for the given types.
EDIT: as per miniBill's request, this I how the method might be used (simple example!):
JsonDictionary["myKey"] = ConvertToType("255"); // 255 is a stringified json value, which should be assigned to myKey as a byte.
Your method isn't ideal as its going to cause a series of exceptions if value is not a SByte.
Seeing as all of these types share a common method .TryParse(string, out T) we can use reflection extract the method and call it for each type. I made the method an extension method on string and also factored out the Type[] array into its own lazy loaded property for faster use.
public static class StringExtensions
{
public static dynamic ConvertToType(this string value)
{
foreach (Type type in ConvertibleTypes)
{
var obj = Activator.CreateInstance(type);
var methodParameterTypes = new Type[] { typeof(string), type.MakeByRefType() };
var method = type.GetMethod("TryParse", methodParameterTypes);
var methodParameters = new object[] { value, obj };
bool success = (bool)method.Invoke(null, methodParameters);
if (success)
{
return methodParameters[1];
}
}
return value;
}
private static Type[] _convertibleTypes = null;
private static Type[] ConvertibleTypes
{
get
{
if (_convertibleTypes == null)
{
_convertibleTypes = new Type[]
{
typeof(System.SByte),
typeof(System.Byte),
typeof(System.Int16),
typeof(System.UInt16),
typeof(System.Int32),
typeof(System.UInt32),
typeof(System.Int64),
typeof(System.UInt64),
typeof(System.Single),
typeof(System.Double),
typeof(System.Decimal),
typeof(System.DateTime),
typeof(System.Guid)
};
}
return _convertibleTypes;
}
}
}
Usage:
string value = "2391203921";
dynamic converted = value.ConvertToType();
Your approach would work but, as you say, it's not that elegant.
I think you have a couple of ways to improve this code:
Move the array out of the function, as psubsee2003 said
Use the TryParse methods for cheaper testing (no catching involved) (e.g.: Int32.TryParse)
Actually write a parser that, after trimming,
Checks if the number is a GUID
Does it have '-' in it at a position > 0?
if(Guid.TryParse)
return result
return string (it can't be a number!)
Checks if the number is fractional (does it have a dot in it?)
Tries to convert to single, double, decimal using the various TryParse
If it fails return string
Does it start with a minus?
Try and parse as Int64, then check size and see where if fits (<256 -> ubyte, < 65536 ushort...)
If it fails return string
Try and parse as Int64
If it works check minimum size it fits into
If it fails it could be an integer, but too big, try parsing as double, if it fails return string
This is something I wrote previously that might be a help:
public static Boolean CanCovertTo(this String value, Type type)
{
var targetType = type.IsNullableType() ? Nullable.GetUnderlyingType(type) : type;
TypeConverter converter = TypeDescriptor.GetConverter(targetType);
return converter.IsValid(value);
}
The basic idea is if you pass the string and a Type that you want to test, you can check if a conversion will be valid before attempting to covert.
The problem with this design is that TypeConverter.IsValid() is just a wrapper (with some exception handling) for TypeConverter.CanConvertFrom() so you really aren't eliminating the exception handling, but since it is part of the BCL, I tend to think that is going to be a better implementation.
So you can implement this like so:
private static Type[] defaultTypes = new Type[]
{
typeof(System.SByte),
typeof(System.Byte),
typeof(System.Int16),
typeof(System.UInt16),
typeof(System.Int32),
typeof(System.UInt32),
typeof(System.Int64),
typeof(System.UInt64),
typeof(System.Single),
typeof(System.Double),
typeof(System.Decimal),
typeof(System.DateTime),
typeof(System.Guid)
};
public static dynamic ConvertToType(string value)
{
return ConvertToType(value, defaultTypes);
}
public static dynamic ConvertToType(string value, Type[] types)
{
foreach (Type type in types)
{
if (!value.CanConvertTo(type))
continue;
return Convert.ChangeType(value, type);
}
return value;
}
There is not really a great way to do this without the exception handling (even the exception handling in the TypeConverter.IsValid method), so you have to live with it if you really need such a method. But you can limit the need for the exception handling if you implement some of the suggestions in miniBill's answer in addition to some improvements in the design.
You could use Reflection to handle all the Parse types by calling the TryParse method, this will be a bit faster than handling multiple exceptions using ChangeType
public Type[] PredefinedTypes = new Type[]
{
typeof(System.SByte),
typeof(System.Byte),
typeof(System.Int16),
typeof(System.UInt16),
typeof(System.Int32),
typeof(System.UInt32),
typeof(System.Int64),
typeof(System.UInt64),
typeof(System.Single),
typeof(System.Double),
typeof(System.Decimal),
typeof(System.DateTime),
typeof(System.Guid)
};
public dynamic ConvertToType(string value)
{
foreach (var predefinedType in PredefinedTypes.Where(t => t.GetMethods().Any(m => m.Name.Equals("TryParse"))))
{
var typeInstance = Activator.CreateInstance(predefinedType);
var methodParamTypes = new Type[] { typeof(string), predefinedType.MakeByRefType() };
var methodArgs = new object[] { value, typeInstance };
if ((bool)predefinedType.GetMethod("TryParse", methodParamTypes).Invoke(predefinedType, methodArgs))
{
return methodArgs[1];
}
}
return value
}
Basically I want the following generic function:
public string StringOrNull<T> (T value)
{
if (value != null)
{
return value.ToString();
}
return null;
}
I know I could use a constraint such as where T: class, but T can be a primitive type, Nullable<>, or a class. Is there a generic way to do this?
Edit
Turns out I jumped the gun. This actually works just fine as this sample shows:
class Program
{
static void Main(string[] args)
{
int i = 7;
Nullable<int> n_i = 7;
Nullable<int> n_i_asNull = null;
String foo = "foo";
String bar = null;
Console.WriteLine(StringOrNull(i));
Console.WriteLine(StringOrNull(n_i));
Console.WriteLine(StringOrNull(n_i_asNull));
Console.WriteLine(StringOrNull(foo));
Console.WriteLine(StringOrNull(bar));
}
static private string StringOrNull<T>(T value)
{
if (value != null)
{
return value.ToString();
}
return null;
}
}
default Keyword in Generic Code
In generic classes and methods, one issue that arises is how to assign a default value to a parameterized type T when you do not know the following in advance:
Whether T will be a reference type or a value type.
If T is a value type, whether it will be a numeric value or a struct.
Here's a fun one:
public static class ExtensionFunctions{
public static string ToStringOrNull( this object target ) {
return target != null ? target.ToString() : null;
}
}
The cool part? This will work:
( (string) null ).ToStringOrNull();
So will this:
5.ToStringOrNull();
Extension functions are pretty awesome... they even work on null objects!
If you pass a primitive type, it will automatically be boxed, so you don't need to worry about the null comparison. Since boxing occurs automatically, you can even explicitly compare an int to null without an error, but the result will always be false (and you'll probably get a compiler warning telling you so).
You can use default keyword to return the default of T:
public string StringOrNull<T> (T value)
{
.....
return default(T).ToString();
}
Why generic?
public string StringOrNull (object value)
{
if (value != null){
return value.ToString();
}
return null;
}
I know the title is confusing so I'll try to explain better. Here's basically what I want to do inside a method:
if (record["id"] != DBNull.Value) _id = Convert.ToInt32(record["id"]);
else id = -1;
I want this to work for multiple types that I have stored in my database. (So if it's a string it converts it to a string and so forth). Any way to do this is fine, and I was trying to do it with a method. I got this far but C# won't automatically convert int to object. ideas?
private void Load(ref object var, object obj, object def)
{
if (var is int)
{
var = Convert.ToInt32(obj);
}
}
int _id;
Load(ref _id, record["id"], -1);
Just to clarify, my error is "cannot convert from ref int to ref object".
Thanks for any help.
You can use Convert.ChangeType() and make your method generic:
private void Load<T, U>(out T value, U obj, T defaultValue)
{
if (obj is DBNull)
value = defaultValue;
else
value = (T)Convert.ChangeType(obj, typeof(T));
}
Now you can just use it like this (simplified example, not sure what you need def for):
int id;
object foo = 42;
Load(out id, foo, 1);
First off, var is a reserved word in C# 3.0 and above (VS 2008 and later).
More to the point, I'd try setting this up as a generic; that way, your method can discover the true types of the variables you pass in, and work with them case-by-case as if they were strongly typed:
private void Load<TVar, TSet>(ref TVar var, TSet obj, TVar def)
{
//this is a little heavy-handed, but in pretty much any situation where
//this can fail, you just want the basic type.
try
{
if (var is IConvertible && obj is IConvertible)
var = (TVar)Convert.ChangeType(obj, typeof(TVar));
else
var = (TVar)obj; //there may just be an explicit operator
}
catch(Exception)
{
var = def; //defined as the same type so they are always assignable
}
}
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!).
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();}
}
}
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)));
}
}
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 });
}
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.
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>();
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.
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");
}
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);
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.
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(); }
}
}
You can do it in one line as below:
YourClass obj = (YourClass)Convert.ChangeType(YourValue, typeof(YourClass));
Happy coding ;)