JadedEric
JadedEric

Reputation: 2073

Retrieve value from a generic type in C#

I want to get the value of a generic type, passed through to a method, which looks like this:

    public virtual Domains.Vegetable.Result Get<T>() where T: Domains.Vegetable.Entity
    {
        var type = typeof(T);
        var info = type.GetProperty("Segment");
        var value = info.GetValue(type).ToString(); // throws exception

        // NOTE: I have tried this, which works, but this feels wrong
        //var entity = Activator.CreateInstance<T>();
        //var segment = entity.Segment;

        // omitted for brevity
    }

The entity called Domains.Vegetable.Entity looks something like this:

public abstract class Entity
{
    /// <summary>
    /// 
    /// </summary>
    public virtual string Segment { get; set; }
}

which is then implemented on any object of my choosing:

public class Tomato: Vegetable.Entity
{
    /// <summary>
    /// 
    /// </summary>
    public override string Segment => "/patch/seeded";
}

So, if I call my method: Get<Tomato>(), I'm expecting to get the value "/patch/seeded" back in the method mentioned above.

Is this even possible?

Upvotes: 0

Views: 2345

Answers (2)

Francesco Baruchelli
Francesco Baruchelli

Reputation: 7468

As everybody told you the exception is due to the fact that Segment is a property of the instances of tomato, and not of the type itself. And as you already found out creating an instance let you access it without exceptions. There are 2 ways you can follow to achieve the result you are expecting, but it's impossible to tell which is the better without knowing the context. The real question here is whether the property has to be static or not.

If it can be static just remove public virtual string Segment { get; set; } from Entity and modify public override string Segment => "/patch/seeded"; into public static string Segment => "/patch/seeded";. In this way your code will work.

The drawback is the fact that you loose the constraint of every class derived from Entity having to implement a property Segment (btw I would have it marked as abstract instead of virtual), and this means that you should check for info != null before using it in your method.

If you want/need to keep your Segment virtual/abstract there is no other way than creating an instance of T. This can be done via reflection as in your sample code, but maybe it'd be better adding a new() constraint on T in this way:

public virtual Domains.Vegetable.Result Get<T>() where T: Domains.Vegetable.Entity, new()
{
    var entity = new T();
    var segment = entity.Segment;

    // omitted for brevity
}

Upvotes: 0

Scott Hannen
Scott Hannen

Reputation: 29207

The reason this fails is that info is a PropertyInfo representing a property named Segment belonging to type Entity. (Assuming that Entity has such a property. If it doesn't, info is null.)

In order to retrieve the value of that property you need an instance of Entity, like this:

var value = info.GetValue(instanceOfEntity).ToString();

Instead, you're trying to read the Segment property an Entity from a Type.

var value = info.GetValue(type).ToString();

You're trying to read the property of one class (Segment) from an instance of another property (Type.) Even if Type had a property named Segment it wouldn't work, because info is a property of Entity and can only be read from an instance of Entity.

In your method there's no instance of an Entity. That raises the question, what are you trying to read the property from? If the method looked like this:

public virtual Domains.Vegetable.Result Get<T>(T entity) where T: Domains.Vegetable.Entity
{
    var type = typeof(T);
    var info = type.GetProperty("Segment");
    var value = info.GetValue(entity).ToString(); // throws exception

    // other stuff
}

then the reflection might work, assuming that the property exists. But if the property existed you would just do this:

public virtual Domains.Vegetable.Result Get<T>(T entity) where T: Domains.Vegetable.Entity
{
    var value = entity.Segment.ToString(); // throws exception

    // other stuff
}

But that's beside the point. You're getting an exception because you're trying to read a property from one class on an instance of a different class.

Upvotes: 1

Related Questions