Steve
Steve

Reputation: 715

Using reflection to instantiate classes

I'm trying to search a specific namespace and return instances of all classes that implement the specified interface and have the specified base class.

Here is the code

private List<T> GetInstancesWithInterface<T, T2>(string @namespace)
{
    var tList = Assembly.GetExecutingAssembly()
                         .GetTypes()
                         .Where(t => t.Namespace == @namespace)
                         .Where(t => t.BaseType == (typeof (T)) && t.GetInterfaces().Contains(typeof(T2)))
                         .Select(t => (T)Activator.CreateInstance(typeof(T), this))
                         .ToList();
    return tList;
}

And here is the calling code

var examples = GetInstancesWithInterface<DrawableGameComponent, IResettable>("GamePhysics.Physics");

foreach (var example in examples)
{
    Debug.Assert(example is IResettable);
    var r = example as IResettable;
    _examples.Add(r);
    Components.Add(example);
    if (Components.Count == 0) continue;
    example.Enabled = false;
    example.Visible = false;
}

The problem I am having is the Assert fails because I get back a list of DrawableGameComponent which cannot be treated as IResettable so the code var r = example as IResettable always returns null.

Additional info

I should have also mentioned that DrawableGameComponent does not implement the IResettable interface. My three example classes that I'm trying to fetch all have DrawableGameComponent as the base class and they also implement IResettable.

Additional thoughts

I was thinking that perhaps I should create a new abstract class called Example which implements DrawableGameComponent and IResettable and then my concrete example classes can just implement the Example base class. I suppose this might be better design but I am also interested to know if I can get it working as it is.

Upvotes: 1

Views: 248

Answers (2)

valverij
valverij

Reputation: 4951

(Posting as requested in comments. Thanks for waiting)

Take a look at the Select portion of the LINQ query:

.Select(t => (T)Activator.CreateInstance(typeof(T), this))

Passing typeof(T) into Activator.CreateInstance will create a new object of type T. Since you are actually looking to create objects out of your found types, you would want to change this to:

.Select(t => (T)Activator.CreateInstance(t, this))

This will ensure that the Activator creates instances of the types found in the previous Where.

As Lee also mentioned, using something like IsSubclassOf or IsAssignableFrom in your Where expression is preferable to a straight up == when searching for subclasses. From my experience, they work about the same, except IsAssignableFrom will also work with interfaces.

Upvotes: 3

Lee
Lee

Reputation: 144206

You're passing the wrong type to Activator.CreateInstance, you should change it to:

.Select(t => (T)Activator.CreateInstance(t, this))

you might also want to use Type.IsSubclassOf in your Where clause instead of looking at the base class directly.

Upvotes: 4

Related Questions