Reputation: 13565
I am using dynamic keyword to work with an outside assembly and it works fine as far as accessing it's methods and primitive type members are concerned. So for example my class dynamically loaded class looks like followig:
public class Student
{
public bool IsGood { get; set; }
public StudentType St { get; set; }
public University University { get; set; }
}
I can dynamically load the object from assembly by doing something like:
var assembly = Assembly.LoadFrom("//path");
Type type = assembly.GetType("TestFrameWork.Student");
var student = Activator.CreateInstance(type);
It fails on following code:
student.IsGood = true;
student.St = TestFrameWork.StudentType.SomethingElse;
Student Type is an Enum from dynamically loaded assembly;
I can get the student object. Now is the interesting part. I can call it's methods. I can get all its properties. I can set it's primitive properties
So I can pretty much do student.IsGood = true; and it will set that property. That is equally true if I had other primitive properties of type int, float etc.
BUT
When I try to set it to a property which is native to dynamically loaded assembly It fails with RuntimeBinderException
So for example,
if I try to do student.University = new University() where University is a native type of loaded assembly. It fails.
here is the stack trace:
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Boolean nonPublic) at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark) at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) at System.Reflection.Assembly.CreateInstance(String typeName, Boolean ignoreCase, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) at System.Reflection.Assembly.CreateInstance(String typeName) at TaskManagementFramework.PluginModule.CreateInstanceT in c:\Dropbox\CPTFramework_DynamicLoading\TaskManagementFramework\Plugin Loading\PluginModule.cs:line 19 at TaskManagementFramework.PluginLifecycleManager.GetPluginInstance(String id, String parentXmlSectionDescription, Type expectedInterface, Boolean useSingleInstance, IPlugin& plugin) in c:\Dropbox\CPTFramework_DynamicLoading\TaskManagementFramework\Plugin Loading\PluginLifecycleManager.cs:line 53 at TaskManagementFramework.PluginsXmlParser.ParsePlugins(XElement pluginsListElement, String parentXmlSectionDescription, PluginLifecycleManager pluginLifecycleManager, List`1& plugins) in c:\Dropbox\CPTFramework_DynamicLoading\TaskManagementFramework\XML Parsing\PluginsXmlParser.cs:line 39
Any idea why? I searched through internet nothing specifically addresses this isssue..
Upvotes: 4
Views: 728
Reputation: 2613
I believe the correct way to do this sort of thing is like this
class Program
{
static void Main(string[] args)
{
object d = new Person();
d.GetType().GetField("Parent").SetValue(d,new Person("Test"));
Console.WriteLine(((Person)d).Parent.name);
Console.Read();
}
}
public class Person{
public String name = "";
public Person Parent;
public Person()
{
}
public Person(String s)
{
name = s;
}
}
Cast the University into an object and set the value using reflection. Here is an example with simple code, just replace Person with whatever university is :
If this does not work, it probably means that university inside of your native code cannot be parsed into university because they are seen as two different types. So create an instance of the university that object is using and set values to that. Once you've done that set that instance you created.
like such (its psuedocode btw) :
var assembly = Assembly.LoadFrom("//path");
Type type = assembly.GetType("TestFrameWork.University");
var Uni = Activator.CreateInstance(type);
type = assembly.GetType("TestFrameWork.Student");
var student = Activator.CreateInstance(type);
student.University = Uni;
Edit: The enum The enum is a little bit more tricky Here is something I definitely think will help:
class Program
{
static void Main(string[] args)
{
Person p = new Person();
Type t = p.Enuminator.GetType();
dynamic e = Activator.CreateInstance(t);
FieldInfo [] FieldArray = t.GetFields();
p.GetType().GetField("Enuminator").SetValue(p, FieldArray[3].GetValue(e));
Console.WriteLine(p.Enuminator);
Console.Read();
}
}
public class Person{
public String name = "";
public Person Parent;
public Enumtest Enuminator;
public Person()
{
}
public Person(String s)
{
name = s;
}
}
public enum Enumtest
{
chicken,
monkey,
frog
}
It definitely is ugly; however you should be able to pull this off as long as you know the structure you are trying to import ahead of time. Also you can change Dynamic type with Object type. I only did that because I trying get it right.
Upvotes: 1
Reputation: 13565
To summerize: .NET framework 4.0 absolutely does not support directly assigning the native types of dynamically loaded assembly.
Which means that we will have to do something that has been suggested in the answer above which is:
Type type = assembly.GetType("TestFrameWork.Student");
type.GetProperty("University").SetValue(student, new University(), null);
If you are loading the entire assembly at runtime which you probably are you will not have University type ar compile time as well. In that case your solution is
type2 = assembly.GetType("TestFrameWork.University");
type.GetProperty("University").SetValue(student, Activator.CreateInstance(type2`enter code here`), null);
Hopefully this helps.
But I think this is a bug in .Net 4.0. I might be wrong though
Upvotes: 3