代码之家  ›  专栏  ›  技术社区  ›  Tom Corelis

有人能解释一下反思的实际性吗?[副本]

  •  18
  • Tom Corelis  · 技术社区  · 17 年前

    我问了我的另一个朋友一个不同的问题(我现在已经忘记了),他的回答只是在他签字之前的“反思”。

    有很多哲学上的答案——“应用程序审视自身”——但它们并没有提供任何实际的提示,说明实际发生了什么,或者在这种情况下如何使用它。

    那么,什么是反射,为什么它很重要,我为什么/如何使用它?

    18 回复  |  直到 17 年前
        1
  •  14
  •   Hejazzman    17 年前

    反射提供了在运行时确定事物和执行代码的能力。

    你不知道 有

    例如:

    a) 您可以使用反射来配置应用程序,方法是加载外部配置文件并基于该文件启动服务。您的应用程序不必事先知道实现这些服务的类,只要它们符合特定的接口或API。

    c) 对于通过检查代码工作的程序来说,反射也是非常宝贵的。例如IDE或UI设计器。

    d) 反射可以帮助您减少样板代码。

    e) 反射对于在代码中定义小型领域特定语言(DSL)非常方便。

        2
  •  9
  •   Arun Bertil    11 年前

    反射是编写在运行时执行代码的静态代码的能力,这通常是在编译时确定的。

    例如,我可以通过在该命令中编译来调用类方法进行绘制,例如:

    pen.DrawLine()
    

     if(pen.Methods.Contains("DrawLine"))
     {
        pen.InvokeMethod("DrawLine"))
     }
    

    我不是反射大师,但我将反射用于插件体系结构。

    我知道用例有点技术性,但本质上反射允许我动态加载插件(即在运行时),并允许我对其进行类型安全调用。

        3
  •  7
  •   Yes - that Jake.    17 年前

    最常见的反射使用是被称为RTTI(运行时类型信息)的扩展,并且主要是C++程序员的领域。

    反射是.net构建方式的副作用,Microsoft选择将用于创建Visual Studio和.net运行时的库公开给Microsoft以外的开发人员。

        public static IMyCompanySetting UnwrapSetting(XmlNode settingNode)
        {
    
            string typeName = settingNode.Attributes["type"].Value;
            string typeAssembly;
            if(settingNode.Attributes["assembly"] != null)
            {
                typeAssembly = settingNode.Attributes["assembly"].Value;
            }
    
            Type settingType = null;
            Assembly settingAssembly = null;
            try
            {
                // Create an object based on the type and assembly properties stored in the XML
                try
                {
                    settingAssembly = Assembly.Load(typeAssembly);
                    if (settingAssembly == null)
                    {
                        return null;
                    }
                }
                catch (Exception outerEx)
                {
                    try
                    {
                        settingType = GetOrphanType(typeName);
                    }
                    catch (Exception innerEx)
                    {
                        throw new Exception("Failed to create object " + typeName + " :: " + innerEx.ToString(), outerEx);
                    }
                }
    
                // We will try in order:
                // 1. Get the type from the named assembly.
                // 2. Get the type using its fully-qualified name.
                // 3. Do a deep search for the most basic name of the class.
                if (settingType == null && settingAssembly != null) settingType = settingAssembly.GetType(typeName);
                if (settingType == null) settingType = Type.GetType(typeName);
                if (settingType == null) settingType = GetOrphanType(typeName);
                if (settingType == null) throw new System.Exception(
                    String.Format("Unable to load definition for type {0} using loosest possible binding.", typeName));
            }
            catch (Exception ex)
            {
                throw new CometConfigurationException(
                    String.Format("Could not create object of type {0} from assembly {1}", typeName, typeAssembly), ex);
            }
    
            bool settingIsCreated = false;
            IMyCompanySetting theSetting = null;
    
            // If the class has a constructor that accepts a single parameter that is an XML node,
            // call that constructor.
            foreach (ConstructorInfo ctor in settingType.GetConstructors())
            {
                ParameterInfo[] parameters = ctor.GetParameters();
                if (parameters.Length == 1)
                {
                    if (parameters[0].ParameterType == typeof(XmlNode))
                    {
                        object[] theParams = { settingNode };
                        try
                        {
                            theSetting = (IMyCompanySetting)ctor.Invoke(theParams);
                            settingIsCreated = true;
                        }
                        catch (System.Exception ex)
                        {
                            // If there is a pre-existing constructor that accepts an XML node
                            // with a different schema from the one provided here, it will fail
                            // and we'll go to the default constructor.
                            UtilitiesAndConstants.ReportExceptionToCommonLog(ex);
                            settingIsCreated = false;
                        }
                    }
                }
            }
    

    这段代码允许我们创建无限数量的类,这些类实现IMyCompanySetting,并使用XML进行序列化和反序列化。然后,给定作为对象序列化输出的XML块,系统可以将其转换回对象,即使对象本身来自序列化库没有静态链接的库。

    这里有三件事反映了没有它是不可能做到的:

    基于编译时未知的类的对象的签名调用对象构造函数。

        4
  •  4
  •   Chase Seibert    17 年前

    假设您有两个接口的替代实现。您希望允许用户通过简单的文本配置文件选择其中一个。

    通过反射,您可以简单地从配置文件中读取要使用其实现的类的名称(作为字符串),并实例化该类的实例。

        5
  •  4
  •   Jhonny D. Cano -Leftware-    17 年前

    考虑一个插件系统,在该系统中,主机不知道它将持有的插件;通过反射(以及正确的体系结构),您可以构建一个简单的解决方案来实现这一点。

    考虑另一种场景,在这种情况下,您必须调用给定字符串的对象上的方法,反射也给出了实现此方法的方法。

        6
  •  4
  •   BlueMonkMN    17 年前

    有时,在不知道类在设计时具有哪些属性或方法的情况下读取类的属性或调用类的方法是很有用的。实现这一点的方法是通过反思。正如下面的代码所演示的,您可以获得一个类上所有属性的列表,并在编译时检索它们的值,而不需要知道任何关于它的信息。或者,您可以按名称获取方法,即使您在编译时不知道该方法的名称,也可以通过反射调用它。例如,这将允许您创建一种脚本语言,该语言在另一个用户提供的DLL中定义的对象上运行。(当然,您也可以枚举类中的所有方法或按名称检索特定属性,但下面的代码中没有演示这些情况。)

    class Program
    {
      static void Main(string[] args)
      {
         UserDefinedClass udc = new UserDefinedClass();
         udc.UserProperty = "This is the property value";
    
         ClassTracer ct = new ClassTracer(udc);
         ct.TraceProperties();
         ct.CallMethod("UserFunction", "parameter 1 value");
      }
    }
    
    class ClassTracer
    {
      object target;
    
      public ClassTracer(object target)
      {
         this.target = target;
      }
    
      public void TraceProperties()
      {
         // Get a list of all properties implemented by the class
         System.Reflection.PropertyInfo[] pis = target.GetType().GetProperties();
         foreach (System.Reflection.PropertyInfo pi in pis)
         {
            Console.WriteLine("{0}: {1}", pi.Name, pi.GetValue(target, null));
         }
      }
    
      public void CallMethod(string MethodName, string arg1)
      {
         System.Reflection.MethodInfo mi = target.GetType().GetMethod(MethodName);
         if (mi != null)
         {
            mi.Invoke(target, new object[] { arg1 });
         }
      }
    }
    
    class UserDefinedClass
    {
      private string userPropertyValue;
    
      public string UserProperty
      {
         get
         {
            return userPropertyValue;
         }
         set
         {
            userPropertyValue = value;
         }
      }
    
      public void UserFunction(string parameter)
      {
         Console.WriteLine("{0} - {1}", userPropertyValue, parameter);
      }
    }
    
        7
  •  3
  •   tylerl    17 年前

    foo.DoBar() 你可以打电话:

    foo.GetType().GetMethod("DoBar").Invoke(foo,null);
    

    这似乎是一种到达同一目的地的迂回方法,但它也可以用来执行一些可能违反规则的黑魔法,例如在框架类上调用私有方法。它还可以用于实际生成代码和输出新的二进制文件,以及一系列对一小群人非常有用的东西。

    MSDN section on Reflection

        8
  •  2
  •   Mehrdad Afshari    17 年前

    在许多情况下,应用程序不应该对自身进行太多假设,而应该在运行时查看它的实际情况。这就是反思进入表演的地方。例如,ASP.NET对您使用的成员资格提供程序不作任何假设,只是它继承了相应的类。它使用反射在运行时查找类并实例化它。由于类之间的解耦和依赖关系的减少,这提供了大量的可扩展性。

    当然,这只是反射的一个用例。在许多其他情况下,它可能非常有用。

        9
  •  2
  •   Matt Davis    17 年前

    安德鲁·特罗尔森,这本书的作者 Pro C# 2008 and the .NET 3.5 Platform

    在.NET世界中, 反射

    我还使用反射来加载和保存应用程序设置。


    关于C,我只知道一件事# 该死,因为我所有的GUI应用都喜欢 抛出令人难以置信的恼人、毫无意义的东西 “异常已由 调用“异常”的目标在 Application.Run(new frmMain());, 而不是停在真正的 出现问题(如图所示)

        10
  •  2
  •   Andrei Rînea    17 年前

    假设您有一些业务实体,它们都来自一个名为Entity的基类。也就是说,您需要/希望所有派生类都是可克隆的。您可以在基类上实现方法“Clone”(接口ICloneable),该方法将循环遍历当前类的所有属性(尽管它是在实体类上实现的),并将它们复制到克隆对象。在这种情况下,反思真的很有帮助。因为您无法知道基类中属性的名称和计数。并且您不希望在所有派生类中实现该方法。但是,您可能希望使该方法成为虚拟的。

        11
  •  2
  •   dance2die    17 年前

    其他人对此负责

    什么是反思,为什么它很重要

    我如何使用它?

    通过以下名称空间

    我为什么要用它?

    当你看到这样的产品时 Reflector 实际的 足够地

        12
  •  2
  •   Frank Schwieterman    17 年前

    如果你跑得又快又猛,也许你是从一个对象继承的,需要访问一个私有字段。你没有来源,但你有反射没关系。(哈奇,我知道)

    我很少使用反射,但它偶尔会派上用场。

        13
  •  1
  •   Rob    17 年前

    我使用反射实现了一个自定义数据绑定系统。

    例如,使用绑定API,我可以编写以下内容:

    绑定b=新绑定(文本框,“文本”,myObject,“文本”,BindingMode.Bidirectional);

    绑定(对象、字符串、对象、字符串、绑定模式)

    因此,系统可以处理任何对象(有一个重要的限制条件)。

    我检查第一个对象,并在第一个字符串中找到与命名成员对应的成员(textBox和“Text”,即textBox是否有名为“Text”的成员)。重复第二个对象和字符串。

    注意。我在.NET1.0中实现了这一点,因此它比微软的绑定实现要早——我还没有时间去研究它。

        14
  •  1
  •   oglester    17 年前

    实际应用

        15
  •  0
  •   mP.    17 年前

    如前所述,反射可用于使用类名创建新实例。序列化也由反射提供动力。在本例中,代码检查所有可序列化字段,并递归序列化这些字段,直到整个图形被使用并写入其字节形式。

        16
  •  0
  •   Lou    17 年前

    就反射的实际用途而言,我们使用它来允许我们的客户提供他们自己的本地化。

        17
  •  0
  •   heartlandcoder    17 年前

    这里已经详细介绍了反射的许多用途,但另一个用途可能是记录对象(包括其私有成员)的状态,以便在调试中使用。

        18
  •  0
  •   Abhijeet Patel    17 年前

    你能发布一些示例代码来说明你是如何执行反射的吗?PDB文件是否与使用反射API的程序集位于同一位置?