The Reflection API provides the ability to manipulate an object's state, meaning its properties and fields. Using the Reflection API, you can call property accessors as well as directly change the value of an object's fields.
The two classes that represent fields and properties are the FieldInfo class and the PropertyInfo class, respectively. As with most of the reflection classes, you first have to search and discover a FieldInfo or PropertyInfo object before it can be invoked. Again, the Type class provides methods for discovering both fields and properties.
The Type.GetFields method has two overloads. One overload takes no parameters and returns an array of FieldInfo objects that represents all of the public fields defined for the Type. If there are no public fields defined, then an empty array of type FieldInfo is returned. Listing 13.15 code demonstrates how to use the GetFields method to discover all of the public fields on a class:
C# public class Type_GetFields { public int intType; public bool boolType; public string stringType; } public class Test { public static void Main(string[] args) { Type t = typeof(Type_GetFields); FieldInfo[] fields = t.GetFields(); foreach(FieldInfo field in fields) { MessageBox.Show(String.Format( "Found {0} field {1} of type {2} ", (field.IsPublic ? "Public" : "Non-Public"), field.Name, field.FieldType)); } } } VB Module Module1 Public Class Type_GetFields Public intType As Int32 Public boolType As Boolean Public stringType As String End Class Public Sub Main() Dim tgf = New Type_GetFields() Dim t = tgf.GetType() Dim fields() = t.GetFields() Dim visibility As String Dim i As Int32 For i = 0 To fields.Length - 1 If fields(i).IsPublic Then visibility = "Public" Else visibility = "Non-Public" End If MessageBox.Show(String.Format( _ "Found {0} field {1} of type {2} ", _ visibility, _ fields(i).Name, _ fields(i).FieldType)) Next i End Sub End Module |
The GetFields method has an overload that allows you to customize how the search for the desired fields is conducted. This customization is controlled by passing a BindingFlags value to the GetFields method. The BindingFlags values can either define which fields to include in the search, values described in Table 13.4, or change how the search works, values described in Table 13.5.
MEMBER | MEANING |
---|---|
Instance | Includes instance fields in the search |
Static | Includes static fields in the search |
Public | Includes public fields in the search |
NonPublic | Includes private and protected fields in the search |
FlattenHierarchy | Includes static fields up the class hierarchy |
MEMBER | MEANING |
---|---|
DeclaredOnly | Searches only the fields declared on the current Type and not fields that were inherited |
It should be noted that if neither BindingFlags.Instance nor BindingFlags.Static is specified, then no FieldInfo objects will be returned. If no fields are found that adhere to the specified binding constraints, or if no fields are defined for the Type, then a zero length array of type FieldInfo is returned. Listing 13.16 demonstrates how to search for a field by using a binding constraint.
C# public class Type_GetFields { public static int StaticIntType; public static bool StaticBoolType; public static string StaticStringType; protected static short StaticShortType; public int IntType; public bool boolType; public string stringType; } public class Test { public static void Main() { Type t = typeof(Type_GetFields); FieldInfo[] fields = t.GetFields(BindingFlags.Static | BindingFlags.Public); foreach(FieldInfo field in fields) { MessageBox.Show(String.Format( "Found {0} {1} field {2} of type {3} ", (field.IsPublic ? "public" : "non-public"), (field.IsStatic ? "static" : "instance"), field.Name, field.FieldType)); } } } VB Module Module1 Public Class Type_GetFields Public Shared SharedIntType As Int32 Public Shared SharedBoolType As Boolean Public Shared SharedStringType As String Protected Shared SharedShortType As String Public intType As Int32 Public boolType As Boolean Public stringType As String End Class Public Sub Main() Dim tgf = New Type_GetFields() Dim t = tgf.GetType() Dim fields = t.GetFields(BindingFlags.Static Or _ BindingFlags.Public) Dim visibility As String Dim i As Int32 For i = 0 To fields.Length - 1 If fields(i).IsPublic Then visibility = "Public" Else visibility = "Non-Public" End If MessageBox.Show(String.Format( _ "Found {0} field {1} of type {2} ", _ visibility, _ fields(i).Name, _ fields(i).FieldType)) Next i End Sub End Module |
The Type class also provides the GetField method for discovering a Type's fields by name and an optional binding constraint. The GetField type comes in two overloaded versions. The simplest overload accepts the string name of the field for which to search. The search for the name is case-sensitive, and if the field is not found or the requested field is nonpublic, then null is returned. Listing 13.17 demonstrates how to search for a field by its name only.
C# public class Type_GetField { public int IntType; public bool boolType; public string stringType; } public class Test { public static void Main() { Type t = typeof(Type_GetField); FieldInfo field = t.GetField("stringType"); MessageBox.Show(String.Format( "Found {0} {1} field {2} of type {3} ", (field.IsPublic ? "public" : "non-public"), (field.IsStatic ? "static" : "instance"), field.Name, field.FieldType)); } } VB Module Module1 Public Class Type_GetFields Public intType As Int32 Public boolType As Boolean Public stringType As String End Class Public Sub Main() Dim tgf = New Type_GetFields() Dim t = tgf.GetType() Dim field = t.GetField("stringType") Dim visibility As String If field.IsPublic Then visibility = "Public" Else visibility = "Non-Public" End If MessageBox.Show(String.Format( _ "Found {0} field {1} of type {2} ", _ visibility, _ field.Name, _ field.FieldType)) End Sub End Module |
The second overload of GetField allows you to search for fields given a binding constraint. As with all reflection methods, the binding constraints are specified by the BindFlags enumeration. The BindingFlags values that apply to the GetField method can either define which fields to include in the search or change how the search works. Tables 13.6 and 13.7 describe both types of BindingFlags enumeration.
MEMBER | MEANING |
---|---|
Instance | Includes instance fields in the search |
Static | Includes static fields in the search |
Public | Includes public fields in the search |
NonPublic | Includes private and protected fields in the search |
FlattenHierarchy | Includes static fields up the class hierarchy |
MEMBER | MEANING |
---|---|
IgnoreCase | Ignores the case of the specified name |
DeclaredOnly | Searches only the fields declared on the current Type and not fields that were inherited |
As with GetFields, either BindingFlags.Instance or BindingFlags.Static must be supplied, or null will be returned. If a field that matches the specified binding constraints cannot be found, null is returned. Listing 13.18 demonstrates how to find a static field through a case-insensitive search.
C# public class Type_GetField { public static int StaticIntType; public static bool StaticBoolType; public static string StaticStringType; public static short StaticShortType; } public class Test { public static void Main() { Type t = typeof(Type_GetField); FieldInfo field = t.GetField("sTATICsHORTtYPE", BindingFlags.Static | BindingFlags.IgnoreCase | BindingFlags.Public); if(field == null) { MessageBox.Show("Could not find the field StaticShortType"); return; } MessageBox.Show(String.Format( "Found {0} {1} field {2} of type {3} ", (field.IsPublic ? "public" : "non-public"), (field.IsStatic ? "static" : "instance"), field.Name, field.FieldType)); } } VB Module Module1 Public Class Type_GetField Public Shared SharedIntType As Int32 Public Shared SharedBoolType As Boolean Public Shared SharedStringType As String Public Shared SharedShortType As Short End Class Public Sub Main() Dim tgf = New Type_GetField() Dim t = tgf.GetType() Dim field = t.GetField("sHaReDsHORTtYPE", _ BindingFlags.Static Or _ BindingFlags.IgnoreCase Or _ BindingFlags.Public) Dim visibility As String If field Is Nothing Then MessageBox.Show("Could not find the field StaticShortType") Return End If If field.IsPublic Then visibility = "Public" Else visibility = "Non-Public" End If MessageBox.Show(String.Format( _ "Found {0} field {1} of type {2} ", _ visibility, _ field.Name, _ field.FieldType)) End Sub End Module |
Now that we have a FieldInfo object, we can use it to get or set a field's value. The FieldInfo object provides the GetValue method and the SetValue methods to expose this functionality.
The GetValue method takes a single parameter of type object. This object is the type instance whose field will be retrieved. The object should be an instance of a class that inherits or declares the field. If the field is static, then the object parameter is ignored. The GetValue method returns an object instance representing the field value. Listing 13.19 demonstrates retrieving a Type's public field.
C# public class FieldInfo_GetValue { public int intType = 10; public bool boolType = true; public string stringType = "Initial Value"; } public class Test { public static void Main() { Type t = typeof(FieldInfo_GetValue); FieldInfo field = t.GetField("stringType"); if(field == null) { MessageBox.Show("Could not find the field named stringType"); return; } FieldInfo_GetValue obj = new FieldInfo_GetValue(); object value = field.GetValue(obj); MessageBox.Show( string.Format("Found the value, '{0}', in the {1} field", value.ToString(), field.Name)); } } VB Module Module1 Public Class Field_GetValue Public intType = 10 Public boolType = True Public stringType = "Initial Value" End Class Public Sub Main() Dim tgv = New Field_GetValue() Dim t = tgv.GetType() Dim field = t.GetField("stringType") If field Is Nothing Then MessageBox.Show("Could not find the field stringType") Return End If Dim obj = New Field_GetValue() Dim value = field.GetValue(obj) MessageBox.Show( _ String.Format("Found the value, '{0}', in the {1} field", _ value.ToString(), _ field.Name)) End Sub End Module |
Using the SetValue method is just as simple. Unlike GetValue, which takes one parameter, SetValue takes two. The first parameter is of type object, and it is the type instance whose field will be changed. If the field is static, then this parameter is ignored. Again, the object should be an instance of a class that inherits or declares the field. The second parameter is also of type object, but this object contains the new value to assign to the field. This object should contain an instance that is the same type as the field. Listing 13.20 demonstrates how to use the SetValue method.
C# public class FieldInfo_SetValue { public int IntType = 10; public bool boolType = true; public string stringType = "Initial Value"; } public class Test { public static void Main() { Type t = typeof(FieldInfo_SetValue); FieldInfo field = t.GetField("stringType"); if(field == null) { MessageBox.Show("Could not find the field named stringType"); return; } FieldInfo_SetValue obj = new FieldInfo_SetValue(); object value = field.GetValue(obj); MessageBox.Show(string.Format( "Found the value, '{0}', in the {1} " + "field before changing the field", value.ToString(), field.Name)); string newValue = "New Value"; field.SetValue(obj, newValue); value = field.GetValue(obj); MessageBox.Show(string.Format( "Found the value, '{0}', in the {1} " + "field after changing the field", value.ToString(), field.Name)); } } VB Module Module1 Public Class FieldInfo_SetValue Public intType = 10 Public boolType = True Public stringType = "Initial Value" End Class Public Sub Main() Dim tsv = New FieldInfo_SetValue() Dim t = tsv.GetType() Dim field = t.GetField("stringType") Dim obj = New FieldInfo_SetValue() Dim value = field.GetValue(obj) MessageBox.Show(String.Format( _ "Found the value, '{0}', in the {1} field" & _ " before changing the field", _ value.ToString(), _ field.Name)) Dim newValue = "New Value" field.SetValue(obj, newValue) value = field.GetValue(obj) MessageBox.Show(String.Format( _ "Found the value, '{0}', in the {1} " & _ "field after changing the field", _ value.ToString(), _ field.Name)) End Sub End Module |
Let us now discuss manipulating an object's properties. The Type.GetProperties method has two overloads. One overload takes no parameters and returns an array of PropertyInfo objects that represents all of the public properties defined for the Type. If there are no public properties defined, then an empty array of type PropertyInfo is returned. Listing 13.21 demonstrates how to use the GetProperties method to discover all of the public fields in a class.
C# public class Type_GetProperties { int intType = 0; bool boolType = false; string stringType = "Initial Value"; public int Int { get{ return intType; } set{ intType = value; } } public bool Bool { set{ boolType = value; } } public string String { get{ return stringType; } } } public class Test { public static void Main() { Type t = typeof(Type_GetProperties); PropertyInfo[] properties = t.GetProperties(); foreach(PropertyInfo property in properties) { MessageBox.Show(String.Format( "Found property {2} of type {3} with {0}{1} access", (property.CanRead ? "Read" : ""), (property.CanWrite ? "Write" : ""), property.Name, property.PropertyType)); } } } VB Module Module1 Public Class Type_GetProperties Dim intType = 0 Dim boolType = False Dim strType = "Initial Value" Public Property Int32Type() As Int32 Get Return intType End Get Set(ByVal Value As Int32) intType = Value End Set End Property Public WriteOnly Property BooleanType() As Boolean Set(ByVal Value As Boolean) boolType = Value End Set End Property Public ReadOnly Property StringType() As String Get Return strType End Get End Property End Class Sub Main() Dim tgp = New Type_GetProperties() Dim t = tgp.GetType() Dim properties() = t.GetProperties() Dim i As Int32 Dim canRead As String Dim canWrite As String For i = 0 To properties.Length - 1 If properties(i).CanRead Then canRead = "Read" Else canRead = String.Empty End If If properties(i).CanWrite Then canWrite = "Write" Else canWrite = String.Empty End If MessageBox.Show(String.Format( _ "Found property {2} of type {3} with {0}{1} access", _ canRead, _ canWrite, _ properties(i).Name, _ properties(i).PropertyType)) Next i End Sub End Module |
The GetProperties method has an overload that allows you to specify the binding constraints by passing a BindingFlags value to the GetProperties method. The BindingFlags values can either define which fields to include in the search, values described in Table 13.8, or change how the search works, values described in Table 13.9.
MEMBER | MEANING |
---|---|
Instance | Includes instance properties in the search |
Static | Includes static properties in the search |
Public | Includes public properties in the search |
NonPublic | Includes private and protected properties in the search |
FlattenHierarchy | Includes static properties up the class hierarchy |
MEMBER | MEANING |
---|---|
DeclaredOnly | Searches only the properties declared on the current Type and not properties that were inherited |
The meaning of BindingFlags.Public and BindingFlags.NonPublic is a little different for properties. A property is considered public to reflection if it has at least one accessor that is public. Otherwise, the property is considered private.
If a property that matches the specified binding constraints cannot be found, then an empty array is returned. Listing 13.22 demonstrates how to retrieve a list of static properties from a given type.
C# public class Type_GetProperties { int intType = 0; static bool boolType = false; static string stringType = "Initial Value"; public int Int { get{ return intType; } set{ intType = value; } } public static bool Bool { set{ boolType = value; } } public static string String { get{ return stringType; } } } public class Test { public static void Main() { Type t = typeof(Type_GetProperties); PropertyInfo[] properties = t.GetProperties(BindingFlags.Static | BindingFlags.Public); foreach(PropertyInfo property in properties) { MessageBox.Show(String.Format( "Found property {0} of type {1} with {2}{3} access", property.Name, property.PropertyType, (property.CanRead ? "Read" : ""), (property.CanWrite ? "Write" : ""))); } } } VB Module Module1 Public Class Type_GetProperties Dim intType = 0 Shared boolType = False Shared strType = "Initial Value" Public Property Int32Type() As Int32 Get Return intType End Get Set(ByVal Value As Int32) intType = Value End Set End Property Public Shared WriteOnly Property BooleanType() As Boolean Set(ByVal Value As Boolean) boolType = Value End Set End Property Public Shared ReadOnly Property StringType() As String Get Return strType End Get End Property End Class Sub Main() Dim tgp = New Type_GetProperties() Dim t = tgp.GetType() Dim properties() = _ t.GetProperties(BindingFlags.Static Or BindingFlags.Public) Dim i As Int32 Dim canRead As String Dim canWrite As String For i = 0 To properties.Length - 1 If properties(i).CanRead Then canRead = "Read" Else canRead = String.Empty End If If properties(i).CanWrite Then canWrite = "Write" Else canWrite = String.Empty End If MessageBox.Show(String.Format( _ "Found property {2} of type {3} with {0}{1} access", _ canRead, _ canWrite, _ properties(i).Name, _ properties(i).PropertyType)) Next i End Sub End Module |
The Type class also provides the GetProperty method. The first overload we will discuss takes one parameter, the name of the property. This method searches all public properties attempting to match the name of the property to the specified name parameter. The search is case-sensitive, and if the property cannot be found, a null reference is returned. Listing 13.23 demonstrates how to use this method.
C# public class Type_GetProperty { int intType = 0; static bool boolType = false; static string stringType = "Initial Value"; public int Int { get{ return intType; } set{ intType = value; } } public bool Bool { set{ boolType = value; } } public string String { get{ return stringType; } } } public class Test { public static void Main(string[] args) { Type t = typeof(Type_GetProperty); PropertyInfo property = t.GetProperty("Int"); MessageBox.Show(String.Format( "Found property {0} of type {1} with {2}{3} access", property.Name, property.PropertyType, (property.CanRead ? "Read" : ""), (property.CanWrite ? "Write" : ""))); } } VB Module Module1 Public Class Type_GetProperties Dim intType = 0 Shared boolType = False Shared strType = "Initial Value" Public Property Int32Type() As Int32 Get Return intType End Get Set(ByVal Value As Int32) intType = Value End Set End Property Public WriteOnly Property BooleanType() As Boolean Set(ByVal Value As Boolean) boolType = Value End Set End Property Public ReadOnly Property StringType() As String Get Return strType End Get End Property End Class Sub Main() Dim tgp = New Type_GetProperties() Dim t = tgp.GetType() Dim prop = t.GetProperty("Int32Type") Dim canRead As String Dim canWrite As String If prop.CanRead Then canRead = "Read" Else canRead = String.Empty End If If prop.CanWrite Then canWrite = "Write" Else canWrite = String.Empty End If MessageBox.Show(String.Format( _ "Found property {2} of type {3} with {0}{1} access", _ canRead, _ canWrite, _ prop.Name, _ prop.PropertyType)) End Sub End Module |
The second override allows for searching with given binding context specified by passing a BindingFlags enumeration value. The BindingFlags values that apply to the GetProperty method are specified in Tables 13.10 and 13.11.
MEMBER | MEANING |
---|---|
IgnoreCase | Ignores the case of the specified name |
DeclaredOnly | Searches only the properties declared on the current Type and not properties that were inherited |
This method is demonstrated by the code in Listing 13.24.
C# public class Type_GetProperty { int intType = 0; static bool boolType = false; static string stringType = "Initial Value"; public int Int { get{ return intType; } set{ intType = value; } } public static bool Bool { set{ boolType = value; } } public static string String { get{ return stringType; } } } public class Test { public static void Main() { Type t = typeof(Type_GetProperty); PropertyInfo property = t.GetProperty("bOOl", BindingFlags.Static | BindingFlags.IgnoreCase | BindingFlags.Public); MessageBox.Show(String.Format( "Found property {0} of type {1} with {2}{3} access", property.Name, property.PropertyType, (property.CanRead ? "Read" : ""), (property.CanWrite ? "Write" : ""))); } } VB Module Module1 Public Class Type_GetProperty Dim intType = 0 Shared boolType = False Shared strType = "Initial Value" Public Property Int32Type() As Int32 Get Return intType End Get Set(ByVal Value As Int32) intType = Value End Set End Property Public Shared WriteOnly Property BooleanType() As Boolean Set(ByVal Value As Boolean) boolType = Value End Set End Property Public ReadOnly Property StringType() As String Get Return strType End Get End Property End Class Sub Main() Dim tgp = New Type_GetProperty() Dim t = tgp.GetType() Dim prop = t.GetProperty("BoOlEaNType", _ BindingFlags.Static Or _ BindingFlags.IgnoreCase Or _ BindingFlags.Public) Dim canRead As String Dim canWrite As String If prop.CanRead Then canRead = "Read" Else canRead = String.Empty End If If prop.CanWrite Then canWrite = "Write" Else canWrite = String.Empty End If MessageBox.Show(String.Format( _ "Found property {2} of type {3} with {0}{1} access", _ canRead, _ canWrite, _ prop.Name, _ prop.PropertyType)) End Sub End Module |
Now that we have a PropertyInfo object in hand, we can get and set its values by using the PropertyInfo's SetValue and GetValue. These methods are very similar to their FieldInfo counterparts except that they each take an extra parameter of type Object[]. This object array represents index values for indexed properties. If the property is not indexed, then this parameter should be null. Listing 13.25 demonstrates how to use both the GetValue and SetValue methods.
C# public class PropertyInfo_SetValue { int intType = 0; bool boolType = false; string stringType = "Initial Value"; public int Int { get{ return intType; } set{ intType = value; } } public bool Bool { set{ boolType = value; } } public string String { get{ return stringType; } set{ stringType = value; } } } public class Test { public static void Main(string[] args) { Type t = typeof(PropertyInfo_SetValue); PropertyInfo property = t.GetProperty("String"); if(property == null) { MessageBox.Show("Could not find the property named String"); return; } PropertyInfo_SetValue obj = new PropertyInfo_SetValue(); object value = property.GetValue(obj, null); MessageBox.Show(string.Format( "Found the value, '{0}', in the {1} " + "property before changing the property", value.ToString(), property.Name)); string newValue = "New Value"; property.SetValue(obj, newValue, null); value = property.GetValue(obj, null); MessageBox.Show(string.Format( "Found the value, '{0}', in the {1} " + "property after changing the field", value.ToString(), property.Name)); } } VB Module Module1 Public Class PropertyInfo_SetValue Dim intType = 0 Shared boolType = False Shared strType = "Initial Value" Public Property Int32Type() As Int32 Get Return intType End Get Set(ByVal Value As Int32) intType = Value End Set End Property Public WriteOnly Property BooleanType() As Boolean Set(ByVal Value As Boolean) boolType = Value End Set End Property Public Property StringType() As String Get Return strType End Get Set(ByVal Value As String) strType = Value End Set End Property End Class Sub Main() Dim tgp = New PropertyInfo_SetValue() Dim t = tgp.GetType() Dim prop = t.GetProperty("StringType") If prop Is Nothing Then MessageBox.Show("Could not find the property named String") Return End If Dim obj = New PropertyInfo_SetValue() Dim value = prop.GetValue(obj, Nothing) MessageBox.Show(String.Format( _ "Found the value, '{0}', in the {1} " & _ "property before changing the property", _ value.ToString(), _ prop.Name)) Dim newValue = "New Value" prop.SetValue(obj, newValue, Nothing) value = prop.GetValue(obj, Nothing) MessageBox.Show(String.Format( _ "Found the value, '{0}', in the {1} " & _ "property after changing the field", _ value.ToString(), _ prop.Name)) End Sub End Module |
3.144.90.182