Подтвердить что ты не робот

Изменение нескольких строковых полей

У меня есть следующий код:

  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();
    } 
}

Конечно, это запрещено в С#. Один из вариантов - написать помощника и использовать отражение. Есть ли другой способ, которым я могу это достичь (отражая то, что многие свойства будут иметь успех в этом конкретном сценарии, и я не могу этого себе позволить?)

4b9b3361

Ответ 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), как описано в других ответах. Или, возможно, вы можете попробовать Аспектно ориентированный подход к программированию.