我正在阅读和学习有关C#反射的内容。很想知道它如何帮助我在日常工作中使用,因此我想听听比我更有经验的人分享使用反射可以实现哪些样例或者想法,以及如何减少我们编写的代码量。
谢谢。
我正在阅读和学习有关C#反射的内容。很想知道它如何帮助我在日常工作中使用,因此我想听听比我更有经验的人分享使用反射可以实现哪些样例或者想法,以及如何减少我们编写的代码量。
谢谢。
我最近使用它向枚举字段添加自定义属性:
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());
}
class属性:
[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
}
一般来说,反射允许您访问关于对象的元数据。结合其他技术使用反射可以使您的程序更加动态化。例如,您可以加载DLL并确定它是否包含接口的实现。您可以使用此功能在运行时发现支持功能的dll。您可以使用此功能扩展应用程序而无需重新编译和重启。
Visual Studio中的Intellisense使用反射为您提供有关正在使用的对象的信息。
请注意,使用反射会带来成本。反射对象可能会很慢。但是如果您需要它,反射是一个非常有用的工具。
其中一个用途是:您可以创建一个插件架构,在配置文件中指定要使用的类的名称。使用反射,您可以将此字符串获取并创建所请求对象的实例。如果该对象实现了已知接口,则可以通过普通(非反射)代码使用它。
对于不需要了解调用者的库代码(如泛型),但需要更丰富地访问数据的情况下,这是非常宝贵的。例如:
当然,您应该尽量减少反射操作的次数,但您可以通过缓存从Delegate.CreateDelegate / Expression / DynamicMethod
创建的委托来减轻成本。
VS中的属性窗口是基于反射的 - 如果您创建了一个用户控件,可以立即从PropertyGrid
(如果需要,也可以使用它)修改其上的任何属性。当然,您可以添加属性以增强其显示方式(这些属性通过反射访问)。
我还使用它来实现自定义二进制序列化类。
在这里,我有一个类,在其中使用反射对其进行序列化/反序列化 - 并提供用于附加UI信息的属性。
[TypeConverter(typeof(IndexedExpandableObjectConverter))]
[BinarySerializeable]
public sealed class Socket
{
#region Fields (7)
[SerializedPosition(0)]
Byte _mode = 1;
...
[SerializedPositionAttribute(4)]
UInt16 _localPort;
...
#区域 属性 (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));
}
这是一种基于枚举或魔术字符串执行方法的方式...
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");
}
}
我使用了反射来让自己更灵活地满足不断变化的需求。也就是说,客户一直在改变他们想要在数据库中放置数据库表的位置。我所做的就是让对象自我检查其字段,并在对象本身内调用这些字段的对象构造函数。然后,如果应该在其他地方找到一个表?点击、粘贴、完成。
当然,这并没有在最终生产中使用,但在迭代阶段中去除了一些我需要更改的样板文件。
我使用反射来方便翻译我们表单上的标签和按钮等控件。使用反射,我会遍历表单上所有的控件,并将控件的名称、文本和标题写入 XML 文件。在 XML 中翻译完控件标题和文本后,读取文件并将每个控件的标题和文本设置为其翻译后的值。
我们的表单需要翻译成多种语言,使用反射帮助我们节省了很多时间。
我用它来:
我只在生产代码中使用过一次反射。那是一个情况,我必须对一个启动例程中特定类方法的标准化使用进行调整(抱歉没有更具体的说明 - 那是很久以前,细节有些模糊)。解决问题的唯一方法是引入该方法的不同版本,并在运行时检查各种条件/代码条件等来确定应调用哪个方法。这是一个相当紧凑的代码片段,为本来可能是一个混乱的问题提供了简洁的解决方案。