反射提供了在运行时确定事物并执行代码的能力。
如果您不想使用它,也不必使用它,但它对于动态行为非常有用。
例如:
a)您可以使用反射通过加载外部配置文件并基于其启动服务来配置应用程序。只要它们符合特定接口或API,您的应用程序就不必预先知道实现这些服务的类。
b)使用反射,您可以即时生成类和代码,从而简化某些编程任务,因为程序员不必明确创建所需的所有代码。
c)反射对于检查代码的程序非常宝贵。其中一个例子是集成开发环境(IDE)或用户界面(UI)设计器。
d)反射帮助您减少样板代码。
e)反射对于在代码中定义小型领域特定语言(DSL)非常方便。
(我的定义)反射是编写静态代码以在运行时执行代码的能力,通常在编译时确定。
例如,我可以通过编译该命令来调用类方法进行绘图:
pen.DrawLine()
通过反射,我可以首先查看我的对象是否有一个叫做“drawline”的方法,如果有,就调用它。(请注意,这不是实际的C#反射语法)
if(pen.Methods.Contains("DrawLine"))
{
pen.InvokeMethod("DrawLine"))
}
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;
}
}
}
}
假设你有一个接口的两个不同实现。你想要让用户通过一个简单的文本配置文件来选择其中一个。
使用反射,你可以从配置文件中读取你想要使用的实现类的名称(作为字符串),并实例化该类的一个实例。
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);
}
}
反射可以让您深入一个程序集并使用它,即使您以后没有引用它。
考虑一个插件系统,其中主机不知道它将持有哪些插件;使用反射(和正确的架构),您可以构建一个简单的解决方案来实现这一点。
再考虑另一种情况,您必须根据字符串调用对象上的方法,反射也可以帮助您实现这一点。
还有许多其他用途,但我希望这两个用例能够激发您对CLR这个优秀特性的兴趣。
反射是使用代码来检查代码本身。例如,您可以调用 foo.DoBar()
,也可以调用:
foo.GetType().GetMethod("DoBar").Invoke(foo,null);
有许多情况下,应用程序不应该对自身做出太多假设,而应该在运行时查看其实际情况。这就是反射发挥作用的地方。例如,ASP.NET对使用的成员资格提供程序没有任何假设,除了它继承了适当的类。它使用反射在运行时查找类并实例化它。由于解耦和减少类之间的依赖性,这提供了很大程度上的可扩展性。
当然,这只是反射的单个用例。还有许多其他用例可能非常有用。
在.NET世界中,反射是运行时类型发现的过程。
我不确定我能给你一个准确的解释,但我可以告诉你我如何使用它。
我可以在程序集(dll)上放置一个用户定义的属性。使用反射在运行时,我可以检查某个目录位置中的所有程序集,以查看它们是否定义了此属性。这可以提示我的应用程序如何使用程序集,比如作为插件。
我还使用反射来加载和保存应用程序设置。