当前位置: 首页 > 编程语言 > C#

谁能解释一下反射的用处?分享

时间:2023-04-10 17:28:46 C#

C#:谁能解释一下反射的用处?所以我试着搜索,希望有人对此有一个很好的解释,但运气不好。我问了我的另一个朋友一个不同的问题(我现在已经忘记了),他的回答只是他签字前的“反思”。我是C#世界的新手,曾经是一名业余VB.net程序员(也是JavaScript、ActionScript和C),我正在尽我最大努力掌握这些高级概念。有许多哲学答案——“应用程序看自己”——但它们没有提供任何关于实际发生了什么或如何在该上下文中使用它的实用提示。那么,什么是反射,为什么它很重要,为什么/如何使用它?反射提供了在运行时确定事物和执行代码的能力。如果你不想,你不必使用它,但它对动态行为很方便。例如:a)您可以使用反射来配置您的应用程序,方法是加载外部配置文件并启动基于它的服务。您的应用程序不必事先知道实现这些服务的类,只要它们符合特定的接口或API。b)使用反射,可以动态生成类和代码,这简化了一些编程任务,因为程序员不必显式地创建所有需要的代码。c)反射对于通过检查代码工作的程序来说也是无价的。一个例子是IDE或UI设计器。d)反射可以帮助您减少样板代码。e)反射可以方便地在您的代码中定义一个微型领域特定语言(DSL)。(我的定义)反射是编写在运行时执行代码的静态代码的能力,通常在编译时确定。比如我可以通过编译这个命令调用一个类的方法来画图,比如:pen.DrawLine()或者通过反射,我可以先看看我的对象有没有一个叫“drawline”的方法,如果有,就调用它。(注意这不是实际的C#反射语法)if(pen.Methods.Contains("DrawLine")){pen.InvokeMethod("DrawLine"))}我不是反射大师,但我使用反射作为插件架构。通过反射,我可以在运行时加载一个.NET程序集(在本例中为dll),找出.NET程序集中的所有类型,查看是否有任何实现特定接口,如果有,则实例化我调用的类接口方法。我知道用例有点技术性,但本质上反射允许我动态加载插件(即在运行时)并让我对其进行类型安全调用。反射最常见的用途是以前称为RTTI(运行时类型信息)的扩展,主要是C++程序员的领域。反射是.net构建方式的副作用,Microsoft已选择向Microsoft外部的开发人员公开其用于创建VisualStudio和.net运行时的库。大多数反射库专注于可以在运行时调用的类型发现和创建。这允许一些非常强大的自引用代码。下面举例说明我们的配置管理系统的核心(为清晨起见,删掉了一些位置):字符串类型程序集;if(settingNode.Attributes["assembly"]!=null){typeAssembly=settingNode.Attributes["assembly"].Value;}输入settingType=null;程序集设置Assembly=null;try{//根据存储在XML中的类型和程序集属性创建一个对象try{settingAssembly=Assembly.Load(typeAssembly);如果(settingAssembly==null){返回null;}}catch(ExceptionouterEx){try{settingType=GetOrphanType(typeName);}catch(ExceptioninnerEx){thrownewException("创建对象失败"+typeName+"::"+innerEx.ToString(),outerEx);}}//我们将按顺序尝试://1.从指定程序集中获取类型。//2.使用其完全限定名称获取类型。//3.深入搜索类的最基本名称。if(settingType==null&&settingAssembly!=null)settingType=settingAssembly.GetType(typeName);如果(settingType==null)settingType=Type.GetType(typeName);如果(settingType==null)settingType=GetOrphanType(typeName);if(settingType==null)thrownewSystem.Exception(String.Format("无法使用最松散的绑定加载类型{0}的定义。",typeName));}catch(Exceptionex){thrownewCometConfigurationException(String.Format("无法从程序集{1}创建类型为{0}的对象",typeName,typeAssembly),ex);}boolsettingIsCreated=false;IMyCompanySettingtheSetting=null;//如果该类有一个接受单个参数(即XML节点)的构造函数,//调用该构造函数。foreach(ConstructorInfoctorinsettingType.GetConstructors()){ParameterInfo[]parameters=ctor.GetParameters();if(parameters.Length==1){if(parameters[0].ParameterType==typeof(XmlNode)){object[]theParams={settingNode};尝试{theSetting=(IMyCcompanySetting)ctor.Invoke(theParams);settingIsCreated=true;}catch(System.Exceptionex){//如果有一个预先存在的构造函数接受一个XML节点//其架构与此处提供的不同,它将失败//我们将转到默认构造函数.UtilitiesAndConstants.ReportExceptionToCommonLog(ex);settingIsCreated=false;}}}}这段代码允许我们创建无限数量的实现IMyCompanySetting的类,并使用XML将它们序列化本身来自序列化库未静态链接的库。没有它,这里反映的三件事是不可能的:在运行时按名称加载程序集。在运行时根据程序名称从程序集中加载对象。根据编译时未知的类对象的签名调用对象构造函数。假设您有两个接口的替代实现。您希望允许用户通过一个简单的文本配置文件来选择一个或另一个。使用反射,您只需从配置文件(作为字符串)中读取您想要实现的类的名称,然后实例化该类的实例。反射使您可以深入到一个组件,并在以后不引用它的任何地方使用它。考虑一个插件系统,主机不知道它将容纳的插件;使用反射(和正确的架构),您可以为此目的构建一个简单的解决方案。考虑另一种情况,您必须在给定字符串的情况下调用对象上的方法,反射也为您提供了执行此操作的方法。还有很多其他用途,但我希望这两个,打开你对CLR的这个优秀特性的兴趣有时能够读取属性或调用类的方法而不知道类在设计时具有哪些属性或方法是有用的.实现这一目标的方法是反思。正如下面的代码所示,您可以获得一个类的所有属性的列表并检索它们的值,而无需在编译时了解其值。或者你可以通过名称获取方法,即使你在编译时不知道方法的名称,你也可以通过反射来调用它。例如,这将允许您创建一种脚本语言,该语言对定义在另一个用户提供的DLL中的对象进行操作。(当然,您也可以枚举类中的所有方法或通过名称检索特定属性,但这些情况不会在下面的代码中演示。)classProgram{staticvoidMain(string[]args){UserDefinedClassudc=newUserDefinedClass();udc.UserProperty="这是属性值";ClassTracerct=newClassTracer(udc);ct.TraceProperties();ct.CallMethod("UserFunction","参数1值");}}classClassTracer{对象目标;publicClassTracer(objecttarget){this.target=target;}publicvoidTraceProperties(){//获取由类System.Reflection.PropertyInfo[]实现的所有属性的列表pis=target.GetType().GetProperties();foreach(System.Reflection.PropertyInfopiinpis){Console.WriteLine("{0}:{1}",pi.Name,pi.GetValue(target,null));}}publicvoidCallMethod(stringMethodName,stringarg1){System.Reflection.MethodInfomi=target.GetType().GetMethod(MethodName);if(mi!=null){mi.Invoke(target,newobject[]{arg1});}}}classUserDefinedClass{privatestringuserPropertyValue;publicstringUserProperty{get{返回userPropertyVal等;}设置{userPropertyValue=值;}}publicvoidUserFunction(stringparameter){Console.WriteLine("{0}-{1}",userPropertyValue,parameter);}}反射是用代码来检查代码本身例如,你可以调用下面的foo.DoBar()而不是调用foo.DoBar():foo.GetType().GetMethod("DoBar").Invoke(富,空);这看起来像是一个到达相同目的地的循环方法,但它也可以用来做一些可能违反规则的黑魔法,比如在框架类上调用私有方法。它还可以用于实际生成代码和输出新的二进制文件,以及一些对一小部分人非常有用的东西。有关详细信息,请查看有关反射的MSDN部分。在许多情况下,应用程序不应该对自己做出很多假设,而应该查看运行时以了解它实际上是什么。这就是反思进入节目的地方。例如,ASP.NET不假定您使用哪个成员资格提供程序,它只是继承相应的类。它使用反射在运行时查找类并实例化它。由于类之间的解耦和减少依赖性,这提供了很多可扩展性。当然,这只是反射的一个用例。可能还有许多其他情况可能非常有用。ProC#2008和.NET3.5平台的作者AndrewTroelsen是这样定义反射的:在.NETUniverse中,反射是运行时类型发现的过程。我不确定我能否准确解释这意味着什么,但我可以告诉你我是如何使用它的。我可以将用户定义的属性放在程序集(dll)上。在运行时使用反射,我可以检查某个目录位置中的所有程序集,看看它们是否定义了这个属性。这让我的应用程序知道如何使用程序集,例如插件。我还使用反射来加载和保存应用程序设置。对于它的价值,除非您使用反射,否则我不确定它与您的GUI抛出异常有什么关系。我对C#反射的唯一了解是它很烦人,因为我所有的GUI应用程序都喜欢抛出令人难以置信的烦人、毫无意义的“异常已经被调用的目标抛出”Application.Runexception(newfrmMain());而不是在真正出现问题的地方停下来(如innerException所示)。你的陈述让我相信你的应用程序中的任何地方都只有很少的try/catch块,因此每个异常都会渗透回调用堆栈的顶部。如果在调试模式下使用VisualStudio2005/2008,请转到“调试”菜单并选择“异常...”菜单选项。在异常对话框中,选中抛出列下的复选框。现在,当您运行应用程序并抛出异常时,调试器将在抛出异常的地方中断。假设您有一些业务实体,它们都派生自名为Entity的基类。假设您需要/希望所有派生类都是可克隆的。您可以在基类上实现一个方法“Clone”(接口ICloneable),它将循环遍历当前类的所有属性(尽管它是在Entity类中实现的)并将它们复制到克隆的对象。在这种情况下,反思真的很有帮助。因为你无从得知基类的属性名称和个数。而且您不想在所有派生类中实现该方法。但是,您可能希望使该方法成为虚拟的。其他人已经回答了什么是反射,为什么它很重要,所以我只回答以下内容。我该如何使用它?通过以下命名空间为什么要使用它?调用/检查程序集类型、成员、属性当您查看像Reflector这样的产品时,能够检查代码结构非常有用。如果你跑得又快又猛,也许你正在继承一个对象并需要访问一个私有字段。你没有消息来源,但没关系,你有反思。(Harkey,我知道)我见过的一个更合理的用法是在实现C#泛型时使用反射,我想在泛型类型上执行否则不可用的操作。泛型类型上唯一可用的操作是由泛型约束提供的操作(即,如果您约束泛型类型来实现IFoo,则可以对该类的实例使用IFoo方法)。虽然有些操作不可用-例如我正在使用应该具有特定非默认构造函数的泛型类。我不能限制泛型方法只接受带有该构造函数的泛型类型参数,但至少我可以在通过反射实现泛型时尝试使用构造函数。我很少使用反射,但它偶尔会派上用场。我使用反射来实现自定义数据绑定系统。例如,使用我的绑定API,我可以编写以下内容:bindingb=newBinding(textBox,"Text",myObject,"Text",BindingMode.Bidirectional);对textBox对象中文本的任何更改都由Binding对象(附加到TextChanged事件)检测并传递给myObject.Text属性。检测到对myObject.Text的更改(通过其TextChanged事件)并将其传递给textBox.Text成员。我使用反射实现了这个系统。Binding构造函数声明为:Binding(Object,String,Object,String,BindingMode)因此,该系统适用于任何对象(有一个重要的条件)。我检查第一个对象并在第一个字符串中找到与指定成员对应的成员(文本框和“文本”,即文本框有一个名为“文本”的成员)。重复第二个对象和字符串。附带条件:对于要在这种情况下使用的对象,它们必须实现非正式要求,即任何可绑定属性都必须具有相应的PropertyNameChanged事件。令人高兴的是,几乎所有.NetUI组件都遵循此约定。然后我检查对象是否有必要的PropertyNameChanged事件,向它们添加事件处理程序,并设置所有内容。注意。我在.Net1.0中实现了它,所以它早于Microsoft的绑定实现-我还没有调查过。实际应用使用反射将数据与对象对齐,就像在数据映射的情况下一样。例如,您可以将数据库列映射到对象的属性。如果您正在编写一个对象关系映射器,您将需要一些方法从配置设置(即DatabaseColumnName映射到Class.MemberName)到它引用的对象。反射之所以如此命名,是因为它允许一些代码看起来就像在照镜子一样。如前所述,反射可用于使用类名创建新实例。序列化也是由反射驱动的。在这种情况下,代码会检查所有可序列化的字段并递归地序列化这些字段,直到整个图都被使用并写入其字节形式。反射允许通过在运行时发现类型、方法、字段等在编译时转义静态类型。JVM上的很多动态语言(不清楚CLR但id想象的一样)都是使用反射来派发方法。就反射的实际使用而言,我们使用它来让我们的客户提供他们自己的本地化。通过我们的应用程序,我们提供了一个本地资源数据库,客户可以通过为所有字符串、菜单项、对话框控件等提供语言翻译来工作。使用这个客户端翻译数据库,我们然后在应用程序启动时使用反射并遍历所有控件并将默认语言字符串替换为客户端提供的翻译字符串。此处详细介绍了反射的许多用途,但另一种用途可能是记录对象的状态,包括其私有成员,以用于调试目的。你能发布一些示例代码来展示你是如何进行反射的吗?您是否将PDB文件放在与使用反射API的程序集相同的位置?如果是这样,那么在VisualStudio中转到“调试”菜单->“异常”并选中“抛出公共语言运行时”复选框。在调试模式下运行应用程序。希望当您的反射程序集中存在真正的异常时,调试器应该中断。以上是C#学习教程:C#:谁能解释一下反射的用处?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处: