![](/img/trans.png)
[英]How to invoke a method which takes input parameter as another class object using Reflection in C#?(Method gives argument exception)
[英]Reflection : How to invoke methods which takes a class object as input
我正在使用反射從另一個dll調用一個方法。該方法將一個類對象作為輸入。 我怎么稱呼這種方法。 我嘗試將同一個類復制到兩個dll,然后創建了該類的對象並傳遞它。 它會在complie time本身中引發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)
我的問題是Class Test在另一個程序集中(這些類在兩個不同的程序集中)
你有一點設計問題。 您有一個程序集(讓我們稱之為程序集A),其中包含您發布的示例反射代碼。 你還有一個包含MyClass和Test的第二個程序集(我們稱之為程序集B)。
問題是您在反射代碼中嘗試創建Test類的實例,以便將其作為參數傳遞給MyClass.MyFunction。
您提到您將Test類的源代碼復制到程序集A中; 那不管用。 你在那里做的基本上是創建兩個具有相同名稱和相同結構的不同類。 由於這兩個類就CLR而言並不相同,如果你試圖將一個類轉換為另一個,你將得到一個無效的強制轉換異常。
鑒於您到目前為止發布的內容,在我看來,對您的方法最直接的解決方案是使用第三個程序集(讓我們稱之為程序集C),其中包含程序集A和B都知道的組件。創建一個類在您的解決方案中的庫項目,將Test類移動到該項目中,在前兩個項目中刪除Test類的任何其他事件,並在引用新項目的前兩個項目中添加引用。 完成后,程序集A和程序集B將引用Test類的相同類定義,並且您發布的示例代碼將起作用。
但是,讓我指出一些事情。 如果程序集A中的代碼對程序集B中的代碼知之甚少,以便實例化MyClass並直接調用MyFunction(而不是通過反射),那么它如何充分了解該代碼以了解要傳遞的參數? MyFunction是否具有程序集A理解的通用方法簽名? 如果是這種情況,那么MyClass應該實現程序集A知道的接口,以便程序集A可以直接調用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類的任何知識為了構建一個。 在這種情況下,您可以使用工廠模式方法,基本上使組件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接口和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);
}
}
如果你的對象是一個Type
對象的對象,那么你可以只傳遞對象的類型作為參數...例如
object[] args = {typeof(typeneeded)};
要么
object[] args = { assembly.GetType(typeneeded) };
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實例的方式加載Test對象,並且由於Test需要構造函數中的參數,您必須使用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
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.