Изменение нескольких строковых полей
У меня есть следующий код:
class SearchCriteria
{
public string Name { get; set; }
public string Email { get; set; }
public string Company { get; set; }
// ... around 20 fields follow
public void Trim()
{
if( ! String.IsNullOrEmpty( Name ) )
{
Name = Name.Trim();
}
if( ! String.IsNullOrEmpty( Email ) )
{
Email = Email.Trim();
}
// ... repeat for all 20 fields in the class.
}
}
Я хочу написать одну функцию, которая будет правильно обрезать поля, например:
public void Trim()
{
Trim( Name );
Trim( Email );
// ...
}
private static void Trim( ref string field )
{
if( ! String.IsNullOrEmpty( field ) )
{
field = field.Trim();
}
}
Конечно, это запрещено в С#.
Один из вариантов - написать помощника и использовать отражение.
Есть ли другой способ, которым я могу это достичь (отражая то, что многие свойства будут иметь успех в этом конкретном сценарии, и я не могу этого себе позволить?)
Ответы
Ответ 1
Если у вас уже есть код, что вы спрашиваете? Это читаемо и эффективно. Но, возможно, было бы лучше, если бы свойства уже обрезали переданное значение в первую очередь.
class SearchCriteria
{
private string _Name;
public string Name
{
get { return _Name; }
set { _Name = value == null ? null : value.Trim(); }
}
private string _Email;
public string Email
{
get { return _Email; }
set { _Email = value == null ? null : value.Trim(); }
}
private string _Company;
public string Company
{
get { return _Company; }
set { _Company = value == null ? null : value.Trim(); }
}
// ... around 20 fields follow
}
Даже если вы можете использовать подход с отражением. Считайте, что этот код всегда трудно понять и поддерживать. И он будет без проблем отделки, даже если они не должны быть обрезаны. Например, если другой разработчик расширяет этот класс.
Ответ 2
public void Trim()
{
Name = Trim( Name );
Email = Trim( Email );
// ...
}
private string Trim(string field )
{
if( ! String.IsNullOrEmpty( field ) )
field = field.Trim();
return field;
}
EDIT:
попробуйте также применить Trim
fuction в сеттерах свойств
class SearchCriteria
{
private string Trim(string field)
{
if( ! String.IsNullOrEmpty( field ) )
field = field.Trim();
return field;
}
private string _name;
public string Name
{
get { return _name; }
set { _name = Trim(value); }
}
private string _email;
public string Email
{
get { return _email; }
set { _email = Trim(value); }
}
// ... other string properties
// no public void Trim() method
}
Ответ 3
Кажется, переборщик... сохранил время в Trim()
, потратил впустую время в объявлении поля
class SearchCriteria
{
private Dictionary<string, string> _internalValues = new Dictionary<string, string>();
public string Name { get { return _internalValues.ContainsKey("Name") ? _internalValues["Name"] : null; } set { _internalValues["Name"] = value; } }
....
public void Trim()
{
foreach (var entry in _internalValues)
{
if (!string.IsNullOrEmpty(entry.Value)) _internalValues[entry.Key] = entry.Value.Trim();
}
}
}
Ответ 4
Я предпочитаю этот стиль, дублирование неизбежно для поддержания читаемости, но это сэкономит некоторое пространство экрана
class SearchCriteria
{
public string Name { get; set; }
public string Email { get; set; }
public string Company { get; set; }
public void Trim()
{
if(!String.IsNullOrEmpty(Name)) Name = Name.Trim();
if(!String.IsNullOrEmpty(Email)) Email = Email.Trim();
if(!String.IsNullOrEmpty(Company)) Company = Company.Trim();
}
}
void Main()
{
var criteria = new SearchCriteria();
criteria.Email = "thing ";
Console.WriteLine(criteria.Email.Length);
criteria.Trim();
Console.WriteLine(criteria.Email);
Console.WriteLine(criteria.Email.Length);
}
Ответ 5
Если вы не использовали свойства auto, вы можете просто использовать ссылку ref. Я согласен, что это отнюдь не оптимально.
class SearchCriteria
{
private string _name;
public string Name { get { return _name; } set { _name = value; }}
public string Email { get; set; }
public string Company { get; set; }
// ... around 20 fields follow
void Trim(ref string str)
{
if (!String.IsNullOrEmpty(str))
{
str = str.Trim();
}
}
public void Trim()
{
Trim(ref _name);
// ... repeat for all 20 fields in the class.
}
}
Ответ 6
Вы можете изменить свой код следующим образом:
public void Trim()
{
Name = Trim(Name);
Email = Trim(Email);
// ...
}
private static void Trim(string field)
{
if( ! String.IsNullOrWhiteSpace( field ) )
{
field = field.Trim();
}
return field;
}
Вы не можете передать свойство по ссылке, а метод String.IsNullOrEmpty()
будет считать пустое пространство непустым, поэтому я использовал String.IsNullOrWhiteSpace()
.
Ответ 7
Просто закончите. Я не знаю, хорошо ли это. Но вы можете использовать, как сказал Тим Шмельтер. Но поскольку он также указал, что код сложнее поддерживать, и если кто-то расширит его, это могут быть проблемы. Но вот пример того, как вы могли это сделать:
class SearchCriteria
{
public string Name { get; set; }
public string Email { get; set; }
public string Company { get; set; }
// ... around 20 fields follow
public void Trim()
{
typeof(SearchCriteria).GetProperties()
.Where (w =>w.PropertyType==typeof(string))
.ToList().ForEach(f=>
{
var value=f.GetValue(this);
if(value!=null && !string.IsNullOrEmpty(value.ToString()))
{
f.SetValue(this,value.ToString().Trim(),null);
}
});
}
}
Ответ 8
Отражение, очевидно, будет не самым эффективным решением, но с некоторыми изменениями и кэшированием его все еще можно использовать.
Вот помощник отражения, который позволит вам создавать коллекцию делегатов мутаторов и кэшировать его внутри вашего класса:
public static class ReflectionHelper
{
public static IEnumerable<PropertyInfo> GetPropertiesOfType<THolder, TPropType>()
{
return typeof(THolder).GetPropertiesOfType(typeof(TPropType));
}
public static IEnumerable<PropertyInfo> GetPropertiesOfType(this Type holderType, Type propType)
{
if (holderType == null)
throw new ArgumentNullException("holderType");
if (propType == null)
throw new ArgumentNullException("propType");
return holderType
.GetProperties()
.Where(prop =>
prop.PropertyType == propType);
}
public static IEnumerable<Action<Func<TPropType, TPropType>>> CreateMutators<THolder, TPropType>(THolder holder)
{
if (holder == null)
throw new ArgumentNullException("holder");
return holder.GetType()
.GetPropertiesOfType(typeof(TPropType))
.Select(prop =>
new
{
getDelegate = (Func<TPropType>)Func.CreateDelegate(
typeof(Func<TPropType>),
holder,
prop.GetGetMethod()),
setDelegate = (Action<TPropType>)Action.CreateDelegate(
typeof(Action<TPropType>),
holder,
prop.GetSetMethod())
})
.Select(accessor =>
(Action<Func<TPropType, TPropType>>)((mutate) =>
{
var original = accessor.getDelegate();
var mutated = mutate(original);
accessor.setDelegate(mutated);
}))
.ToArray();
}
}
Код класса - вы кешируете мутаторы и используете их внутри метода Trim:
class SearchCriteria
{
public SearchCriteria()
{
this.Name = "adsfasd ";
this.Email = " adsfasd ";
this.Company = " asdf adsfasd ";
this.stringMutators = ReflectionHelper.CreateMutators<SearchCriteria, String>(this);
}
public string Name { get; set; }
public string Email { get; set; }
public string Company { get; set; }
// ... around 20 fields follow
private IEnumerable<Action<Func<String, String>>> stringMutators;
private String TrimMutate(String value)
{
if (String.IsNullOrEmpty(value))
return value;
return value.Trim();
}
public void Trim()
{
foreach (var mutator in this.stringMutators)
{
mutator(this.TrimMutate);
}
}
public override string ToString()
{
return String.Format("Name = |{0}|, Email = |{1}|, Company = |{2}|",
this.Name,
this.Email,
this.Company);
}
}
Основной код:
var criteria = new SearchCriteria();
Console.WriteLine("Before trim:");
Console.WriteLine(criteria);
Console.WriteLine("After trim:");
criteria.Trim();
Console.WriteLine(criteria);
P.S.: Однако это не очень прямое или ясное решение, поэтому я рекомендую пойти с "умным" сеттером (getters), как описано в других ответах. Или, возможно, вы можете попробовать Аспектно ориентированный подход к программированию.