0

I was trying to create a class were one of the properties was generic without the class itself being generic. I discover that you can't do that; generic properties aren't allowed. A bit of digging here too me to the thread Making a generic property were I found a work around that works nicely for me.

Subsequently, I ended up with this class...

[Serializable]
public class ConfigurationItem
{
    private readonly Type type;

    public string Description { get; set; }

    public IDictionary<string, ConfigurationItem> Items { get; private set; }

    public string Name { get; set; }

    public string Summary { get; set; }

    public object Value { get; private set; }

    public ConfigurationItem(string name, Type type = null, object value = null)
    {
        this.Name = name;
        this.type = type ?? typeof(string);
        this.Value = value;
        Items = new Dictionary<string, ConfigurationItem>();
    }

    public string Export()
    {
        return JsonConvert.SerializeObject(this);
    }

    public T GetValue<T>()
    {
        return (T)Convert.ChangeType(Value, type);
    }
}

Now the only issue I have is that if I want to get the value, properly cast, I have to supply the type when I call GetValue(). Instinctively, I can't help but feel, given that the class knows the type that should be returned, it should be possible for me to construct a GetValue() method that doesn't require any additional information from me.

I can't figure out how.

I did find the thread Getting a generic method to infer the type parameter from the runtime type which appears to suggest that it is possible, but I understand very little about Reflection and couldn't make any sense of what was being said.

Is there a way of constructing a GetType() method that doesn't require that I supply the generic type? And can you explain it in a way that my feeble brain can comprehend it?

EDIT A number of people have actually pointed out that really I don't need to do this anyway, nonetheless as a learning exercise I followed up the suggestion from @ShoaibShakeel to look at the C# dynamic type and came up with these additional methods...

    public dynamic GetValue()
    {
        return typeof(ConfigurationItem)
            .GetMethod("GetReturnValue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
            .MakeGenericMethod(type)
            .Invoke(this, new object[] { });
    }

    private T GetReturnValue<T>()
    {
        return (T)Convert.ChangeType(Value, type);
    }
Community
  • 1
  • 1
Stuart Hemming
  • 1,553
  • 2
  • 21
  • 44
  • 1
    Without specifying type CLR won't be able to know how much data to read from memory and how to parse it. I think you should look at `dynamic` type . https://msdn.microsoft.com/en-us/library/dd264736.aspx – Shoaib Shakeel Oct 21 '15 at 09:17
  • 1
    Why not just `object`? Injecting a run-time evaluated type does not make sense here. After all, the return type could only help you for compile time type checks. This is obviously not possible in this case. So `object` is just fine. Or, if the caller knows the type, then your generic method is probably preferable. – Nico Schertler Oct 21 '15 at 09:21
  • @NicoSchertler I understand your point, I guess I didn't about it in that respect. However, see my edit. – Stuart Hemming Oct 21 '15 at 09:59
  • And why not just `return Value;`? There's no need to call the generic function. – Nico Schertler Oct 21 '15 at 10:55

3 Answers3

1

Instinctively, I can't help but feel, given that the class knows the type that should be returned

While it is true that the Value object knows its own type, this information is only available at runtime though (once there is an actual object that has a type). So in order to have type information at compile time, you need to supply the necessary information. After all, it’s impossible to know statically what object is being assigned there.

But assuming that GetValue() was able to return the typed object automatically, what would you want to do with it?

var x = configurationItem.GetValue();

So what type should x be of, and what would you want to do with it afterwards? Do you only want to print it or something? Then object is enough. Do you want to calculate something with it? Then you would already require that it’s an int, or a float or something, so you would need an actual static type—which is why you would have to supply that type information.

Just because the return type is object that does not mean that the object itself loses the type information. A string assigned to an object property will still be a string, even if you retrieve it much later. And if you expect a string and want to do something with it, then you can surely just cast it to a string.

poke
  • 369,085
  • 72
  • 557
  • 602
  • You are correct in what you say, I just didn't like the idea of having to write out the generic type all the time (I'm naturally lazy). However, see my edit. – Stuart Hemming Oct 21 '15 at 10:00
  • 1
    @StuartHemming The question still remains: Why would you want to have a static type there? There is no need to have it, if you don’t want to do anything type-specific afterwards with the object. And the `dynamic` solution also gives you no benefit since you 1) could just return `Value` as `dynamic` directly, and 2) since `dynamic` gives you as much type information as `object`. – poke Oct 21 '15 at 10:03
  • I guess I was remiss in omitting the fact that I _do_ want (potentially, at least) to be able to do type-specific stuff with the value when I get it back. Returning the Value property as `dynamic` didn't occur to me as I really didn't know about it. The only point in following this through to the conclusion I have is by way of a learning exercise for me and teaching exercise for anyone like me faced with a similar isssue in the future. – Stuart Hemming Oct 21 '15 at 10:19
0

No.

The compiler cannot infer the type from the variable it is assigned to since it is ambiguous, especially in large inheritance trees.

Consider the line

object o = configurationItem.GetValue<int>();

int may be a valid conversion for your value but object isn't, since objectdoes not implement the IConvertible interface (required by Convert.ChangeType().

Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
0

I was able to get my class to return a value, cast correctly, using reflection and dynamics using the following additions to my original class.

public dynamic GetValue()
{
    return typeof(ConfigurationItem)
        .GetMethod("GetReturnValue", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
        .MakeGenericMethod(type)
        .Invoke(this, new object[] { });
}

private T GetReturnValue<T>()
{
    return (T)Convert.ChangeType(Value, type);
}
Stuart Hemming
  • 1,553
  • 2
  • 21
  • 44