【问题标题】:Modify multiple string fields修改多个字符串字段
【发布时间】:2015-04-24 14:50:04
【问题描述】:

我有以下代码:

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

当然,这在 C# 中是不允许的。 我有一个选择是编写一个助手并使用反射。 有没有其他方法可以实现这一点(考虑到这么多属性肯定会对特定场景产生性能影响,而我负担不起)?

【问题讨论】:

  • 也许你可以在它们各自的属性设置器中修剪字符串?
  • 我可能会重新设计整个类以使用 HashMap 来保存 20 个不同的字符串值,因为它们似乎是可选属性列表。然后你有一个 PUT 方法,它可以进行修剪。
  • @Falco:你怎么知道所有都是字符串?可能有一个DateTime DayOfBirth。使用List<String> 太抽象了,因为你不能说:再给我SearchCriteria.Email。如果你使用Dictionary<string, string>,你必须知道密钥。
  • @TimSchmelter 您可以为字典的键名创建静态常量。或者一个 Enum 并有一个 dictionary 这将清理类很多。 -> 而且我确定只有字符串字段,因为他想在所有字段上调用 ​​trim()。你不能修剪日期;-)
  • 我想说的是,拥有 20 个属性的情况并不少见,通常最好为它们使用 List<string>。您节省了几行代码,但代码变得更糟,因为所有代码都必须是字符串,以后无法更改(例如,将 string Company 更改为 Company Company)。您必须以某种方式循环它们或知道键,对于其他开发人员来说,该类不再可读或可维护。基本上你是在用字符串替换 OOP 概念。

标签: c#


【解决方案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;
    }
    

    编辑:

    也尝试在属性设置器中应用Trim 函数

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

      【讨论】:

      • 如果没有遗留原因导致类的公共接口必须保持不变,我会废弃整个访问器并简单地执行 SearchCriteria.get(SCEnum.Email);
      【解决方案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】:

        如果您不使用自动属性,则可以只使用 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】:

          刚刚完成。我不知道这是不是一个好方法。但是你可以像 Tim Schmelter 所说的那样使用反射。但正如他还指出的那样,代码更难维护,如果有人扩展它可能会出现问题。但这里有一个例子说明你如何做到这一点:

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

          【讨论】:

            【解决方案7】:

            你可以修改你的代码如下

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

            【讨论】:

              【解决方案8】:

              反射显然不是性能最高的解决方案,但经过一些修改和缓存,它仍然可以使用。

              这里是反射助手,它允许您创建 mutator 委托的集合并将其缓存在您的类中:

              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),因为它在其他答案中有所描述。或者,也许,你可以试试Aspect Oriented Programming approach

              【讨论】:

                猜你喜欢
                • 2013-03-29
                • 2018-04-25
                • 2017-10-16
                • 1970-01-01
                • 2017-09-26
                • 2014-05-30
                • 2018-04-10
                • 2021-10-22
                • 1970-01-01
                相关资源
                最近更新 更多