Question

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'.

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top