Jedi_Maseter_Sam Jedi_Maseter_Sam - 2 months ago 9
C# Question

C# casting object to class with type argument

I have a class called Property that stores one value and raises an event if the value changes. There is a different class called Observable that I want to contain a dictionary of properties, and I have an example below. How can I get the type of a property stored in the dictionary so I can modify the object?

Property Class:

public class Property<T>
{
public T Value { get; set; }
}


Observable Class:

public class Observable
{
public readonly Dictionary<string, object>
Properties = new Dictionary<string, object>();

public Property<T> Get<T>(string name)
{
object obj;
Properties.TryGetValue(name, out obj);
return (Property<T>) obj;
}
}


EDIT: This is the error I am getting -
Unhandled Exception: System.InvalidCastException: Unable to cast object of type 'System.String' to type 'ConsoleApplication.Property`1[System.String]'.


Also, if I could get rid of the Get function and replace it with something that does not require knowing the type that would be much better.

Update: Is there a way to get the type by hashing the name with a type?

Answer

EDIT (for updated post):

Is there a way to get the type by hashing the name with a type?

Is something like this what you're going for? dotnetfiddle link

public interface IProperty
{
    object Value { get; }
    Type TypeOfValue { get; }
}

public class Property : IProperty
{
    public object Value { get; set; }
    public Type TypeOfValue { get; set; }
    public override string ToString() { return TypeOfValue.FullName + ": " + Value; }
}

public class Observable
{
    public readonly Dictionary<string, Property> 
        Properties = new Dictionary<string, Property>();

    // use interface so consumer of your property can't mess with the
    // type or value stored in your dictionary within Observable
    public IProperty Get(string name)
    {
        Property obj;
        if (Properties.TryGetValue(name, out obj)){
            return obj;
        }

        // throw something which makes more sense to you
        throw new ArgumentException(name + " does not exist in the dictionary");
    }

    // sample of how you'd store these properties
    public void Set(string name, object val)
    {
        if (Properties.ContainsKey(name)){
            Properties[name].Value = val;
            Properties[name].TypeOfValue = val.GetType();
        } else {
            Properties[name] = new Property {
                Value = val,
                TypeOfValue = val.GetType()
            };
        }
    }
}

ORIGINAL ANSWER:

It's possible that you're accidently inserting a string into your dictionary inside of Observable instead of a Property<string>, and if so the problem isn't in the code that you've posted and instead wherever you're inserting a string instead of a Property<string>.

Two solutions if you don't like the invalid cast exception.

Solution 1: customize the errors your function throws.

If you'd like to keep the signature of Get<T>(...) I'd recommend some error handling. The error you're seeing is because the property in your dictionary is of type string, not of type Property<string>. The following code will give you something to work with for more error handling.

public class Observable
{
    public readonly Dictionary<string, object> 
        Properties = new Dictionary<string, object>();

    public Property<T> Get<T>(string name)
    {
        object obj;
        if (Properties.TryGetValue(name, out obj)){
            try {
                return (Property<T>) obj;
            } catch(InvalidCastException){
                // throw something which makes more sense to you
                throw new ArgumentException("Could not cast the given object to the desired type");
            }
        }

        // throw something which makes more sense to you
        throw new ArgumentException(name + " does not exist in the dictionary");
    }
}

Solution 2: return a general object

public class Observable
{
    public readonly Dictionary<string, object> 
        Properties = new Dictionary<string, object>();

    public object Get(string name)
    {
        object obj;
        if (Properties.TryGetValue(name, out obj)){
            return obj;
        }

        // or do whatever you want when the given key doesn't exist.
        return null;
    }
}
Comments