使用Reflections设置嵌套属性值

我一直在搜索,但找不到我的问题的确切答案。 以下面的代码为例:

public class Company { private string m_strName; private Customer m_objCustomer; public Company() { m_strName = ""; m_objCustomer = new Customer(); } public string Name { get { return m_strName; } set { m_strName = value; } } public Customer CustomerInformaion { get { return m_objCustomer; } set { m_objCustomer = value; } } } public class Customer { private string m_strName; private Details m_objDetails; public Customer() { m_strName = ""; m_objDetails = new Details(); } public string Name { get { return m_strName; } set { m_strName = value; } } public Details CustomerDetails { get { return m_objDetails; } set { m_objDetails = value; } } } public class Details { private string m_strPhoneNumber; private string m_strEmailAddress; public Details() { m_strPhoneNumber = ""; m_strEmailAddress = ""; } public string PhoneNumber { get { return m_strPhoneNumber; } set { m_strPhoneNumber = value; } } public string EmailAddress { get { return m_strEmailAddress; } set { m_strEmailAddress = value; } } } 

现在,我已经设置了一个包含许多文本字段的表单,用户可以在该表单中输入有关公司客户的信息。 其中一个字段是电子邮件地址文本字段,其Tag属性设置为EmailAddress。 我希望能够查看TextBox的Tag并遍历整个Company对象,以查找具有匹配Name的属性,并将其值设置为TextBox的Text属性。 我可以找到该物业,但设定其价值已certificate非常困难。 这是我到目前为止所拥有的:

 foreach (PropertyInfo info in m_objCompany.GetType().GetProperties()) { if (info.PropertyType != typeof(System.String)) { foreach (PropertyInfo info2 in info.PropertyType.GetProperties()) { if (objTextBox.Tag.Equals(info2.Name)) { if (info2.CanWrite) { Object objValue = Convert.ChangeType(objTextBox.Text, info.PropertyType); info2.SetValue(m_objCompany, objValue, null); } } } } } 

我的问题是,当我运行代码时,我在ChangeType和/或SetValue上收到错误。 问题是Reflection正在停止在info2并尝试将值设置为Details of Details – 因为它是Property EmailAddress的父级。

确定如何将SetValue指向适当的属性的任何帮助将是有帮助和赞赏的。 因为我确信你可以猜到我的课程比近100个属性所提供的范例更大。 大多数都是字符串值,将通过TextBox对象手动输入。 我正在尝试创建一个可以被所有TextBox对象调用的例程,从而可以使用该对象的Tag属性来指示我正在尝试设置的类的哪个属性。 从那里开始到XML序列化领域。

你最内线

 info2.SetValue(m_objCompany, objValue, null); 

试图在外部对象上设置内部属性(info2)的值。 外部对象没有内部对象。

你可能想要的是这样的:

  public void Bar(object m_objCompany) { foreach (PropertyInfo info in m_objCompany.GetType().GetProperties()) { if (info.PropertyType != typeof(System.String)) { // Somehow create the outer property object outerPropertyValue = info.PropertyType.GetConstructor(new Type[] { }).Invoke(new object[] { }); foreach (PropertyInfo info2 in info.PropertyType.GetProperties()) { if ("blah" == "blah") { if (info2.CanWrite) { Object innerPropertyValue = Convert.ChangeType("blah", info2.PropertyType); info2.SetValue(outerPropertyValue, innerPropertyValue, null); } } } info.SetValue(m_objCompany, outerPropertyValue, null); } } } 

当您遇到要设置的属性时,需要创建该属性(outerPropertyValue),然后设置该属性的属性(通过innerPropertyValue),然后在原始对象(m_objCompany)上设置外部属性。

这是我用于Reflection的一些代码。 在这种情况下,您需要调用SetValue方法。

 Reflector.SetValue(TARGET_OBJECT, "Customer.Details.PhoneNumber", "ValueToSet"); 

更新:添加了缺少的ConversionResult结构。 对不起,我很抱歉。

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; namespace YourNamespace { public struct ConversionResult { public Boolean Success; public object ConvertedValue; } public static class Reflector { private static BindingFlags DefaultBindings = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic; #region Public Methods ///  /// Execute the "codeToExecute" string on the "source" object ///  /// Object the code should be executed against /// Code that should be executed ex. 'Person.Age' /// The result of execute codeToExecute on source public static object GetValue(object source, String codeToExecute) { ReflectorResult reflectorResult = GetReflectorResult(source, codeToExecute, true, false); if (reflectorResult != null) { return reflectorResult.Value; } return null; } ///  /// Sets the "source" object to the "value" specified in "codeToExecute" ///  /// Object the code should be executed against /// Code that should be executed ex. 'Person.Age' /// Value to set the source+codeToExecute to. public static Boolean SetValue(object source, String codeToExecute, object value) { return SetValue(source, codeToExecute, value, false); } ///  /// Sets the "source" object to the "value" specified in "codeToExecute" ///  /// Object the code should be executed against /// Code that should be executed ex. 'Person.Age' /// Value to set the source+codeToExecute to. /// Creates items it cannot find public static Boolean SetValue(object source, String codeToExecute, object value, Boolean createIfNotExists) { Boolean executed = true; ReflectorResult reflectorResult = GetReflectorResult(source, codeToExecute, false, createIfNotExists); if (reflectorResult != null) { TypeConverter typeConverter = null; PropertyInfo propertyInfo = reflectorResult.MemberInfo as PropertyInfo; if (propertyInfo != null) { if (propertyInfo.CanWrite) { typeConverter = GetTypeConverter(propertyInfo); ConversionResult conversionResult = ConvertValue(value, propertyInfo.PropertyType, typeConverter); if (conversionResult.Success) { propertyInfo.SetValue(reflectorResult.PreviousValue, conversionResult.ConvertedValue, reflectorResult.MemberInfoParameters); } else { executed = false; PentaLogger.LogVerbose("Invalid value: " + value); } } } else { FieldInfo fieldInfo = reflectorResult.MemberInfo as FieldInfo; if (fieldInfo != null) { typeConverter = GetTypeConverter(fieldInfo); ConversionResult conversionResult = ConvertValue(value, fieldInfo.FieldType, typeConverter); if (conversionResult.Success) { fieldInfo.SetValue(reflectorResult.PreviousValue, conversionResult.ConvertedValue); } else { executed = false; PentaLogger.LogVerbose("Invalid value: " + value); } } else { // both property and field are invalid executed = false; } } } else { executed = false; } return executed; } ///  /// Sets the "source" object to the "value" specified in "codeToExecute" ///  /// Object the code should be executed against /// Code that should be executed ex. 'Person.Age' /// Value to set the source+codeToExecute to. public static void RunDynamicCode(object source, String codeToExecute) { GetReflectorResult(source, codeToExecute, true, false); } ///  /// Executes the method on the "source" object with the passed parameters ///  /// Object the code should be executed against /// Method to call /// Method Parameters public static object ExecuteMethod(object source, String methodName, object[] parameters) { if (parameters == null) { parameters = new object[0]; } MethodInfo[] methodInfos = GetMethods(source, methodName); foreach (MethodInfo methodInfo in methodInfos) { object[] convertedParameters = GetParameters(methodInfo, parameters); if (convertedParameters != null) { return methodInfo.Invoke(source, convertedParameters); } } return null; } ///  /// Executes the method on the "source" object with the passed parameters ///  /// Object the code should be executed against /// Method to call /// Method Parameter public static object ExecuteMethod(object source, String methodName, object parameter) { return ExecuteMethod(source, methodName, new object[] { parameter }); } ///  /// Executes the method on the "source" object with no parameters ///  /// Object the code should be executed against /// Method to call public static object ExecuteMethod(object source, String methodName) { return ExecuteMethod(source, methodName, null); } ///  /// Copies all public properties and fields from source to target ///  ///  ///  public static void CopyObject(object source, object target) { if (source != null && target != null) { Type targetType = target.GetType(); Type sourceType = source.GetType(); PropertyInfo[] properties = sourceType.GetProperties(DefaultBindings); foreach (PropertyInfo sourceProperty in properties) { PropertyInfo targetProperty = targetType.GetProperty(sourceProperty.Name, sourceProperty.PropertyType); if (targetProperty != null && targetProperty.CanRead && targetProperty.CanWrite) { object value = sourceProperty.GetValue(source, null); targetProperty.SetValue(target, value, null); } } FieldInfo[] fields = sourceType.GetFields(DefaultBindings); foreach (FieldInfo sourceField in fields) { FieldInfo targetField = targetType.GetField(sourceField.Name); if (targetField != null && targetField.IsPublic) { object value = sourceField.GetValue(source); targetField.SetValue(target, value); } } } } ///  /// Convert the object to the correct type ///  /// Value to convert /// Type to convert to /// Converted value public static ConversionResult ConvertValue(object value, Type type, TypeConverter typeConverter) { ConversionResult conversionResult = new ConversionResult(); conversionResult.Success = false; if (value != null && type != null) { Type objectType = value.GetType(); if (objectType == type) { conversionResult.Success = true; conversionResult.ConvertedValue = value; } else { // If there is an explicit type converter use it if (typeConverter != null && typeConverter.CanConvertFrom(objectType)) { try { conversionResult.ConvertedValue = typeConverter.ConvertFrom(value); conversionResult.Success = true; } catch (FormatException) { } catch (Exception e) { if (!(e.InnerException is FormatException)) { throw; } } } else { try { conversionResult.ConvertedValue = Convert.ChangeType(value, type, CultureInfo.CurrentCulture); conversionResult.Success = true; } catch (InvalidCastException) { } } } } return conversionResult; } public static Boolean CanCreateObect(String classPath, Assembly assembly, params object[] parameters) { Boolean canCreate = false; Type type = Type.GetType(classPath); if (type == null) { String pathWithAssembly = classPath + ", " + assembly.FullName; type = Type.GetType(pathWithAssembly); } if (type != null) { foreach (ConstructorInfo ci in type.GetConstructors()) { if (ci.IsPublic) { ParameterInfo[] constructorParameters = ci.GetParameters(); if (constructorParameters.Length == parameters.Length) { for(Int32 i=0; i items = new List(); Int32 parenAndbracketCount = 0; String buffer = ""; foreach (Char c in codeToExecute.ToCharArray()) { if (c == '.') { if (buffer.Length > 0) { items.Add(buffer); buffer = ""; } continue; } else if (c == '[') { parenAndbracketCount++; if (buffer.Length > 0) { items.Add(buffer); } buffer = c.ToString(); } else if (c == ']' || c == ')') { parenAndbracketCount--; buffer += c; if (buffer.Length > 0) { items.Add(buffer); buffer = ""; } } else if (Char.IsWhiteSpace(c) || Char.IsControl(c)) { if (parenAndbracketCount == 0) { // Skip it continue; } else { buffer += c; } } else if (c == '(') { parenAndbracketCount++; buffer += c; } else { buffer += c; } } if (buffer.Length > 0) { items.Add(buffer); } return items.ToArray(); } private static object[] GetParameters(String codeFragment, MemberInfo memberInfo) { String parameters = SplitParametersFromMethod(codeFragment); if (String.IsNullOrEmpty(parameters)) return new object[0]; object[] parameterArray = parameters.Split(','); return GetParameters(memberInfo, parameterArray); } private static object[] GetParameters(MemberInfo memberInfo, object[] parameterArray) { ParameterInfo[] parameterInfo = null; TypeConverter typeConverter = null; PropertyInfo propertyInfo = memberInfo as PropertyInfo; if (propertyInfo != null) { parameterInfo = propertyInfo.GetIndexParameters(); typeConverter = GetTypeConverter(parameterInfo[0]); } else { MethodInfo methodInfo = memberInfo as MethodInfo; if (methodInfo != null) { parameterInfo = methodInfo.GetParameters(); } } if (parameterInfo == null) { return null; } object[] returnParameters = new object[parameterInfo.Length]; for (Int32 i = 0; i < parameterArray.Length; i++) { ConversionResult converstionResult = ConvertValue(parameterArray[i], parameterInfo[i].ParameterType, typeConverter); if (converstionResult.Success) { returnParameters[i] = converstionResult.ConvertedValue; } else { return null; } } return returnParameters; } private static TypeConverter GetTypeConverter(MemberInfo memberInfo, Type targetType) { object[] typeConverters = memberInfo.GetCustomAttributes(typeof(TypeConverterAttribute), true); if (typeConverters.Length > 0) { TypeConverterAttribute typeConverterAttribute = (TypeConverterAttribute)typeConverters[0]; Type typeFromName = Type.GetType(typeConverterAttribute.ConverterTypeName); if ((typeFromName != null) && typeof(TypeConverter).IsAssignableFrom(typeFromName)) { return (TypeConverter)Activator.CreateInstance(typeFromName); } } return TypeDescriptor.GetConverter(targetType); } private static TypeConverter GetTypeConverter(PropertyInfo propertyInfo) { return GetTypeConverter(propertyInfo, propertyInfo.PropertyType); } private static TypeConverter GetTypeConverter(FieldInfo fieldInfo) { return GetTypeConverter(fieldInfo, fieldInfo.FieldType); } private static TypeConverter GetTypeConverter(ParameterInfo parameterInfo) { return GetTypeConverter(parameterInfo.Member, parameterInfo.ParameterType); } private static ArrayDefinition GetArrayDefinition(object value, String codeToExecute) { // All IList classes have an Item property except for System.Array. List retrieveMemberInfos = new List(); foreach (PropertyInfo propertyInfo in value.GetType().GetProperties(DefaultBindings)) { if (propertyInfo.Name == "Item") { retrieveMemberInfos.Add(propertyInfo); } } if (retrieveMemberInfos.Count == 0) { // We didn't find any Item properties so this is probably an Array. Use the GetValue method foreach (MethodInfo methodInfo in value.GetType().GetMethods(DefaultBindings)) { if (methodInfo.Name == "GetValue") { retrieveMemberInfos.Add(methodInfo); } } } // Some members have overloaded this[] methods. Find the correct method. foreach (MemberInfo memberInfo in retrieveMemberInfos) { object[] parameters = GetParameters(codeToExecute, memberInfo); if (parameters != null) { ArrayDefinition arrayDefinition = new ArrayDefinition(); arrayDefinition.Parameters = parameters; arrayDefinition.RetrieveMemberInfo = memberInfo; return arrayDefinition; } } return null; } private static void ProcessArray(ReflectorResult result, String codeFragment, Boolean createIfNotExists) { Int32 failCount = 0; ArrayDefinition arrayDefinition = GetArrayDefinition(result.Value, codeFragment); if (arrayDefinition != null) { // If this is anything but System.Array we need to call a Property PropertyInfo propertyInfo = arrayDefinition.RetrieveMemberInfo as PropertyInfo; if (propertyInfo != null) { SetPropertyInfoValue: try { object value = propertyInfo.GetValue(result.Value, arrayDefinition.Parameters); result.SetResult(value, propertyInfo, arrayDefinition.Parameters); } catch (TargetInvocationException ex) { failCount++; if (ex.InnerException is ArgumentOutOfRangeException && failCount == 1 && createIfNotExists) { if (CreateArrayItem(result, arrayDefinition)) { goto SetPropertyInfoValue; } } // Tried to fix it but failed. Blow up result.Clear(); throw new InvalidCodeFragmentException(codeFragment); } } else { // System.Array has a Method to call MethodInfo methodInfo = arrayDefinition.RetrieveMemberInfo as MethodInfo; if (methodInfo != null) { try { // We can't support dynamically creating array items object value = methodInfo.Invoke(result.Value, arrayDefinition.Parameters); result.SetResult(value, methodInfo, arrayDefinition.Parameters); } catch (TargetInvocationException) { result.Clear(); throw new InvalidCodeFragmentException(codeFragment); } } } } else { result.Clear(); throw new InvalidCodeFragmentException(codeFragment); } } private static Boolean CreateArrayItem(ReflectorResult result, ArrayDefinition arrayDefinition) { Type resultType = result.Value.GetType(); Type containedType = null; if (resultType.IsArray) { containedType = resultType.GetElementType(); } else { containedType = resultType.GetGenericArguments()[0]; } object newInstance = Activator.CreateInstance(containedType); if (!resultType.IsArray) { MethodInfo[] methods = GetMethods(result.Value, "Insert"); foreach (MethodInfo methodInfo in methods) { object[] temp = new object[arrayDefinition.Parameters.Length + 1]; arrayDefinition.Parameters.CopyTo(temp, 0); temp[arrayDefinition.Parameters.Length] = newInstance; object[] parameters = GetParameters(methodInfo, temp); if (parameters != null) { methodInfo.Invoke(result.Value, parameters); return true; } } } return false; } private static void ProcessProperty(ReflectorResult result, String codeFragment, bool retrieveValue) { // This is just a regular property PropertyInfo propertyInfo = result.Value.GetType().GetProperty(codeFragment, DefaultBindings); if (propertyInfo != null) { object value = result.Value; if (retrieveValue) { value = propertyInfo.GetValue(result.Value, null); result.SetResult(value, propertyInfo, null); } result.SetResult(value, propertyInfo, null); } else { // Maybe it is a field FieldInfo fieldInfo = result.Value.GetType().GetField(codeFragment, DefaultBindings); if (fieldInfo != null) { object value = result.Value; if (retrieveValue) { value = fieldInfo.GetValue(result.Value); } result.SetResult(value, fieldInfo, null); } else { // This item is missing, log it and set the value to null result.Clear(); throw new InvalidCodeFragmentException(codeFragment); } } } private static void ProcessMethod(ReflectorResult result, String codeFragment) { // This is just a regular property String methodName = codeFragment.Substring(0, codeFragment.IndexOf('(')); MethodInfo[] methodInfos = GetMethods(result.Value, methodName); foreach (MethodInfo methodInfo in methodInfos) { object[] parameters = GetParameters(codeFragment, methodInfo); if (parameters != null) { object value = methodInfo.Invoke(result.Value, parameters); result.SetResult(value, null, null); break; } } } private static String SplitParametersFromMethod(String codeFragment) { char startCharacter = '['; char endCharacter = ']'; if (codeFragment.EndsWith(")", StringComparison.CurrentCulture)) { // This is a function startCharacter = '('; endCharacter = ')'; } Int32 startParam = codeFragment.IndexOf(startCharacter) + 1; if (startParam < 1) return null; Int32 endParam = codeFragment.IndexOf(endCharacter); if (endParam < 0) return null; return codeFragment.Substring(startParam, endParam - startParam).Trim(); } private static MethodInfo[] GetMethods(object value, String methodName) { if (String.IsNullOrEmpty(methodName)) { throw new ArgumentNullException("methodName"); } if (value == null) { return new MethodInfo[0]; } List methodInfos = new List(); foreach (MethodInfo methodInfo in value.GetType().GetMethods(DefaultBindings)) { if (methodInfo.Name == methodName) { methodInfos.Add(methodInfo); } } return methodInfos.ToArray(); } #endregion #region Helper Classes private class ArrayDefinition { public MemberInfo RetrieveMemberInfo { get; set; } public object[] Parameters { get; set; } } private class ReflectorResult { public ReflectorResult(object startValue) { SetResult(startValue, null, null); } public MemberInfo MemberInfo { get; private set; } public object[] MemberInfoParameters { get; private set; } public object PreviousValue { get; set; } public object Value { get; private set; } public void SetResult(object value, MemberInfo memberInfo, object[] memberInfoParameters) { Value = value; MemberInfo = memberInfo; MemberInfoParameters = memberInfoParameters; } public void Clear() { MemberInfo = null; Value = null; PreviousValue = null; } } [Serializable] [SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic")] [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")] private class InvalidCodeFragmentException : Exception { public InvalidCodeFragmentException(String invalidFragment) : base(invalidFragment) { } } #endregion } }