Reputation: 9425
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'.
Upvotes: 1
Views: 2410
Reputation: 43046
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.
Upvotes: 1