2010-09-07 2 views
3

Я использую отражение для вызова метода из другой dll. Этот метод принимает объект класса в качестве входного. Как я могу назвать этот метод. Я пробовал копировать один и тот же класс в обе библиотеки dll, и я создал объект этого класса и передал его. Он выдает ошибку конвертации invliad в самое время complie. Затем я попытался позволить функции взять объект в качестве аргумента, а затем попытался применить его к требуемому классу. Это бросает недопустимое исключение литья во время выполнения. это то, что я пыталсяReflection: Как вызвать методы, которые принимают объект класса в качестве ввода

  Test objTest = new Test("name","type");    
      Assembly assembly = Assembly.Load("MyProject.Components");    
      Type dllType = assembly.GetType("MynameSpace.MyClass"); 
      if (dllType != null) 
      { 
       MethodInfo m = dllType.GetMethod("MyFunction"); 
       object objdll; 
       objdll = Activator.CreateInstance(dllType); 
       object[] args = { objTest }; 
       if ((m != null)) 
       {       
        strReturnValue += (string)m.Invoke(objdll, args);       
       } 
      } 

Во втором DLL метод подобен

public string MyFunction(Test obj) 

Моя проблема заключается в том, что тест класса в другой сборки (классы в две разные узлы)

+1

Пожалуйста, включите подпись метода, которую вы пытаетесь вызвать ('MyFunction' специально, из' dllType.GetMethod («MyFunction»); '). Кроме того, где происходит исключение? – Femaref

ответ

0

Если вы имеете в виду объект класса объект, который является Type объект, то вы можете просто передать тип объекта в качестве параметра ... например.

object[] args = {typeof(typeneeded)}; 

или

object[] args = { assembly.GetType(typeneeded) }; 
+0

Моя проблема в том, что тест класса находится в другой сборке (классы находятся в двух разных сборках) – ash

+0

Затем используйте второй метод, и все должно быть в порядке! –

+0

Затем, как придать некоторую ценность аргументам [0] i? – ash

0

MethodInfo.Invoke() объявляется следующее:

public Object Invoke(
    Object obj, 
    Object[] parameters 
) 

Первый параметр указывает объект для работы, второй определяет параметры для функции.

Я реплицировать свой код в LINQPad, и это работает отлично:

void Main() 
{ 
    string strReturnValue = ""; 
    Test objTest = new Test("name","type");    
    Type dllType = typeof(MyClass); 
    if (dllType != null) 
    { 
     MethodInfo m = dllType.GetMethod("MyFunction"); 
     object objdll; 
     objdll = Activator.CreateInstance(dllType); 
     object[] args = { objTest }; 
     if ((m != null)) 
     {       
      strReturnValue += (string)m.Invoke(objdll, args);       
     } 
    } 
} 

public class Test 
{ 
    public Test(string s1, string s2) 
    { 
    } 
} 

public class MyClass 
{ 
    public string MyFunction(Test t) 
    { 
     return ""; 
    } 
} 

Вам придется загрузить тест-объекта так же, как загрузить экземпляр MyClass, а также тест требует параметров в конструкторе, вам «придется использовать ConstructorInfo:

Assembly assembly = Assembly.Load(); //assembly of "Test" 
Type testType = assembly.GetType("Test"); 
ConstructorInfo ci = testType.GetConstructor(
           BindingFlags.Public | BindingFlags.Instance, 
           null, 
           new Type[]{typeof(string), typeof(string)}, 
           null); 
Test objTest = ci.Invoke(new object[] { "name", "type" }); 

теперь вы можете использовать objTest в коде.

+0

Моя проблема в том, что Class Test находится в другой сборке (классы находятся в двух разных сборках) – ash

+0

@ash Почему вы не можете просто загрузить эту сборку, как вы загружаете «MyProject.Components»? – Will

+0

Этот класс Test находится в Myproject.components.I попытался скопировать один и тот же класс в главную dll и передать этот объект. Но он выдает исключение – ash

1

У вас есть проблема с дизайном. У вас есть сборка (назовем ее сборкой А), содержащей код отражения образца, который вы опубликовали. У вас также есть вторая сборка (назовем ее сборкой B), которая содержит MyClass и Test.

Проблема в том, что в коде отражения вы пытаетесь создать экземпляр класса Test, чтобы передать его в качестве параметра MyClass.MyFunction.

Вы упомянули, что вы скопировали исходный код для класса Test в сборку A; это не будет работать. То, что вы там сделали, по существу, создает два разных класса с одинаковым именем и одинаковой структурой. Поскольку два класса не совпадают с CLR, вы получите недопустимое исключение литых, если попытаетесь отбросить один на другой.

Учитывая то, что вы опубликовали до сих пор, мне кажется, что наиболее простым решением для вашего подхода является наличие третьей сборки (назовем ее сборкой C), которая содержит компоненты, которые известны обеим сборкам A и B ,Создайте проект библиотеки классов в своем решении, переместите класс Test в этот проект, избавитесь от любых других событий класса Test в первых двух проектах и ​​добавьте ссылки в оба первых двух проекта, ссылающихся на новый проект. Как только вы это сделаете, как сборка A, так и сборка B будут ссылаться на одно и то же определение класса для класса Test, и пример кода, который вы опубликовали, будет работать.

Позвольте мне указать на что-то. Если код в сборке A не знает достаточно о коде в сборке B, чтобы создать экземпляр MyClass и напрямую вызвать MyFunction (а не через отражение), то как он знает об этом коде, чтобы узнать, какие параметры должны пройти? Есть ли в MyFunction общая сигнатура метода, которую понимает сборка A? Если это так, то MyClass, вероятно, следует реализовать интерфейс, что узел А знает, так что сборка А может вызвать MyFunction непосредственно, как показано ниже:

 Assembly assembly = Assembly.Load("MyProject.Components"); 
     Type dllType = assembly.GetType("MynameSpace.MyClass"); 
     if (dllType != null) 
     { 
      IMyInterface instance = Activator.CreateInstance(dllType) as IMyInterface; 
      if (instance != null) // check if this object actually implements the IMyInterface interface 
      { 
       instance.MyFunction(objTest); 
      } 
     } 

Если это не похоже, подход, который вы хотите, то есть другие варианты. Поскольку, похоже, вы не хотите, чтобы сборка A имела прямую ссылку на сборку B, если вы сохраняете класс Test внутри сборки B, то нет никакой возможности для сборки A иметь какие-либо знания класса Test для его построения. В этом случае вы можете использовать подход с заводскими шаблонами, в основном, чтобы сборка А знала какой-то заводский объект, способный создавать экземпляр объекта Test. Ниже приведен пример реализации:

Я уже упоминал о создании третьего проекта. Я бы по-прежнему рекомендовал это сделать. В моем примере я назвал свой «MyProject.Common». Он содержит следующий код:

// define a simple factory interface 
public interface IFactory 
{ 
    object CreateInstance(); 
} 

// and a generic one (hey, why not?) 
public interface IFactory<T> : IFactory 
{ 
    new T CreateInstance(); 
} 

// define a Factory attribute that will be used to identify the concrete implementation of a factory 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] 
public class FactoryAttribute : Attribute 
{ 
    public Type FactoryType { get; set; } 

    public FactoryAttribute(Type factoryType) 
    { 
     this.FactoryType = factoryType; 
    } 
} 

The IFactory интерфейсы и будет известно и понятно другими проектами в моем решении атрибута Factory, так как оба они ссылаются на проекте MyProject.Common.

Ниже приведен код, содержащийся в моем проекте «MyProject.Components»:

public class Test 
{ 
    public string Name { get; set; } 

    public string Type { get; set; } 

    public Test(string name, string type) 
    { 
     this.Name = name; 
     this.Type = type; 
    } 
} 

public class TestFactory : IFactory<Test> 
{ 
    #region IFactory<Test> Members 

    public Test CreateInstance() 
    { 
     return new Test("name", "type"); 
    } 

    #endregion 

    #region IFactory Members 

    object IFactory.CreateInstance() 
    { 
     return this.CreateInstance(); 
    } 

    #endregion 
} 


public class MyClass 
{ 
    // the Factory attribute on the first parameter indicates that the class TestFactory 
    // should be used as a factory object to construct the argument for this method 
    public string MyFunction([Factory(typeof(TestFactory))]Test obj) 
    { 
     if (obj == null) 
      return null; 
     else 
      return obj.ToString(); 
    } 
} 

Наконец, я заменил оригинальный код отражения, который Вы отправляли сообщение со следующим:

 Assembly assembly = Assembly.Load("MyProject.Components"); 
     Type dllType = assembly.GetType("MynameSpace.MyClass"); 
     if (dllType != null) 
     { 
      MethodInfo m = dllType.GetMethod("MyFunction"); 
      object objdll; 
      objdll = Activator.CreateInstance(dllType); 

      // use the parameter information to construct the arguments 
      ParameterInfo[] parameters = m.GetParameters(); 
      object[] args; 
      if (parameters != null && parameters.Length > 0) 
      { 
       args = new object[parameters.Length]; 
       for (int i = 0; i < parameters.Length; i++) 
       { 
        // check for factory attributes on the actual parameter 
        FactoryAttribute[] attributes = parameters[i].GetCustomAttributes(typeof(FactoryAttribute), true) as FactoryAttribute[]; 

        // if no attributes were found, check the parameter type for factory attributes 
        if (attributes == null || attributes.Length == 0) 
         attributes = parameters[i].ParameterType.GetCustomAttributes(typeof(FactoryAttribute), true) as FactoryAttribute[]; 

        // if no attributes were found still, then give up 
        if (attributes == null || attributes.Length == 0) 
        { 
         // this parameter has no factory specified, 
         // so how would this code know how to create the argument for that parameter ??? 
         args[i] = null; 
         continue; // move on to the next parameter 
        } 

        // there should only be one factory attribute, so use the first one 
        // assumption made here is that all factory classes will have a parameterless constructor 
        IFactory factory = Activator.CreateInstance(attributes[0].FactoryType) as IFactory; 

        args[i] = factory.CreateInstance(); 
       } 
      } 
      else 
       // there are no parameters 
       args = null; 


      if ((m != null)) 
      { 
       strReturnValue += (string)m.Invoke(objdll, args); 
      } 
     } 
Смежные вопросы