现有实例上的C#反射InvokeMember [英] C# Reflection InvokeMember on existing instance

查看:152
本文介绍了现有实例上的C#反射InvokeMember的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

大家好

我有点卡在这里,有一些要求.我们有一个适用于不同客户的应用程序的基本代码,其中一些具有仅适用于他们的特定代码,我们正在寻找一种将基本代码合并到一个应用程序中并在不同程序集中对特定操作进行编码的方法.

我的方法是将大多数方法虚拟化,当某人需要在方法中添加特定操作时,他应该继承基类,重写该方法并更改在那里所需的内容.之后,在代码库中,它将加载程序集并查找哪些方法被覆盖,以便当说Start()方法执行时,如果该方法被覆盖,则应从程序集中获取该方法,然后调用InvokeMember,但目标应该是该类的实际实例.
这么说吧,我有一个像这样的方法:

Hi everyone

I''m kinda stuck here with some requirement. We have a base code for an app that works for different clients, out of which some have specific code that applies just to them and we were looking for a way to merge the base code into one app and code the specific operations in different assemblies.

My approach is to make most of the methods virtual and when someone needs to add a specific operation in a method, he should inherit the base class, override the method and change whatever he has to there; after that, on the code base, it will load the assembly and find which methods were overridden so that when the method let''s say Start() executes, if it''s overridden, it should get the method from the assembly and call the InvokeMember but the target should be the actual instance of the class.
SO let´s say that I have a method like this:

//Class in basecode
public class ProcessManager
{
     public virtual void Start()
     {
      //Check to see if the method is overridden
      //If it is, load the assembly:
      Assembly myDll = Assembly.LoadFrom(@"C:\Users...\My.dll");
            Type myType = myDll.GetType("MyNameSpace.OverridenClass");
            MethodInfo mi = myType.GetMethod("Start");
            myType.InvokeMember(mi.Name, BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, this, null);
     }
}
//Class in a different Assembly
public class OverriddenClass : ProcessManager
{
     public override Start()
     {
        //My implementation
     }
}



这样,我得到了TargetException,对象与目标不匹配.如果重写的类继承自ProcessManager,则这就是为什么.
有没有办法纠正这个问题,或者这种方法不正确?我不想指定新实例,因为ProcessManager实例已经完成了一些初始化并具有一些值,所以我需要在该实例上执行该方法.

你们推荐什么???

非常感谢!



Like that, I''m getting a TargetException, the object doesn''t match target. Idk why, if the overriden class inherits from ProcessManager.
Is there a way to correct this or is this approach incorrect?? I don''t wanna specify a new Instance since the ProcessManager instance has already done some Initialization and has some values so I need to execute that method on that instance.

What do you guys recommend????

Thanks a lot!

推荐答案

您正在做的事情是使用反射将代码从程序集中移出程序集的一种非常糟糕的方法.它基于字符串,您可能会拼写错误;否则该方法的配置文件可能会有所不同.到目前为止,即使方法InvokeMembers之一也不是最佳的调用技术.在所有情况下,如果您做错了事,编译器将无法检测到问题.

如果存在完美的技术,为什么要做所有这些呢?如果操作正确,编译器将不允许您进行错误的调用或错误的调用.好的技术是维护的最佳方法.

您应该基于接口进行操作.首先,您应该创建一些插件接口或一组接口.您可以在主机应用程序和所有有效插件都引用的单独程序集中定义接口.您也可以简单地将其放入主机部件中,但是随后您将需要通过插件引用您的主机部件.不用:担心:这不是循环引用,因为主机程序集不引用插件,所以依赖项仅在运行时创建.因此,这是行之有效的合法技术.

现在,您希望该接口由已加载程序集的类之一实现.您可以找出某个类是否使用GetInterfaceGetInterfaces方法来实现您的接口,以获取已实现的接口,并将其与所需接口的类型进行比较.请注意,不必使用接口类型的名称,即使您使用它,也可以使用接口类型来计算它,而不用硬编码(就像您在代码示例中所做的那样),这是至关重要的. />
顺便说一句,该类型不必是公共的.而且,将其保留为internal.这样,反射可以访问它,但是不能直接引用.除非您也需要它,否则.

在下一步中,您将找到预期签名的构造函数,并使用System.Type.GetConstructors调用它,然后使用System.Reflection.ConstructorInfo.Invoke方法之一调用它.

最后,将获得的类实例转换为所需的接口.您已经可以检查出该接口是否由您的类型实现了,因此您可以放心地进行操作.

大功告成!您获得了引用的接口,该接口保证有效并且可以正常工作.仅在调用接口方法时,它们就不会出错或具有错误的签名.

请参阅:
http://msdn.microsoft.com/en-us/library/system.type.aspx [ ^ ],
http://msdn.microsoft.com/en-us/library/system. Reflection.constructorinfo.invoke.aspx [ ^ ].

我使用更高级的方法.在前面的步骤中,您必须扫描所有类型的问题.这不是很好.我添加了一个特殊的程序集级属性,该属性告诉我作为实现某些接口的那些类型,主机应检查哪些类型.主机代码从属性对象中获取此类型(或多个类型),并仅对其进行检查.

我在过去的回答中描述了此技术和其他技术:
创建使用可重载插件的WPF应用程序... [^ ],
AppDomain拒绝加载程序集 [使用CodeDom生成代码 [创建使用可重载插件的WPF应用程序... [ ^ ],
动态加载用户控件 [
What are you doing is a very bad way of getting the code out of an assembly using Reflection. It is based on the string, which you could misspell; or the profile of the method could be different, of something else. Even one of the methods InvokeMembers is not a best invocation technique, by far. In all cases, the compiler won''t be able to detect a problem if you do something wrong.

Why doing all that, if a perfect technique exists? If you do it right, a compiler won''t allow you to make a wrong invocation or a wrong call. The good technique is the best for maintenance.

You should do it based on interfaces. First, you should create some plug-in interface or a set of interfaces. You can define the interface in a separate assembly referenced by both host application and all valid plug-ins. You can also simply put it in the host assembly, but then you will need to reference you host assembly by the plug-in. Don''t: worry: this is not a circular reference, because host assembly does not reference plug-ins, the dependency is created only during run time. So, this is perfectly legitimate technique which works.

Now, you expect the interface to be implemented by one of the classes of the loaded assembly. You can find out if some class implements your interface using GetInterface or GetInterfaces method to get implemented interfaces and compare it with the type of the interface you expect. Note that the name of interface type does not have to be involved, and even if you use it, you calculate it out of interface type, never hard-code (as you did in your code sample), which is critically important.

By the way, the type does not have to be public. More over, keep it internal. In this way, Reflection can access it, but direct referencing — not. Unless you need it, too, of cause.

On next step, you find out a constructor of expected signature and invoke it using System.Type.GetConstructors and invoke it using one of System.Reflection.ConstructorInfo.Invoke methods.

Finally, you cast the obtained class instance to the required interface. You can safely do it, as you already checked up that this interface is implemented by your type.

You are done! You got the interface referenced which is guaranteed to be valid and work. As you are calling interface methods only, they cannot be wrong or having wrong signature.

Please see:
http://msdn.microsoft.com/en-us/library/system.type.aspx[^],
http://msdn.microsoft.com/en-us/library/system.reflection.constructorinfo.invoke.aspx[^].

I use a bit more advanced method. The problem that in the previous steps, you have to scan all type. This is not so good. I add a special assembly-level attribute which tells me what types should be examined by the host as those implementing some interface. The host code gets this type (or types) from the attribute object and examine it (them) only.

I described this and other technique in my past answers:
Create WPF Application that uses Reloadable Plugins...[^],
AppDomain refuses to load an assembly[^],
code generating using CodeDom[^].

See also my other solutions on related questions:
Create WPF Application that uses Reloadable Plugins...[^],
Dynamically Load User Controls[^].

—SA


这篇关于现有实例上的C#反射InvokeMember的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆