Activator Question

D

Dan Dorey

My goal is to pass in an assembly and have the method return an
instance of any classes that inherit from the generic type T.

My problem is that I'm unable to cast from the result of
Activator.CreateInstance(curType) to T even though curType is a
concrete class of T (T is abstract in this case). I'm unsure why I
can't make this cast. In another method where I don't have the
restriction of T being a class, this approch works fine when T is an
interface.


I kind of feel like I'm going about this the wrong way, so if anyone
has suggestions on a better approach, please let me know :)


Thanks,
Dan


public static List<T> GetClassesByClassName<T>(Assembly assembly,
object[] classParams) where T : class
{
List<T> ret = new List<T>();


foreach (Type curType in assembly.GetTypes())
{


if (InheritsFrom(curType, typeof(T)) &&
curType.IsAbstract == false)
{
if (classParams == null)
{


ret.Add((T)Activator.CreateInstance(curType));
}
else
{


ret.Add((T)Activator.CreateInstance(curType, classParams));
}
}
}


return ret;
}


private static bool InheritsFrom(Type curType, Type targetType)
{
bool ret = false;


while (curType.FullName != "System.Object")
{
if (curType.FullName == targetType.FullName)
{
ret = true;
break;
}
else
{
curType = curType.BaseType;
}
}


return ret;
}
 
J

Jon Skeet [C# MVP]

Dan Dorey said:
My goal is to pass in an assembly and have the method return an
instance of any classes that inherit from the generic type T.

My problem is that I'm unable to cast from the result of
Activator.CreateInstance(curType) to T even though curType is a
concrete class of T (T is abstract in this case). I'm unsure why I
can't make this cast. In another method where I don't have the
restriction of T being a class, this approch works fine when T is an
interface.

I kind of feel like I'm going about this the wrong way, so if anyone
has suggestions on a better approach, please let me know :)

Well, instead of InheritsFrom, you'd be better off using
Type.IsAssignableFrom or Type.IsSubclassOf.

However, I don't get any compile-time errors with your code.

Could you post a short but complete program which demonstrates the
problem?

See http://www.pobox.com/~skeet/csharp/complete.html for details of
what I mean by that.
 
D

Dan Dorey

well interestingly enough, I made a quick test app so I could show you
the problem, but it appears my test app works just fine. Also note
that the casting error I was getting before was at runtime not when
compiling.

I had to use my ugly method of testing for inhertience because
IsSubclass and IsAssignableFrom were returning false for me.
Interestingly IsAssignableFrom returns true if my class Action is NOT
abstract. Clearly I'm missing something about the workings of these
methods.

I'll have to start changing my test app so that it's closer and closer
to the real thing and see when it breaks. Here is the test app and if
you have any thoughts let me know. Thanks for getting me to do the
test app... at least I know it's possible now with this approch.

Thanks,
Dan


*******************************************************************
From a startup project, you have to add this line of code:

DynamicLoader.GetClassesByClassName<Node>(Directory.GetCurrentDirectory()
+ @"\PluginTester.dll", null);

********************************************************************

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;

namespace PluginTester
{
public abstract class Node
{
public abstract string NodeID { get; }
}

public abstract class Action : Node
{
//public abstract string NodeID { get; }
public abstract string ActionID { get; }

}

public class MyAction : Action
{
public override string NodeID
{
get
{
return "MyAction";
}
}
public override string ActionID
{
get { return "MyActionID"; }
}
}

public class DynamicLoader
{
public static List<T> GetClassesByClassName<T>(string
fileName, object[] classParams) where T : class
{
List<T> ret = new List<T>();

Assembly assembly = Assembly.LoadFrom(fileName);
foreach (Type curType in assembly.GetTypes())
{

if (InheritsFrom(curType, typeof(T)) &&
curType.IsAbstract == false)
{
if (classParams == null)
{
ret.Add((T)Activator.CreateInstance(curType));
}
else
{
ret.Add((T)Activator.CreateInstance(curType,
classParams));
}
}
}

return ret;
}

private static bool InheritsFrom(Type curType, Type
targetType)
{
bool ret = false;

while (curType.FullName != "System.Object")
{
if (curType.FullName == targetType.FullName)
{
ret = true;
break;
}
else
{
curType = curType.BaseType;
}
}

return ret;
}

}




}
 
D

Dan Dorey

Well I just copied all the code from my mainline application into the
test app and it works there :S I should be able to figure it out from
here even if it doesn't make sense at the moment.
 
J

Jon Skeet [C# MVP]

Dan Dorey said:
well interestingly enough, I made a quick test app so I could show you
the problem, but it appears my test app works just fine. Also note
that the casting error I was getting before was at runtime not when
compiling.

Right. Should the type involved definitely have "worked"?
I had to use my ugly method of testing for inhertience because
IsSubclass and IsAssignableFrom were returning false for me.
Interestingly IsAssignableFrom returns true if my class Action is NOT
abstract. Clearly I'm missing something about the workings of these
methods.

Could you give a concrete example of this? It always returns the same
result for me, using:

Console.WriteLine ("ret={0}", ret);
Console.WriteLine ("IsAssignableFrom={0}",
targetType.IsAssignableFrom(curType));

at the bottom of InheritsFrom.
I'll have to start changing my test app so that it's closer and closer
to the real thing and see when it breaks. Here is the test app and if
you have any thoughts let me know. Thanks for getting me to do the
test app... at least I know it's possible now with this approch.

My only other request is that you include a suitable Main method so we
know what we're dealing with :) Also, if you use the currently
executing assembly rather than using a filename, it'll make testing
easier.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top