I have the following interface:
public interface IDynamicData: IPersistent
{
string Name { get; }
DataType Type { get; set; }
string InputFormat { get; set; }
dynamic Value { get; }
string DisplayValue { get; }
}
I have a couple of classes that implement this interface, but the one I am interested in is this one:
public class DynamicInput : IDynamicData
{
public string Name { get; private set; }
private DataType _Type;
public DataType Type
{
get { return _Type; }
set
{
_Type = value;
switch (Type)
{
case DataType.String:
Mapping = new StringMap();
break;
case DataType.Numeric:
Mapping = new DoubleMap();
break;
case DataType.DateTime:
Mapping = new DateTimeMap();
break;
default:
Mapping = new StringMap();
break;
}
}
}
[Browsable(false)]
public dynamic Value { get; private set; }
[Browsable(false)]
public string DisplayValue
{
get
{
return Value != null && (Type.Equals(DataType.DateTime) || Type.Equals(DataType.Numeric))
? Value.ToString(InputFormat)
: Value;
}
}
}
Now, the key here is the dynamic Value property. This value is populated from a string input using the StringMap, DecimalMap, DoubleMap and DateTimeMap classes, where the key method is (similar) to this one:
public override dynamic ProcessInput(string input, int index, string inputFormat, double multiplier, char splitOn = ',')
{
_IsValid = false;
try
{
_Input = input;
double tmp;
_IsValid = Double.TryParse(input.Split(splitOn)[index], out tmp);
return tmp * multiplier;
}
catch
{
_IsValid = false;
return 0;
}
}
This seesm to work fine, and I effectively have a strongly typed Value property at runtime (at least how I understand it). I would like to know when this definition of the type occurs though - when the object is instantiated, or when the Value is assigned to.
I would like to be able to let the user change the type of the Value property at runtime, which involves setting the DataType property to either string/DateTime/double etc. This however, sometimes results in a RuntimeBindingException being thrown at runtime. It doesn't happen all the time, only sometimes, which, I am assuming occurs when the DataType property is changed in between the Value property being set and the DisplayText property being read. That is where the error occurs, in this getter:
public string DisplayValue
{
get
{
return Value != null && (Type.Equals(DataType.DateTime) || Type.Equals(DataType.Numeric))
? Value.ToString(InputFormat)
: Value;
}
}
I would just like to understand why this happens, and if my analysis is correct, and potentially if there are any workarounds to this problem. If its not possible then thats fine, I will lock down editing of the type of the IDynamicData object.
EDIT
I have experienced this only once - hard to replicate. The instance was, the object was created with DataType.Numeric. I had input strings like "12.345" coming in, every second and being parsed then assigned to the Value property. Then, while I am still receiving data, I changed to DataType.String. This is where the exception occurred. That is, DoubleMap was returning a "dynamic" double, then while input data is still coming in, the StringMap returned a "dynamic" string representation of `12.345'.
I suspect you have a value such as the string "15" but the Type property is set to, for example, DataType.Numeric. In such a case, your code would attempt to call the parameterized overload of ToString(). Because the receiving object is a string, there is no such overload, so the binder throws an exception.
That is a bit speculative. If you could post a working example that demonstrates the problem, or at least describe the specific values you are using when the exception is thrown, we could be more certain of its cause.
If you want to convert a string representation of a number to an int or double representation of that number, you will need to convert the actual value in the Value property. (The actual type of the property is object, by the way, so value routes stored here will be boxed.) The best solution for this would depend somewhat on how you want the calling code to look.
EDIT in response to your edit: the other possibility is that the Value object is a numeric data type such as double, but the Type property is DataType.String. In that case, your code tries to return the double directly, which would cause the binder to fail, just as
string s = 1.2
would fall to compile. In that case, you could use ToString(), thus:
return Value != null && (Type.Equals(DataType.DateTime) || Type.Equals(DataType.Numeric))
? Value.ToString(InputFormat)
: Value.ToString();
That is more of a workaround than a solution, however. I would examine the runtime type of the object and get rid of the Type property.
Related
I have a WCF service which has a data member
[DataMember(Name = "callToPhone")]
public bool IsCallToPhone { get; set; }
inside one of the DataContracts. When I call this service and pass "" for the variable IsCallToPhone, I get a deserialization error. I understand that the an empty value cannot be passed to a Bool, but is there any way we could make it accept the value and assign a default false for such case.
This is my error
{
"message": "There was an error deserializing the object of type SciGame.CMP.API.Contracts.Player.CreatePlayerRequest. The value '' cannot be parsed as the type 'Boolean'."
}
What I tried - I tried by making the variable as a nullable type.
public bool? IsCallToPhone { get; set; }
also tried to implement the getter and setter property
public bool IsCallToPhone
{
get { return IsCallToPhone; }
set
{
if (Convert.ToString(value) == "")
{
value = false;
}
IsCallToPhone = value;
}
}
Either of these is not working, is there anything wrong that I am doing
I don't understand this square brackets, what i have to create, that this code line is working, only for testing.
filetype = dataBandREP["VRZ.Parent.SIGNATUR"].ToString();
C# can overload operator, including square brackets.
public class Example
{
public string this[string s] // square bracket operator with string argument
{
get
{
return somethingToReturnString;
}
set
{
somethingToSetString = value;
}
}
public string this[int i] // square bracket operator with int argument
{
get
{
return somethingToReturnInt;
}
set
{
somethingToSetInt = value;
}
}
}
The square brackets mean you are referencing a collection and within that collection, you want "VRZ.Parent.SIGNATUR". [] is an indexer with a string as argument.
This code:
filetype = dataBandREP["VRZ.Parent.SIGNATUR"].ToString();
uses the indexer of some sort of collection, referring to an object, then converting it to its string format in order to populate the filetype variable.
A very common usage of this is with arrays and DataColumn collections. String indexers allow you to fetch a value based on, say, a column header, instead of knowing what integral index in the DataTable.Columns collection is associated with the column.
You're getting different behavior when debugging because the validity of "VRZ.Parent.SIGNATUR" as a string indexer has changed, probably because of a change in the actual data source. That's assuming that you're getting a null reference or similar. Without knowing what exception you're getting, we can't help much more than that.
Below syntax also works.. my class is derived from a ReadOnlyDictionary of some type "AttributeValue" and it exports double values. It gives the user an error box when
the value is not found.
public abstract class AttribDoubleReader: IReadOnlyDictionary<string, AttributeValue>
{
public double this[string key] => GetValue(key);
private double GetValue(string key)
{
if (TryGetValue(key, out AttributeValue value))
{
return value.AsDouble();
}
else
{
MessageBox.Show("ERROR: KEY "+ key + " NOT FOUND.");
throw new KeyNotFoundException();
}
}
}
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...)
I have a task of creating RESTfull Services using ASP.NET MVC4 Web API. One of the Service Method is looking like below:
[HttpPost]
public HttpResponseMessage TagAdd([FromBody] Tag tag)
{
HttpResponseMessage result;
Tag tmpTag = new Tag();
tmpTag.Name = tag.Name;
tmpTag.DataType = tag.DataType;
}
Here Tag looking like this:
public class Tag : Dictionary<Tag.Property, object>
{
public enum Property : int
{
Name = 0,
DataType
}
public enum NativeDataType : int
{
Undefined = 0,
Scaled,
Float,
}
public string Name
{
get
{
object value;
return TryGetValue(Property.Name, out value) ? (string)value : null;
}
set
{
this[Property.Name] = value;
}
}
public NativeDataType DataType
{
get
{
object value;
return TryGetValue(Property.DataType, out value) ? (NativeDataType)(value) : NativeDataType.Undefined;
}
set
{
this[Property.DataType] = value;
}
}
}
Here Current issue is, the enum NativeDataType is treating as long when i sent below JSON request:
{"Name":"ABCD","DataType":2}
unfortunately, Tag class was defined in another assembly. Because of that
tmpTag.DataType = tag.DataType; statement leading to exception(invalid cast) because of cross boundary issue.
How can is fix this issue?
Why enum was treating as long? even i observed uint, int, long, enum were treating as long and float, double were treating as double on call stack.
i can get exact value like below
object value = null;
tag.TryGetValue(Tag.Property.DataType, out value);
tmpTag.DataType = Convert.ToInt32(value);
but, rather accessing one by one Tag elements and convert them to exact type, is there any easiest way to automatically convert and sent to another assembly?
Instead of converting it can you just do a simple cast? Also Inheriting from int should default to INT32 but for some reason your getting int64 (long). Try inheriting from int32 explicitly.
I am wondering what type the 'value' keyword in a property takes.
so:
public class Test
{
string _numberAsString;
int _number = -1;
public Test() {}
public string NumberAsString
{
get { return _numberAsString; }
set { _numberAsString= value; }
}
public int Number
{
get { return int.Parse(_numberAsString); }
set { _number = value; }
}
}
//elsewhere
Test t = new Test();
t.Number = 5;
Now, this doesn't compile, as I'd expect. The type of 'value' is determined by the return type of the property, is that correct? I couldn't find anything to that effect, perhaps it's too obvious (I haven't read the language specs, presumably there's something in there).
I ask, because I would like to set the property with a type that is then converted into the type of the getter. I suppose this doesn't really make sense.
It seems I will have to achieve this by creating a couple of methods.
Yes, the type of 'value' is determined by the return type of the property. What exactly are you trying to accomplish? Do you want to be able to set either Number or NumberAsString to a valid value and get a result back out from either property?
If that's the case you need to do something like this:
public class Test
{
string _numberAsString;
int _number = -1;
public Test() {}
public string NumberAsString
{
get { return _numberAsString; }
set { _numberAsString= value; }
}
public int Number
{
get { return int.Parse(_numberAsString); }
set { _numberAsString = value.ToString(); }
}
}
This would allow you to do this:
Test t = new Test();
t.Number = 5;
Console.WriteLine(t.NumberAsString); // should print out "5"
t.NumberAsString = "5";
Console.WriteLine(t.Number); // should print out "5"
You can't have a get and a set for a property that take different types. The only option you have is to store it internally as one type and then in either the get or the set (or both) perform the conversion from one type to another.
The type of the value in the setter is the type of the property - you cannot pass a string to a property that is an int, this must first be parsed to an int.
Strings in .net cannot be coerced into any other type (in the way perl, awk and many other dynamic languages allow) they can only be treated as string, or as their parent class object.
you could do the follwoing:
private int _number;
private string _numberAsString;
public string NumberAsString
{
get { return _numberAsString; }
set { LinkedSet(value); }
}
public int Number
{
get { return _number; }
set { LinkedSet(value); }
}
private void LinkedSet(string s)
{
this._number = int.Parse(s);
this._numberAsString = s;
}
private void LinkedSet(int i)
{
this._numberAsString = i.ToString();
this._number = i;
}
obviously the NumberAsString setter can throw a FormatException the Number setter cannot.
I do not recommend this in general though unless you really need to avoid converting the number to a string and the string to a number on a regular basis (at which point you can make the setters lazy in their evalition of the linked value - albeit changing the semantics of the exception from on set of the string to on get of the int - likely to be at best annoying or at worst a nasty bug.
Why not just do
Test t = new Test();
t.Number = 5;
Console.WriteLine(t.Number.ToString());
It strikes me that you're trying to be crafty for no real reason.
I think you are trying to use a property here when a method would be more appropriate. A property isn't supposed to have any unforeseen side effects, which this would by performing a type conversion on it (if it were possible).
It doesn't make sense, to me, to allow a setter on NumberAsString.
Why not this?:
public class Test
{
int _number = -1;
public Test() {}
public string NumberAsString
{
get { return _number.ToString(); }
}
public int Number
{
get { return _number; }
set { _number= value; }
}
}