У меня есть несколько больших объектов, каждая из которых имеет около 60 строк. Мне нужно обрезать все эти строки, и я бы хотел сделать это, не запустив this.mystring = this.mystring.Trim(). Вместо этого я ищу способ автоматического обнаружения каждым объектом всех собственных строк, а затем выполнить операцию.
Я немного разбираюсь в размышлениях, но недостаточно, но я думаю, что это возможно?
Кроме того, я не уверен, имеет ли значение это значение, но некоторые свойства строки доступны только для чтения (только у них есть getter), поэтому эти свойства должны быть пропущены.
Справка
Ответ 1
Ну, достаточно легко получить все свойства и выяснить, какие из них являются строками и доступными для записи. LINQ делает это еще проще.
var props = instance.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore non-string properties
.Where(prop => prop.PropertyType == typeof(string))
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
string value = (string) prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
Вы можете захотеть установить свойство только в том случае, если обрезка действительно имеет значение, чтобы избежать избыточных вычислений для сложных свойств - или это может не быть проблемой для вас.
Существуют различные способы повышения производительности при необходимости - например:
- Простое кэширование соответствующих свойств для каждого типа
- Использование
Delegate.CreateDelegate
для создания делегатов для геттеров и сеттеров
- Возможно, использование деревьев выражений, хотя я не уверен, помогут ли они здесь.
Я бы не принял ни одного из этих шагов, если производительность на самом деле не проблема.
Ответ 2
Что-то вроде:
foreach (PropertyInfo prop in obj.GetType().GetProperties(
BindingFlags.Instance | BindingFlags.Public))
{
if (prop.CanRead && prop.CanWrite && prop.PropertyType == typeof(string)
&& (prop.GetIndexParameters().Length == 0)) // watch for indexers!
{
var s = (string)prop.GetValue(obj, null);
if (!string.IsNullOrEmpty(s)) s = s.Trim();
prop.SetValue(obj, s, null);
}
}
Ответ 3
Не нужно делать IEnumerable
проверку петли реквизита, и если фактический экземпляр является IEnumerable
, реквизиты игнорируются. Исправить для части IEnumerable
:
private void TrimWhitespace(object instance)
{
if (instance != null)
{
if (instance is IEnumerable)
{
foreach (var item in (IEnumerable)instance)
{
TrimWhitespace(item);
}
}
var props = instance.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
if (prop.GetValue(instance, null) is string)
{
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else
TrimWhitespace(prop.GetValue(instance, null));
}
}
}
Ответ 4
Итак, чтобы немного расшириться, у меня есть сложный объект со списками списков, и я хотел бы пройти его и обрезать все дочерние строковые объекты. Я отправляю то, что я сделал, из того, что я построил с @Jon, в его ответе. Мне любопытно, есть ли лучший способ сделать это, или если я пропустил что-то очевидное.
Объекты, которые у меня есть, более сложны, чем это, но это должно проиллюстрировать, что я пытался.
public class Customer
{
public string Name { get; set; }
public List<Contact> Contacts { get; set; }
}
public class Contact
{
public string Name { get; set; }
public List<Email> EmailAddresses {get; set;}
}
public class Email
{
public string EmailAddress {get; set;}
}
private void TrimWhitespace(object instance)
{
if (instance != null)
{
var props = instance.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
// Ignore indexers
.Where(prop => prop.GetIndexParameters().Length == 0)
// Must be both readable and writable
.Where(prop => prop.CanWrite && prop.CanRead);
foreach (PropertyInfo prop in props)
{
if (instance is IEnumerable)
{
foreach (var item in (IEnumerable)instance)
{
TrimWhitespace(item);
}
}
else if (prop.GetValue(instance, null) is string)
{
string value = (string)prop.GetValue(instance, null);
if (value != null)
{
value = value.Trim();
prop.SetValue(instance, value, null);
}
}
else
TrimWhitespace(prop.GetValue(instance, null));
}
}
}
Мысли?