【问题标题】:Reflection. What can we achieve using it?反射。使用它我们可以实现什么?
【发布时间】:2010-12-26 05:52:19
【问题描述】:

我正在阅读和学习 C# 中的反射。知道它如何在我的日常工作中帮助我会很好,所以我希望比我更有经验的人告诉我使用它可以实现什么样的事情的示例或想法,或者我们如何减少代码量我们写的。

谢谢。

【问题讨论】:

  • 相关的是,但我个人认为这个问题是相当无益的。积分是逆推导是微积分,但我敢打赌,即使你知道推导是什么,它也几乎没有教给你任何东西。 :D

标签: c# reflection .net-3.5


【解决方案1】:

我最近使用它为我的枚举中的字段添加自定义属性:

public enum ShapeName
{
    // Lines
    [ShapeDescription(ShapeType.Line, "Horizontal Scroll Distance", "The horizontal distance to scroll the browser in order to center the game.")]
    HorizontalScrollBar,
    [ShapeDescription(ShapeType.Line, "Vertical Scroll Distance", "The vertical distance to scroll the browser in order to center the game.")]
    VerticalScrollBar,
}

使用反射获取字段:

    public static ShapeDescriptionAttribute GetShapeDescription(this ShapeName shapeName)
    {
        Type type = shapeName.GetType();
        FieldInfo fieldInfo = type.GetField(shapeName.ToString());
        ShapeDescriptionAttribute[] attribs = fieldInfo.GetCustomAttributes(typeof(ShapeDescriptionAttribute), false) as ShapeDescriptionAttribute[];

        return (attribs != null && attribs.Length > 0) ? attribs[0] : new ShapeDescriptionAttribute(ShapeType.NotSet, shapeName.ToString());
    }

属性类:

[AttributeUsage(AttributeTargets.Field)]
public class ShapeDescriptionAttribute: Attribute
{
    #region Constructor
    public ShapeDescriptionAttribute(ShapeType shapeType, string name) : this(shapeType, name, name) { }

    public ShapeDescriptionAttribute(ShapeType shapeType, string name, string description)
    {
        Description = description;
        Name = name;
        Type = shapeType;
    }
    #endregion

    #region Public Properties
    public string Description { get; protected set; }

    public string Name { get; protected set; }

    public ShapeType Type { get; protected set; }
    #endregion
}

【讨论】:

    【解决方案2】:

    一般来说,反射允许您访问有关对象的元数据。将反射与其他技术相结合可以使您的程序更具动态性。例如,您可以加载一个 DLL 并确定它是否包含接口的实现。您可以使用它来发现在运行时支持功能的 dll。用户可以使用它来扩展应用程序,而无需重新编译,也无需重新启动它。

    Visual Studio 中的 Intellisense 使用反射为您提供有关您正在使用的对象的信息。

    请注意,使用反射是有代价的。反射物体可能很慢。但是如果你需要它,反射是一个非常有用的工具。

    【讨论】:

    • 您可以通过多种方式降低这些成本;基于反射的代码可以和普通代码一样快。
    【解决方案3】:

    其中一种用途:您可以创建一个插件架构,在其中指定要在配置文件中使用的类的名称。使用反射,您可以获取此字符串并创建所请求对象的实例。如果该对象实现了一个已知接口,那么您可以通过普通(非反射)代码使用它。

    【讨论】:

      【解决方案4】:

      对于不需要了解调用者的库代码来说,它是无价的 - 就像泛型一样,但可以更丰富地访问数据。例子:

      • ORM(实现等)
      • 序列化/反序列化
      • 对象克隆/深拷贝
      • UI / 绑定代码(严格来说这是 ComponentModel,但您可以将两者混合使用 - 例如 HyperDescriptor)

      您当然应该尽量减少反射量,但您可以通过缓存来自 Delegate.CreateDelegate / Expression / DynamicMethod 的委托来降低成本

      【讨论】:

        【解决方案5】:

        我使用反射让我能够更灵活地满足不断变化的需求。也就是说,客户不断改变主意,将数据库表放在数据库中的什么位置。我所做的只是让对象自行检查其字段并在对象本身内调用这些字段的对象构造函数。那么,是否应该在其他地方找到一张桌子?点击,粘贴,完成。

        请注意,这并没有在最终生产中出现,但在迭代阶段删除了一些我需要更改的样板。

        【讨论】:

          【解决方案6】:

          我使用反射来帮助翻译表单上的标签和按钮等控件。使用反射,我将遍历表单上的所有控件,并将控件名称、文本和标题写入 XML 文件。在 XML 中翻译控件标题和文​​本后,将在将 XML 中找到的每个控件的标题和文本设置为其翻译后的值时回读该文件。
          我们的表单需要翻译成几种不同的语言,使用反射帮助我们节省了大量时间。

          【讨论】:

            【解决方案7】:

            VS 中的属性窗口是基于反射的 - 如果您创建一个用户控件,您可以立即从 PropertyGrid 修改其上的任何属性(如果您愿意,它也是一个您可以使用的控件)。当然,您可以添加属性来增强它的显示方式(通过反射访问)。

            我也用它来实现一个自定义的二进制序列化类。

            我有一个类,我使用反射对其进行序列化/反序列化 - 并为其他 UI 信息提供属性。

            [TypeConverter(typeof(IndexedExpandableObjectConverter))]
            [BinarySerializeable]
            public sealed class Socket
            {
                #region Fields (7) 
            
                [SerializedPosition(0)]
                Byte _mode = 1;
            
                ...
            
                [SerializedPositionAttribute(4)]
                UInt16 _localPort;
            
                ...
            

            #region 属性(5)

                [DisplayName("Listning Port")]
                [Description("The port which the socket will listen for connections on")]
                [DisplayIndex (0)]
                public UInt16 LocalPort
                {
                    get { return _localPort; }
                    set { _localPort = value; }
                }
            
                ...
            

            还有序列化函数——如你所见,它只需要一个对象和你想要的字节顺序(字节序)。其他一切都是由反思决定的。默认的 SerializationProvider 在对象内的字段上使用 SerializedPosition 属性(私有或非私有)。

            public static Byte[] Serialize(Object obj, ByteOrder streamOrder)
            {
            
                var provider = GetProvider(obj);
            
                if (provider.CanSerialize(obj.GetType()))
                    return provider.Serialize(obj, streamOrder);
            
                throw new ArgumentException(obj.GetType() + " is non-serialisable by the specified provider '" + provider.GetType().FullName + "'.");
            }
            
            
            private static IBinarySerializatoinProvider GetProvider(Object obj)
            {
            
                var providerAttrib = Reflector.GetAttribute<BinarySerializationProviderAttribute>(obj);
            
                if (providerAttrib != null)
                    return CreateProvider(providerAttrib.ProviderType);
            
                return CreateProvider(typeof(SerializationProvider));
            }
            

            【讨论】:

              【解决方案8】:

              这是基于枚举或魔术字符串执行方法的方式...

              
                  public enum ReflectionTestMethods
                  {
                      MethodA,
                      MethodB,
                      MethodC
                  }
                  public class ReflectionTest
                  {
              
                      public void Execute(ReflectionTestMethods method)
                      {
                          MethodInfo methodInfo = GetType().GetMethod(method.ToString()
                              , BindingFlags.Instance | BindingFlags.NonPublic);
                          if (methodInfo == null) throw new NotImplementedException(method.ToString());
                          methodInfo.Invoke(this, null);
                      }
              
                      private void MethodA()
                      {
                          Debug.Print("MethodA");
                      }
              
                      private void MethodB()
                      {
                          Debug.Print("MethodB");
                      }
              
                      private void MethodC()
                      {
                          Debug.Print("MethodC");
                      }
                  }
              

              但这也许是一个更好的解决方案...

              
                  public class ActionTest
                  {
                      private readonly Dictionary _actions = new Dictionary();
              
                      public ActionTest()
                      {
                          _actions.Add(ReflectionTestMethods.MethodA.ToString(), new Action(MethodA));
                          _actions.Add(ReflectionTestMethods.MethodB.ToString(), new Action(MethodB));
                          _actions.Add(ReflectionTestMethods.MethodC.ToString(), new Action(MethodC));
                      }
              
                      public void Execute(ReflectionTestMethods method)
                      {
                          if (!_actions.ContainsKey(method.ToString())) 
                              throw new NotImplementedException(method.ToString());
                          _actions[method.ToString()]();
                      }
              
                      private void MethodA()
                      {
                          Debug.Print("MethodA");
                      }
              
                      private void MethodB()
                      {
                          Debug.Print("MethodB");
                      }
                      private void MethodC()
                      {
                          Debug.Print("MethodC");
                      }
                  }
              

              【讨论】:

                【解决方案9】:

                我只在生产代码中使用过一次反射。在这种情况下,我不得不对启动例程中特定类方法的标准化使用进行调整(直到那个时间点)(对不起,没有更具体 - 这是不久前的事了,细节是朦胧)。解决这个问题的唯一方法是引入不同版本的方法并在运行时检查各种标准/代码条件等,以确定我应该调用哪个方法。这是一段非常紧凑的代码,它为原本难以解决的问题提供了简洁的解决方案。

                【讨论】:

                  【解决方案10】:

                  以下是我使用反射的一些事情,如果没有它,那将是非常困难或不可能的:

                  • StringTemplate 模板引擎的我的 C# 端口。反射用于检索动态对象的属性。
                  • 用托管代码编写的 CLI JIT 编译器。

                  Managed Extensibility Framework(一个新的 .NET 库)使用反射来:

                  • 定位和组合部件,包括应用导入。
                  • 它避免加载程序集以执行,直到需要它们的内容。

                  【讨论】:

                    【解决方案11】:

                    我猜想在一个“独立”应用程序中,您使用的每个对象在编译时都是已知的,因此您很少使用反射。

                    但是,当您编写的库或框架代码应该在编译时使用未知的对象(完全了解 - 您可能知道接口或基类)时,反射对于使用这些对象可能是无价的。

                    【讨论】:

                      【解决方案12】:

                      我将它用于:

                      • 依赖注入
                      • Microsoft 缺乏添加“协变/逆变泛型”和“带参数的 new() 约束”等内容的解决方法
                      • 面向方面的编程(在某种程度上,我主要使用 PostSharp)

                      【讨论】:

                        【解决方案13】:

                        反射非常适合“后期绑定”解决方案,您不希望将物理引用连接到您的项目,而是希望稍后“连接”它。这样,如果参考不存在且不重要,则不会出现缺少参考的未处理错误。您会得到一个可以处理的受控错误。

                        【讨论】:

                          【解决方案14】:

                          我在使用 Red Gate .Net Reflector 以更好地理解 .Net 框架本身的机制时的经典用途。有没有想过一个特定的框架对象是如何或为什么以它的方式工作的,或者你是否曾经有点难过,为什么某些东西不能以你认为的方式工作?

                          有时文档可能有点简陋,但通过使用反射和 Redgate 的工具,您可以在框架代码中嗅探,以更好地了解这一切的方式/原因/内容。

                          已经通过“MS局外人”使用反射在CLRs代码中发现了很多错误。

                          【讨论】:

                          • 我需要检查,但我不确定反射器是否使用反射;我的印象是它直接读取和解析 IL,而不是使用反射 API。
                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2015-11-28
                          • 2011-02-27
                          • 1970-01-01
                          • 1970-01-01
                          • 2013-09-08
                          • 1970-01-01
                          • 2014-08-28
                          相关资源
                          最近更新 更多