2010-09-07 48 views
3

我正在使用反射來調用另一個dll.That方法中的方法,該方法將類對象作爲輸入。我怎麼稱呼那個方法。 我試圖將同一個類複製到這兩個DLL,我創建了該類的一個對象,並通過它。它在編譯時間本身中引發invliad轉換錯誤。 然後我試着讓函數把一個對象作爲參數,然後試圖將它轉換爲所需的類。它在運行時拋出無效的轉換異常。 這就是我試圖反射:如何調用將類對象作爲輸入的方法

  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 2010-09-07 11:38:05

回答

0

如果您的意思是按類對象的對象是Type對象,那麼你可以傳遞對象的類型作爲參數...例如,

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

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

我的問題是,類測試是在另一個程序集(類是在兩個不同的程序集) – ash 2010-09-07 12:02:06

+0

然後使用第二種方法,你應該沒問題! – 2010-09-07 12:07:31

+0

那麼如何給我創建的參數[0]賦予一些值呢? – ash 2010-09-07 12:11:55

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的情況下以同樣的方式,並作爲測試需要在構造函數的參數,你必須使用構造函數:

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

我的問題是,類測試是在另一個程序集(類是在兩個不同的程序集) – ash 2010-09-07 12:01:38

+0

@ash爲什麼你不能像加載「MyProject.Components」加載該程序集? – Will 2010-09-07 12:03:44

+0

該類Test在Myproject.components.I嘗試將同一個類複製到主dll並傳遞該對象。但它會引發異常 – ash 2010-09-07 12:08:57

1

你有一點設計問題。您有一個包含您發佈的示例反射代碼的程序集(我們稱之爲程序集A)。您還有第二個程序集(我們稱之爲程序集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的直接引用,因此如果您將程序集的類保留在程序集B的內部,那麼程序集A沒有任何方法可以使用任何有關Test類的知識以構建一個。在這種情況下,您可以使用工廠模式方法,基本上這樣程序集A就知道某種能夠實例化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; 
    } 
} 

的IFactory接口和工廠屬性將被已知的,在我的解決方案由其他項目的理解,因爲它們均引用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); 
      } 
     } 
相關問題